Changeset - r28430:253b9eab2f15
[Not reviewed]
master
0 11 3
Patric Stout - 11 months ago 2024-01-10 21:38:58
truebrain@openttd.org
Codechange: refactor the Windows-only DllLoader in a cross-platform LibraryLoader (#11751)
14 files changed with 268 insertions and 66 deletions:
0 comments (0 inline, 0 general)
CMakeLists.txt
Show inline comments
 
@@ -308,12 +308,13 @@ link_package(PNG TARGET PNG::PNG ENCOURA
 
link_package(ZLIB TARGET ZLIB::ZLIB ENCOURAGED)
 
link_package(LIBLZMA TARGET LibLZMA::LibLZMA ENCOURAGED)
 
link_package(LZO)
 

	
 
if(NOT WIN32 AND NOT EMSCRIPTEN)
 
    link_package(CURL ENCOURAGED)
 
    target_link_libraries(openttd_lib ${CMAKE_DL_LIBS})
 
endif()
 

	
 
if(NOT EMSCRIPTEN)
 
    link_package(unofficial-breakpad TARGET unofficial::breakpad::libbreakpad_client ENCOURAGED)
 
endif()
 

	
src/CMakeLists.txt
Show inline comments
 
@@ -229,12 +229,13 @@ add_files(
 
    league_base.h
 
    league_cmd.h
 
    league_cmd.cpp
 
    league_gui.h
 
    league_gui.cpp
 
    league_type.h
 
    library_loader.h
 
    livery.h
 
    main_gui.cpp
 
    map.cpp
 
    map_func.h
 
    map_type.h
 
    misc.cpp
src/library_loader.h
Show inline comments
 
new file 100644
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD 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, version 2.
 
 * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file library_loader.h Functions/types related to loading libraries dynamically. */
 

	
 
#ifndef LIBRARY_LOADER_H
 
#define LIBRARY_LOADER_H
 

	
 
class LibraryLoader {
 
public:
 
	/**
 
	 * A function loaded from a library.
 
	 *
 
	 * Will automatically cast to the correct function pointer type on retrieval.
 
	 */
 
	class Function {
 
	public:
 
		explicit Function(void *p) : p(p) {}
 

	
 
		template <typename T, typename = std::enable_if_t<std::is_function_v<T>>>
 
		operator T *() const
 
		{
 
			return reinterpret_cast<T *>(this->p);
 
		}
 

	
 
	private:
 
		void *p;
 
	};
 

	
 
	/**
 
	 * Load a library with the given filename.
 
	 */
 
	explicit LibraryLoader(const std::string &filename)
 
	{
 
		this->handle = this->OpenLibrary(filename);
 
	}
 

	
 
	/**
 
	 * Close the library.
 
	 */
 
	~LibraryLoader()
 
	{
 
		if (this->handle != nullptr) {
 
			this->CloseLibrary();
 
		}
 
	}
 

	
 
	/**
 
	 * Check whether an error occurred while loading the library or a function.
 
	 *
 
	 * @return Whether an error occurred.
 
	 */
 
	bool HasError()
 
	{
 
		return this->error.has_value();
 
	}
 

	
 
	/**
 
	 * Get the last error that occurred while loading the library or a function.
 
	 *
 
	 * @return The error message.
 
	 */
 
	std::string GetLastError()
 
	{
 
		return this->error.value_or("No error");
 
	}
 

	
 
	/**
 
	 * Get a function from a loaded library.
 
	 *
 
	 * @param symbol_name The name of the function to get.
 
	 * @return The function. Check HasError() before using.
 
	 */
 
	Function GetFunction(const std::string &symbol_name)
 
	{
 
		if (this->error.has_value()) return Function(nullptr);
 
		return Function(this->GetSymbol(symbol_name));
 
	}
 

	
 
private:
 
	/**
 
	 * Open the library with the given filename.
 
	 *
 
	 * Should set error if any error occurred.
 
	 *
 
	 * @param filename The filename of the library to open.
 
	 */
 
	void *OpenLibrary(const std::string &filename);
 

	
 
	/**
 
	 * Close the library.
 
	 */
 
	void CloseLibrary();
 

	
 
	/**
 
	 * Get a symbol from the library.
 
	 *
 
	 * Should set error if any error occurred.
 
	 *
 
	 * @param symbol_name The name of the symbol to get.
 
	 */
 
	void *GetSymbol(const std::string &symbol_name);
 

	
 
	std::optional<std::string> error = {}; ///< The last error that occurred, if set.
 
	void *handle = nullptr; ///< Handle to the library.
 
};
 

	
 
#endif /* LIBRARY_LOADER_H */
src/os/unix/CMakeLists.txt
Show inline comments
 
@@ -2,12 +2,13 @@ add_files(
 
    crashlog_unix.cpp
 
    survey_unix.cpp
 
    CONDITION UNIX AND NOT APPLE
 
)
 

	
 
add_files(
 
    library_loader_unix.cpp
 
    unix.cpp
 
    CONDITION UNIX
 
)
 

	
 
add_files(
 
    font_unix.cpp
src/os/unix/library_loader_unix.cpp
Show inline comments
 
new file 100644
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD 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, version 2.
 
 * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file library_loader_unix.cpp Implementation of the LibraryLoader for Linux / MacOS */
 

	
 
#include "../../stdafx.h"
 

	
 
#include <dlfcn.h>
 

	
 
#include "../../library_loader.h"
 

	
 
#include "../../safeguards.h"
 

	
 
/* Emscripten cannot dynamically load other files. */
 
#if defined(__EMSCRIPTEN__)
 

	
 
void *LibraryLoader::OpenLibrary(const std::string &)
 
{
 
	this->error = "Dynamic loading is not supported on this platform.";
 
	return nullptr;
 
}
 

	
 
void LibraryLoader::CloseLibrary()
 
{
 
}
 

	
 
void *LibraryLoader::GetSymbol(const std::string &)
 
{
 
	this->error = "Dynamic loading is not supported on this platform.";
 
	return nullptr;
 
}
 

	
 
#else
 

	
 
void *LibraryLoader::OpenLibrary(const std::string &filename)
 
{
 
	void *h = dlopen(filename.c_str(), RTLD_NOW | RTLD_LOCAL);
 
	if (h == nullptr) {
 
		this->error = dlerror();
 
	}
 

	
 
	return h;
 
}
 

	
 
void LibraryLoader::CloseLibrary()
 
{
 
	dlclose(this->handle);
 
}
 

	
 
void *LibraryLoader::GetSymbol(const std::string &symbol_name)
 
{
 
	void *p = dlsym(this->handle, symbol_name.c_str());
 
	if (p == nullptr) {
 
		this->error = dlerror();
 
	}
 

	
 
	return p;
 
}
 

	
 
#endif /* __EMSCRIPTEN__ */
src/os/windows/CMakeLists.txt
Show inline comments
 
add_files(
 
    crashlog_win.cpp
 
    font_win32.cpp
 
    font_win32.h
 
    library_loader_win.cpp
 
    string_uniscribe.cpp
 
    string_uniscribe.h
 
    survey_win.cpp
 
    win32.cpp
 
    win32.h
 
    CONDITION WIN32
src/os/windows/crashlog_win.cpp
Show inline comments
 
@@ -14,12 +14,13 @@
 
#include "../../string_func.h"
 
#include "../../fileio_func.h"
 
#include "../../strings_func.h"
 
#include "../../gamelog.h"
 
#include "../../saveload/saveload.h"
 
#include "../../video/video_driver.hpp"
 
#include "../../library_loader.h"
 

	
 
#include <windows.h>
 
#include <mmsystem.h>
 
#include <signal.h>
 
#include <psapi.h>
 

	
 
@@ -174,39 +175,39 @@ public:
 
#if defined(_MSC_VER)
 
static const uint MAX_SYMBOL_LEN = 512;
 
static const uint MAX_FRAMES     = 64;
 

	
 
/* virtual */ void CrashLogWindows::SurveyStacktrace(nlohmann::json &survey) const
 
{
 
	DllLoader dbghelp(L"dbghelp.dll");
 
	LibraryLoader dbghelp("dbghelp.dll");
 
	struct ProcPtrs {
 
		BOOL (WINAPI * pSymInitialize)(HANDLE, PCSTR, BOOL);
 
		BOOL (WINAPI * pSymSetOptions)(DWORD);
 
		BOOL (WINAPI * pSymCleanup)(HANDLE);
 
		BOOL (WINAPI * pStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64);
 
		PVOID (WINAPI * pSymFunctionTableAccess64)(HANDLE, DWORD64);
 
		DWORD64 (WINAPI * pSymGetModuleBase64)(HANDLE, DWORD64);
 
		BOOL (WINAPI * pSymGetModuleInfo64)(HANDLE, DWORD64, PIMAGEHLP_MODULE64);
 
		BOOL (WINAPI * pSymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64);
 
		BOOL (WINAPI * pSymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
 
	} proc = {
 
		dbghelp.GetProcAddress("SymInitialize"),
 
		dbghelp.GetProcAddress("SymSetOptions"),
 
		dbghelp.GetProcAddress("SymCleanup"),
 
		dbghelp.GetProcAddress("StackWalk64"),
 
		dbghelp.GetProcAddress("SymFunctionTableAccess64"),
 
		dbghelp.GetProcAddress("SymGetModuleBase64"),
 
		dbghelp.GetProcAddress("SymGetModuleInfo64"),
 
		dbghelp.GetProcAddress("SymGetSymFromAddr64"),
 
		dbghelp.GetProcAddress("SymGetLineFromAddr64"),
 
		dbghelp.GetFunction("SymInitialize"),
 
		dbghelp.GetFunction("SymSetOptions"),
 
		dbghelp.GetFunction("SymCleanup"),
 
		dbghelp.GetFunction("StackWalk64"),
 
		dbghelp.GetFunction("SymFunctionTableAccess64"),
 
		dbghelp.GetFunction("SymGetModuleBase64"),
 
		dbghelp.GetFunction("SymGetModuleInfo64"),
 
		dbghelp.GetFunction("SymGetSymFromAddr64"),
 
		dbghelp.GetFunction("SymGetLineFromAddr64"),
 
	};
 

	
 
	survey = nlohmann::json::array();
 

	
 
	/* Try to load the functions from the DLL, if that fails because of a too old dbghelp.dll, just skip it. */
 
	if (dbghelp.Success()) {
 
	if (!dbghelp.HasError()) {
 
		/* Initialize symbol handler. */
 
		HANDLE hCur = GetCurrentProcess();
 
		proc.pSymInitialize(hCur, nullptr, TRUE);
 
		/* Load symbols only when needed, fail silently on errors, demangle symbol names. */
 
		proc.pSymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_UNDNAME);
 

	
src/os/windows/font_win32.cpp
Show inline comments
 
@@ -12,15 +12,16 @@
 
#include "../../blitter/factory.hpp"
 
#include "../../core/alloc_func.hpp"
 
#include "../../core/math_func.hpp"
 
#include "../../core/mem_func.hpp"
 
#include "../../error_func.h"
 
#include "../../fileio_func.h"
 
#include "../../fontdetection.h"
 
#include "../../fontcache.h"
 
#include "../../fontcache/truetypefontcache.h"
 
#include "../../fontdetection.h"
 
#include "../../library_loader.h"
 
#include "../../string_func.h"
 
#include "../../strings_func.h"
 
#include "../../zoom_func.h"
 
#include "font_win32.h"
 

	
 
#include "../../table/control_codes.h"
 
@@ -358,15 +359,15 @@ void LoadWin32Font(FontSize fs)
 
		}
 

	
 
		if (fontPath[0] != 0) {
 
			if (AddFontResourceEx(fontPath, FR_PRIVATE, 0) != 0) {
 
				/* Try a nice little undocumented function first for getting the internal font name.
 
				 * Some documentation is found at: http://www.undocprint.org/winspool/getfontresourceinfo */
 
				static DllLoader _gdi32(L"gdi32.dll");
 
				static LibraryLoader _gdi32("gdi32.dll");
 
				typedef BOOL(WINAPI *PFNGETFONTRESOURCEINFO)(LPCTSTR, LPDWORD, LPVOID, DWORD);
 
				static PFNGETFONTRESOURCEINFO GetFontResourceInfo = _gdi32.GetProcAddress("GetFontResourceInfoW");
 
				static PFNGETFONTRESOURCEINFO GetFontResourceInfo = _gdi32.GetFunction("GetFontResourceInfoW");
 

	
 
				if (GetFontResourceInfo != nullptr) {
 
					/* Try to query an array of LOGFONTs that describe the file. */
 
					DWORD len = 0;
 
					if (GetFontResourceInfo(fontPath, &len, nullptr, 2) && len >= sizeof(LOGFONT)) {
 
						LOGFONT *buf = (LOGFONT *)new byte[len];
src/os/windows/font_win32.h
Show inline comments
 
@@ -10,12 +10,14 @@
 
#ifndef FONT_WIN32_H
 
#define FONT_WIN32_H
 

	
 
#include "../../fontcache/truetypefontcache.h"
 
#include "win32.h"
 

	
 
#include <windows.h>
 

	
 
/** Font cache for fonts that are based on a Win32 font. */
 
class Win32FontCache : public TrueTypeFontCache {
 
private:
 
	LOGFONT logfont;      ///< Logical font information for selecting the font face.
 
	HFONT font = nullptr; ///< The font face associated with this font.
 
	HDC dc = nullptr;     ///< Cached GDI device context.
src/os/windows/library_loader_win.cpp
Show inline comments
 
new file 100644
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD 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, version 2.
 
 * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file library_loader_win.cpp Implementation of the LibraryLoader for Windows */
 

	
 
#include "../../stdafx.h"
 

	
 
#include <windows.h>
 

	
 
#include "../../library_loader.h"
 

	
 
#include "../../safeguards.h"
 

	
 
static std::string GetLoadError()
 
{
 
	auto error_code = GetLastError();
 

	
 
	char buffer[512];
 
	if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error_code,
 
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), nullptr) == 0) {
 
		return fmt::format("Unknown error {}", error_code);
 
	}
 

	
 
	return buffer;
 
}
 

	
 
void *LibraryLoader::OpenLibrary(const std::string &filename)
 
{
 
	void *h = ::LoadLibraryW(OTTD2FS(filename).c_str());
 
	if (h == nullptr) {
 
		this->error = GetLoadError();
 
	}
 

	
 
	return h;
 
}
 

	
 
void LibraryLoader::CloseLibrary()
 
{
 
	HMODULE handle = static_cast<HMODULE>(this->handle);
 

	
 
	::FreeLibrary(handle);
 
}
 

	
 
void *LibraryLoader::GetSymbol(const std::string &symbol_name)
 
{
 
	HMODULE handle = static_cast<HMODULE>(this->handle);
 

	
 
	void *p = reinterpret_cast<void *>(::GetProcAddress(handle, symbol_name.c_str()));
 
	if (p == nullptr) {
 
		this->error = GetLoadError();
 
	}
 

	
 
	return p;
 
}
src/os/windows/win32.cpp
Show inline comments
 
@@ -24,12 +24,13 @@
 
#include "../../fios.h"
 
#include "../../core/alloc_func.hpp"
 
#include "../../string_func.h"
 
#include <sys/stat.h>
 
#include "../../language.h"
 
#include "../../thread.h"
 
#include "../../library_loader.h"
 

	
 
#include "../../safeguards.h"
 

	
 
static bool _has_console;
 
static bool _cursor_disable = true;
 
static bool _cursor_visible = true;
 
@@ -570,14 +571,14 @@ int OTTDStringCompare(std::string_view s
 
#endif
 
#ifndef LINGUISTIC_IGNORECASE
 
#	define LINGUISTIC_IGNORECASE 0x00000010 // linguistically appropriate 'ignore case'
 
#endif
 

	
 
	if (first_time) {
 
		static DllLoader _kernel32(L"Kernel32.dll");
 
		_CompareStringEx = _kernel32.GetProcAddress("CompareStringEx");
 
		static LibraryLoader _kernel32("Kernel32.dll");
 
		_CompareStringEx = _kernel32.GetFunction("CompareStringEx");
 
		first_time = false;
 
	}
 

	
 
	if (_CompareStringEx != nullptr) {
 
		/* CompareStringEx takes UTF-16 strings, even in ANSI-builds. */
 
		int len_s1 = MultiByteToWideChar(CP_UTF8, 0, s1.data(), (int)s1.size(), nullptr, 0);
 
@@ -614,14 +615,14 @@ int Win32StringContains(const std::strin
 
{
 
	typedef int (WINAPI *PFNFINDNLSSTRINGEX)(LPCWSTR, DWORD, LPCWSTR, int, LPCWSTR, int, LPINT, LPNLSVERSIONINFO, LPVOID, LPARAM);
 
	static PFNFINDNLSSTRINGEX _FindNLSStringEx = nullptr;
 
	static bool first_time = true;
 

	
 
	if (first_time) {
 
		static DllLoader _kernel32(L"Kernel32.dll");
 
		_FindNLSStringEx = _kernel32.GetProcAddress("FindNLSStringEx");
 
		static LibraryLoader _kernel32("Kernel32.dll");
 
		_FindNLSStringEx = _kernel32.GetFunction("FindNLSStringEx");
 
		first_time = false;
 
	}
 

	
 
	if (_FindNLSStringEx != nullptr) {
 
		int len_str = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
 
		int len_value = MultiByteToWideChar(CP_UTF8, 0, value.data(), (int)value.size(), nullptr, 0);
src/os/windows/win32.h
Show inline comments
 
@@ -7,57 +7,14 @@
 

	
 
/** @file win32.h declarations of functions for MS windows systems */
 

	
 
#ifndef WIN32_H
 
#define WIN32_H
 

	
 
#include <windows.h>
 
bool MyShowCursor(bool show, bool toggle = false);
 

	
 
class DllLoader {
 
public:
 
	explicit DllLoader(LPCTSTR filename)
 
	{
 
		this->hmodule = ::LoadLibrary(filename);
 
		if (this->hmodule == nullptr) this->success = false;
 
	}
 

	
 

	
 
	~DllLoader()
 
	{
 
		::FreeLibrary(this->hmodule);
 
	}
 

	
 
	bool Success() { return this->success; }
 

	
 
	class ProcAddress {
 
	public:
 
		explicit ProcAddress(void *p) : p(p) {}
 

	
 
		template <typename T, typename = std::enable_if_t<std::is_function_v<T>>>
 
		operator T *() const
 
		{
 
			return reinterpret_cast<T *>(this->p);
 
		}
 

	
 
	private:
 
		void *p;
 
	};
 

	
 
	ProcAddress GetProcAddress(const char *proc_name)
 
	{
 
		void *p = reinterpret_cast<void *>(::GetProcAddress(this->hmodule, proc_name));
 
		if (p == nullptr) this->success = false;
 
		return ProcAddress(p);
 
	}
 

	
 
private:
 
	HMODULE hmodule = nullptr;
 
	bool success = true;
 
};
 

	
 
char *convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen);
 
wchar_t *convert_to_fs(const std::string_view name, wchar_t *utf16_buf, size_t buflen);
 

	
 
void Win32SetCurrentLocaleName(const char *iso_code);
 
int OTTDStringCompare(std::string_view s1, std::string_view s2);
 
int Win32StringContains(const std::string_view str, const std::string_view value, bool case_insensitive);
src/video/win32_v.cpp
Show inline comments
 
@@ -19,12 +19,13 @@
 
#include "../texteff.hpp"
 
#include "../thread.h"
 
#include "../progress.h"
 
#include "../window_gui.h"
 
#include "../window_func.h"
 
#include "../framerate_type.h"
 
#include "../library_loader.h"
 
#include "win32_v.h"
 
#include <windows.h>
 
#include <imm.h>
 
#include <versionhelpers.h>
 

	
 
#include "../safeguards.h"
 
@@ -973,17 +974,17 @@ float VideoDriver_Win32Base::GetDPIScale
 
	static PFNGETDPIFORSYSTEM _GetDpiForSystem = nullptr;
 
	static PFNGETDPIFORMONITOR _GetDpiForMonitor = nullptr;
 

	
 
	static bool init_done = false;
 
	if (!init_done) {
 
		init_done = true;
 
		static DllLoader _user32(L"user32.dll");
 
		static DllLoader _shcore(L"shcore.dll");
 
		_GetDpiForWindow = _user32.GetProcAddress("GetDpiForWindow");
 
		_GetDpiForSystem = _user32.GetProcAddress("GetDpiForSystem");
 
		_GetDpiForMonitor = _shcore.GetProcAddress("GetDpiForMonitor");
 
		static LibraryLoader _user32("user32.dll");
 
		static LibraryLoader _shcore("shcore.dll");
 
		_GetDpiForWindow = _user32.GetFunction("GetDpiForWindow");
 
		_GetDpiForSystem = _user32.GetFunction("GetDpiForSystem");
 
		_GetDpiForMonitor = _shcore.GetFunction("GetDpiForMonitor");
 
	}
 

	
 
	UINT cur_dpi = 0;
 

	
 
	if (cur_dpi == 0 && _GetDpiForWindow != nullptr && this->main_wnd != nullptr) {
 
		/* Per window DPI is supported since Windows 10 Ver 1607. */
src/video/win32_v.h
Show inline comments
 
@@ -10,12 +10,13 @@
 
#ifndef VIDEO_WIN32_H
 
#define VIDEO_WIN32_H
 

	
 
#include "video_driver.hpp"
 
#include <mutex>
 
#include <condition_variable>
 
#include <windows.h>
 

	
 
/** Base class for Windows video drivers. */
 
class VideoDriver_Win32Base : public VideoDriver {
 
public:
 
	VideoDriver_Win32Base() : main_wnd(nullptr), fullscreen(false), buffer_locked(false) {}
 

	
0 comments (0 inline, 0 general)