diff options
Diffstat (limited to 'engines/sword25/kernel')
50 files changed, 7410 insertions, 0 deletions
diff --git a/engines/sword25/kernel/bs_stdint.h b/engines/sword25/kernel/bs_stdint.h new file mode 100755 index 0000000000..5334c3051c --- /dev/null +++ b/engines/sword25/kernel/bs_stdint.h @@ -0,0 +1,17 @@ +#ifndef BS_STDINT_H +#define BS_STDINT_H + +#ifdef _MSC_VER + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef signed __int64 int64_t; +#else + #include <stdint.h> +#endif + +#endif diff --git a/engines/sword25/kernel/callbackregistry.cpp b/engines/sword25/kernel/callbackregistry.cpp new file mode 100755 index 0000000000..abdf2bd704 --- /dev/null +++ b/engines/sword25/kernel/callbackregistry.cpp @@ -0,0 +1,123 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +// Alle Callbackfunktionen die von Objekten gerufen werden, die persistiert werden können, müssen hier registriert werden. +// Beim Speichern wird statt des Pointers der Bezeichner gespeichert. Beim Laden wird der Bezeichner wieder in einen Pointer umgewandelt. +// Diese Klasse führt also so etwas ähnliches wie eine Importtabelle für Callback-Funktionen. +// +// Dieses Vorgehen hat mehrere Vorteile: +// 1. Die Speicherstände sind plattformunabhängig. Es werden keine Pointer auf Funktionen gespeichert, sondern nur Namen von Callbackfunktionen. +// Diese können beim Laden über diese Klasse in systemabhängige Pointer umgewandelt werden. +// 2. Speicherstände können auch nach einem Engineupdate weiterhin benutzt werden. Beim Erstellen einer neun Binary verschieben sich häufig die +// Funktionen. Eine Callbackfunktion könnte sich also nach einem Update an einer anderen Stelle befinden als davor. Wenn im Spielstand der +// Pointer gespeichert war, stürtzt das Programm beim Äufrufen dieser Callbackfunktion ab. Durch das Auflösungverfahren wird beim Laden der +// Callbackbezeichner in den neuen Funktionspointer umgewandelt und der Aufruf kann erfolgen. + +// ----------------------------------------------------------------------------- +// Logging +// ----------------------------------------------------------------------------- + +#define BS_LOG_PREFIX "CALLBACKREGISTRY" + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "callbackregistry.h" + +// ----------------------------------------------------------------------------- + +bool BS_CallbackRegistry::RegisterCallbackFunction(const std::string & Name, void * Ptr) +{ + if (Name == "") + { + BS_LOG_ERRORLN("The empty string is not allowed as a callback function name."); + return false; + } + + if (FindPtrByName(Name) != 0) + { + BS_LOG_ERRORLN("There is already a callback function with the name \"%s\".", Name.c_str()); + return false; + } + if (FindNameByPtr(Ptr) != "") + { + BS_LOG_ERRORLN("There is already a callback function with the pointer 0x%x.", Ptr); + return false; + } + + StoreCallbackFunction(Name, Ptr); + + return true; +} + +// ----------------------------------------------------------------------------- + +void * BS_CallbackRegistry::ResolveCallbackFunction(const std::string & Name) const +{ + void * Result = FindPtrByName(Name); + + if (!Result) + { + BS_LOG_ERRORLN("There is no callback function with the name \"%s\".", Name.c_str()); + } + + return Result; +} + +// ----------------------------------------------------------------------------- + +std::string BS_CallbackRegistry::ResolveCallbackPointer(void * Ptr) const +{ + const std::string & Result = FindNameByPtr(Ptr); + + if (Result == "") + { + BS_LOG_ERRORLN("There is no callback function with the pointer 0x%x.", Ptr); + } + + return Result; +} + +// ----------------------------------------------------------------------------- + +void * BS_CallbackRegistry::FindPtrByName(const std::string & Name) const +{ + // Eintrag in der Map finden und den Pointer zurückgeben. + NameToPtrMap::const_iterator It = m_NameToPtrMap.find(Name); + return It == m_NameToPtrMap.end() ? 0 : It->second; +} + +// ----------------------------------------------------------------------------- + +std::string BS_CallbackRegistry::FindNameByPtr(void * Ptr) const +{ + // Eintrag in der Map finden und den Namen zurückgeben. + PtrToNameMap::const_iterator It = m_PtrToNameMap.find(Ptr); + return It == m_PtrToNameMap.end() ? "" : It->second; +} + +// ----------------------------------------------------------------------------- + +void BS_CallbackRegistry::StoreCallbackFunction(const std::string & Name, void * Ptr) +{ + // Callback-Funktion in beide Maps eintragen. + m_NameToPtrMap[Name] = Ptr; + m_PtrToNameMap[Ptr] = Name; +} diff --git a/engines/sword25/kernel/callbackregistry.h b/engines/sword25/kernel/callbackregistry.h new file mode 100755 index 0000000000..68d2e85b80 --- /dev/null +++ b/engines/sword25/kernel/callbackregistry.h @@ -0,0 +1,62 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_CALLBACK_REGISTRY_H +#define BS_CALLBACK_REGISTRY_H + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "kernel/common.h" + +#include "kernel/memlog_off.h" +#include <map> +#include "kernel/memlog_on.h" + +// ----------------------------------------------------------------------------- +// Klassendeklaration +// ----------------------------------------------------------------------------- + +class BS_CallbackRegistry +{ +public: + static BS_CallbackRegistry & GetInstance() + { + static BS_CallbackRegistry Instance; + return Instance; + } + + bool RegisterCallbackFunction(const std::string & Name, void * Ptr); + void * ResolveCallbackFunction(const std::string & Name) const; + std::string ResolveCallbackPointer(void * Ptr) const; + +private: + typedef std::map<std::string, void *> NameToPtrMap; + NameToPtrMap m_NameToPtrMap; + typedef std::map<void *, std::string> PtrToNameMap; + PtrToNameMap m_PtrToNameMap; + + void * FindPtrByName(const std::string & Name) const; + std::string FindNameByPtr(void * Ptr) const; + void StoreCallbackFunction(const std::string & Name, void * Ptr); +}; + + +#endif diff --git a/engines/sword25/kernel/common.h b/engines/sword25/kernel/common.h new file mode 100755 index 0000000000..29e4a20e73 --- /dev/null +++ b/engines/sword25/kernel/common.h @@ -0,0 +1,55 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +/* + common.h + ----------- + Diese Datei enthält Funktionen und Makros, die im gesamten Projekt bekannt sein müssen. + Daher ist es äußerst wichtig, dass diese Headerdatei in jede andere Headerdatei des Projektes + eingefügt wird. + + Autor: Malte Thiesen +*/ + +#ifndef BS_COMMON_H +#define BS_COMMON_H + +// Globale Konstanten +#if _DEBUG && !DEBUG + #define DEBUG +#endif + +#define BS_ACTIVATE_LOGGING // Wenn definiert, wird Logging aktiviert + +// Engine Includes +#include "memleaks.h" +#include "log.h" + +#ifdef DEBUG +#define BS_ASSERT(EXP) \ + if (!(EXP)) \ + { \ + BS_Log::Log("!!ASSERTION FAILED!! - FILE: %s - LINE: %d.\n", __FILE__, __LINE__); \ + __asm { int 3 }; \ + } +#else +#define BS_ASSERT(EXP) do { (void)(EXP); } while(0) +#endif + +#endif diff --git a/engines/sword25/kernel/cpuinfo.cpp b/engines/sword25/kernel/cpuinfo.cpp new file mode 100755 index 0000000000..f65f7e6125 --- /dev/null +++ b/engines/sword25/kernel/cpuinfo.cpp @@ -0,0 +1,260 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#define BS_LOG_PREFIX "CPUINFO" + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include "cpuinfo.h" + +// ----------------------------------------------------------------------------- +// Konstanten +// ----------------------------------------------------------------------------- + +// Standard CPU-Features +static const unsigned int MMX_BITMASK = 1 << 23; +static const unsigned int SSE_BITMASK = 1 << 25; +static const unsigned int SSE2_BITMASK = 1 << 26; + +// Erweiterte CPU-Features +static const unsigned int _3DNOW_BITMASK = 1 << 30; +static const unsigned int _3DNOWEXT_BITMASK = 1 << 31; + +// ----------------------------------------------------------------------------- +// Konstruktion +// ----------------------------------------------------------------------------- + +BS_CPUInfo::BS_CPUInfo() : + _VendorID(V_UNKNOWN), + _VendorString("unknown"), + _CPUName("unknown"), + _MMXSupported(false), + _SSESupported(false), + _SSE2Supported(false), + _3DNowSupported(false), + _3DNowExtSupported(false) +{ + if (!_IsCPUIDSupported()) + { + BS_LOG_ERRORLN("CPUID instruction ist not supported. Could not gather processor information."); + return; + } + + if (!_ReadVendor()) + { + BS_LOG_WARNINGLN("Unrecognized CPU vendor."); + } + + if (!_ReadCPUName()) + { + BS_LOG_WARNINGLN("Could not determine CPU name."); + } + + if (!_ReadCPUFeatures()) + { + BS_LOG_WARNINGLN("Could not determine CPU-features."); + } +} + +// ----------------------------------------------------------------------------- + +bool BS_CPUInfo::_IsCPUIDSupported() const +{ + __try + { + __asm + { + mov eax, 0 + cpuid + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return false; + } + + return true; +} + +// ----------------------------------------------------------------------------- + +bool BS_CPUInfo::_ReadVendor() +{ + static struct + { + char* VendorString; + BS_CPUInfo::VENDORID ID; + } VENDOR_TABLE[] = + { + "GenuineIntel", V_INTEL, + "AuthenticAMD", V_AMD, + "CyrixInstead", V_CYRIX, + "CentaurHauls", V_CENTAUR, + "NexGenDriven", V_NEXGEN, + "GenuineTMx86", V_TRANSMETA, + "RiseRiseRise", V_RISE, + "UMC UMC UMC", V_UMC, + "SiS SiS SiS", V_SIS, + "Geode by NSC", V_NSC, + 0, V_UNKNOWN, + }; + + // Vendor-String bestimmen + char Buffer[13]; + __asm + { + xor eax, eax + cpuid + mov dword ptr [Buffer], ebx + mov dword ptr [Buffer + 4], edx + mov dword ptr [Buffer + 8], ecx + mov byte ptr [Buffer + 12], 0 + } + _VendorString = Buffer; + + // Vendor-ID bestimmen + int i; + for (i = 0; VENDOR_TABLE[i].VendorString; i++) if (_VendorString == VENDOR_TABLE[i].VendorString) break; + _VendorID = VENDOR_TABLE[i].ID; + + return _VendorID != V_UNKNOWN; +} + +// ----------------------------------------------------------------------------- + +bool BS_CPUInfo::_ReadCPUName() +{ + // Feststellen, ob das CPU-Name Feature vorhanden ist. + unsigned int Result; + __asm + { + mov eax, 0x80000000 + cpuid + mov Result, eax + } + if (Result < 0x80000004) return false; + + // CPU-Namen einlesen + char Buffer[49]; + __asm + { + mov eax,0x80000002 + cpuid + mov dword ptr [Buffer + 0], eax + mov dword ptr [Buffer + 4], ebx + mov dword ptr [Buffer + 8], ecx + mov dword ptr [Buffer + 12], edx + mov eax,0x80000003 + cpuid + mov dword ptr [Buffer + 16], eax + mov dword ptr [Buffer + 20], ebx + mov dword ptr [Buffer + 24], ecx + mov dword ptr [Buffer + 28], edx + mov eax,0x80000004 + cpuid + mov dword ptr [Buffer + 32], eax + mov dword ptr [Buffer + 36], ebx + mov dword ptr [Buffer + 40], ecx + mov dword ptr [Buffer + 44], edx + mov byte ptr [Buffer + 48], 0 + } + std::string TempCPUName = Buffer; + if (TempCPUName.size() != 0) + { + // Führende und nachfolgende Leerzeichen entfernen + std::string::const_iterator StringBegin = TempCPUName.begin(); + for (; StringBegin != TempCPUName.end() && *StringBegin == ' '; StringBegin++); + std::string::const_iterator StringEnd = TempCPUName.end() - 1; + for(; StringEnd >= TempCPUName.begin() && *StringEnd == ' '; StringEnd--); + + if (StringBegin != TempCPUName.end() && StringEnd >= TempCPUName.begin()) + { + _CPUName = std::string(StringBegin, StringEnd + 1); + return true; + } + } + + return false; +} + +// ----------------------------------------------------------------------------- + +bool BS_CPUInfo::_ReadCPUFeatures() +{ + { + // Feststellen ob die Standard-Features abgefragt werden können + unsigned int Result; + __asm + { + xor eax, eax + cpuid + mov Result, eax + } + + // Nicht einmal die Standard-Features können abgefragt werden, also muss abgebrochen werden + if (Result < 1) return false; + + // Standard-Features abfragen + unsigned int Features; + __asm + { + mov eax, 1 + cpuid + mov Features, edx + } + + _MMXSupported = (Features & MMX_BITMASK) != 0; + _SSESupported = (Features & SSE_BITMASK) != 0; + _SSE2Supported = (Features & SSE2_BITMASK) != 0; + } + + + // Feststellen ob erweiterte CPU-Features abgefragt werden können + { + unsigned int Result; + __asm + { + mov eax, 0x80000000 + cpuid + mov Result, eax + } + + // Die erweiterten Features können nicht abgefragt werden, aber die Standard-Features wurden schon + // abgefragt, daher wird true zurückgegeben. + if (Result < 0x80000001) return true; + + // Erweiterte Features abfragen + unsigned int Features; + __asm + { + mov eax, 0x80000001 + cpuid + mov Features, edx + } + + _3DNowSupported = (Features & _3DNOW_BITMASK) != 0; + _3DNowExtSupported = (Features & _3DNOWEXT_BITMASK) != 0; + } + + return true; +} diff --git a/engines/sword25/kernel/cpuinfo.h b/engines/sword25/kernel/cpuinfo.h new file mode 100755 index 0000000000..fab763861a --- /dev/null +++ b/engines/sword25/kernel/cpuinfo.h @@ -0,0 +1,128 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_CPUINFO_H +#define BS_CPUINFO_H + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "common.h" + +// ----------------------------------------------------------------------------- +// Klassendefinition +// ----------------------------------------------------------------------------- + +/** + @brief Diese Singleton-Klasse stellt Informationen über die CPU zur verfügung. +*/ + +class BS_CPUInfo +{ +public: + /** + @brief Definiert die Vendor-IDs + */ + enum VENDORID + { + V_UNKNOWN, + V_INTEL, + V_AMD, + V_CYRIX, + V_CENTAUR, + V_NEXGEN, + V_TRANSMETA, + V_RISE, + V_UMC, + V_SIS, + V_NSC, + }; + + /** + @brief Gibt eine Referenz auf die einzige Instanz dieser Klasse zurück. + */ + static const BS_CPUInfo & GetInstance() + { + static BS_CPUInfo Instance; + return Instance; + } + + /** + @brief Gibt die Vendor-ID des CPU-Herstellers zurück. + @remark Gibt BS_CPUInfo::V_UNKNOWN zurück, wenn die Vendor-ID nicht bestimmt werden konnte. + */ + VENDORID GetVendorID() const { return _VendorID; } + + /** + @brief Gibt den Vendor-String zurück. + @remark Gibt "unknown" zurück, wenn der Vendor-String nicht bestimmt werden konnte. + */ + const std::string & GetVendorString() const { return _VendorString; } + + /** + @brief Gibt den CPU-Namen zurück. + @remark Gibt "unknown" zurück, wenn der CPU-Name nicht bestimmt werden konnte. + */ + const std::string & GetCPUName() const { return _CPUName; } + + /** + @brief Gibt zurück, ob der Prozessor MMX untersützt. + */ + bool IsMMXSupported() const { return _MMXSupported; } + + /** + @brief Gibt zurück, ob der Prozessor SSE unterstützt. + */ + bool IsSSESupported() const { return _SSESupported; } + + /** + @brief Gibt zurück, ob der Prozessor SSE2 unterstützt. + */ + bool IsSSE2Supported() const { return _SSE2Supported; } + + /** + @brief Gibt zurück, ob der Prozessor 3DNow! unterstützt. + */ + bool Is3DNowSupported() const { return _3DNowSupported; } + + /** + @brief Gibt zurück, ob der Prozessor 3DNow!-Ext. unterstützt. + */ + bool Is3DNowExtSupported() const { return _3DNowExtSupported; } + +private: + BS_CPUInfo(); + + VENDORID _VendorID; + std::string _VendorString; + std::string _CPUName; + bool _MMXSupported; + bool _SSESupported; + bool _SSE2Supported; + bool _3DNowSupported; + bool _3DNowExtSupported; + + bool _ReadVendor(); + bool _ReadCPUFeatures(); + bool _ReadCPUName(); + bool _IsCPUIDSupported() const; +}; + +#endif diff --git a/engines/sword25/kernel/debug/debugtools.cpp b/engines/sword25/kernel/debug/debugtools.cpp new file mode 100755 index 0000000000..91abe66ba7 --- /dev/null +++ b/engines/sword25/kernel/debug/debugtools.cpp @@ -0,0 +1,156 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +#include <vector> +#include <string> +#include <sstream> +#include <fstream> +using namespace std; + +#include "kernel/md5.h" +#include "kernel/filesystemutil.h" +#include "debugtools.h" + +// ----------------------------------------------------------------------------- +// Konstanten und Hilfsfunktionen +// ----------------------------------------------------------------------------- + +namespace +{ + const char * VERSION_ID_ERROR = "???"; + const unsigned int READBUFFER_SIZE = 1024 * 100; + + const char * SUBVERSION_ENTRIES_FILENAME = ".svn\\entries"; + + // ------------------------------------------------------------------------- + + unsigned int ParseUnsignedInt(const string & Str, bool & Success) + { + istringstream iss(Str); + + unsigned int Result = 0; + iss >> Result; + + Success = !iss.fail(); + + return Result; + } +} + +// ----------------------------------------------------------------------------- + +const char * BS_Debugtools::GetVersionID() +{ + // Falls die Versions-ID noch nicht bekannt ist, muss sie bestimmt werden + static string VersionIDString; + if (VersionIDString.size() == 0) + { + // Dateinamen der EXE-Datei bestimmen + char FileName[MAX_PATH + 1]; + if (GetModuleFileName(0, FileName, sizeof(FileName)) == 0) return VERSION_ID_ERROR; + + // Datei öffnen + HANDLE FileHandle = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if (INVALID_HANDLE_VALUE == FileHandle) return VERSION_ID_ERROR; + + // Datei stückchenweise Einlesen und MD5-Hash bilden + BS_MD5 md5; + std::vector<unsigned char> ReadBuffer(READBUFFER_SIZE); + DWORD BytesRead = 0; + do + { + // MD5-Hash für das eingelesene Dateistück berechnen. + md5.Update(&ReadBuffer[0], BytesRead); + + if (ReadFile(FileHandle, &ReadBuffer[0], READBUFFER_SIZE, &BytesRead, 0) == FALSE) + { + CloseHandle(FileHandle); + return VERSION_ID_ERROR; + } + } while (BytesRead > 0); + + // Datei schließen + CloseHandle(FileHandle); + + // Falls sich das aktuelle Verzeichnis in einem Subversion-Repository befindet, wird auch die Subversion-Revision mit in die ID gehasht. + // Dieses stellt im Beta-Test sicher, dass jede Änderung einer Datei, und nicht nur der EXE, zu einer neuen Versions-ID führt. + unsigned int SubversionRevision = GetSubversionRevision(); + if (SubversionRevision != 0) md5.Update(reinterpret_cast<unsigned char *>(&SubversionRevision), sizeof(unsigned int)); + + // MD5 abschließen + unsigned char Digest[16]; + md5.GetDigest(Digest); + + // VersionsID-String erstellen + std::ostringstream VersionIDBuf; + VersionIDBuf << std::hex; + for (unsigned int i = 0; i < sizeof(Digest); i++) + { + VersionIDBuf << (unsigned int) Digest[i]; + } + VersionIDString = VersionIDBuf.str(); + } + + return VersionIDString.c_str(); +} + +// ----------------------------------------------------------------------------- + +unsigned int BS_Debugtools::GetSubversionRevision() +{ + // Existiert eine entries Datei? + if (BS_FileSystemUtil::GetInstance().FileExists(SUBVERSION_ENTRIES_FILENAME)) + { + bool Success; + char Buffer[512]; + + // entries Datei öffnen. + ifstream File(SUBVERSION_ENTRIES_FILENAME); + if (File.fail()) return 0; + + // Das Format auslesen und feststellen, ob wir es unterstützen. + File.getline(Buffer, sizeof(Buffer), 0x0A); + unsigned int FormatVersion = ParseUnsignedInt(Buffer, Success); + if (File.fail() || !Success || FormatVersion < 7) return 0; + + // Den Namen des ersten Eintrages auslesen. Dieses muss ein leerer String sein und somit das aktuelle Verzeichnis benennen. + File.getline(Buffer, sizeof(Buffer), 0x0A); + if (File.fail() || strlen(Buffer) != 0) return 0; + + // Den Typ des Eintrages auslesen. Dieser muss "dir" sein. + File.getline(Buffer, sizeof(Buffer), 0x0A); + if (File.fail() || strcmp(Buffer, "dir") != 0) return 0; + + // Die Revision des Eintrages auslesen. + File.getline(Buffer, sizeof(Buffer), 0x0A); + unsigned int Revision = ParseUnsignedInt(Buffer, Success); + if (File.fail() || !Success) return 0; + + return Revision; + } + + return 0; +} diff --git a/engines/sword25/kernel/debug/debugtools.h b/engines/sword25/kernel/debug/debugtools.h new file mode 100755 index 0000000000..30dd04a83d --- /dev/null +++ b/engines/sword25/kernel/debug/debugtools.h @@ -0,0 +1,52 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef DEBUGTOOLS_H +#define DEBUGTOOLS_H + +class BS_Debugtools +{ +public: + /** + @brief Gibt eine ID zurück, die die benutzte Programmversion eindeutig identifiziert. + + Um die Version zu ermitteln wird der MD5-Hash über die EXE-Datei gebildet. + Falls die ausführende Datei in einem SVN-Repository liegt wird zusätzlich die Revision des Verzeichnisses gehasht. + + @return Gibt einen String zurück, der die Versions-ID des Programmes angibt.<br> + Falls die Versions-ID nicht bestimmt werden konnte wird "???" zurückgegeben. + @remark Diese Methode ist momentan nur für WIN32 implementiert. + */ + static const char * GetVersionID(); + + /** + + @brief Gibt die Subversion-Revisionsnummer der Engine zurück. + + Diese Funktion versucht die aktuelle Revision aus der SVN entries Datei für das aktuelle Verzeichnis zu extrahieren. + Dabei werden die SVN entries Formatversionen 7 und größer unterstützt. + Die neueste Version ist aktuell 9. Für folgende Versionen wird angenommen, dass sich das Format des Headers nicht mehr ändert. + + @return Gibt die Revisionsnummer zurück. Falls die ausführende Datei nicht in einem SVN-Repository liegt oder die Revision nicht + festgestellt werden konnte wird 0 zurückgegeben. + */ + static unsigned int GetSubversionRevision(); +}; + +#endif diff --git a/engines/sword25/kernel/debug/memorydumper.cpp b/engines/sword25/kernel/debug/memorydumper.cpp new file mode 100755 index 0000000000..3c79f67bf8 --- /dev/null +++ b/engines/sword25/kernel/debug/memorydumper.cpp @@ -0,0 +1,184 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include <string> +#include <ctime> +#include <sstream> +#include <iomanip> +#include "..\filesystemutil.h" +#include "memorydumper.h" +#include "debugtools.h" + +using namespace std; + +#define BS_LOG_PREFIX "MEMORYDUMPER" + +// ----------------------------------------------------------------------------- +// Konstanten +// ----------------------------------------------------------------------------- + +namespace +{ + const char * DBG_HELP_DLL_FILENAME = "dbghelp.dll"; + const char * MINIDUMPWRITEDUMP_FUNCTIONNAME = "MiniDumpWriteDump"; + const char * DUMPS_DIRECTORYNAME = "dumps"; + const char * DUMPFILE_EXTENSION = ".dmp"; +} + +// ----------------------------------------------------------------------------- +// Construction / Destruction +// ----------------------------------------------------------------------------- + +namespace +{ + HMODULE LoadDbghelpDLL() + { + // Zunächst wird versucht die DLL in dem Verzeichnis der EXE-Datei zu finden, da diese die gewünschte Funktionalität unterstützt + + // Pfad der EXE-Datei bestimmen + char ExePath[MAX_PATH]; + if (GetModuleFileNameA(0, ExePath, sizeof(ExePath))) + { + // EXE-Dateinamen abschneiden und den DLL-Dateinamen anhängen + string DllPath = ExePath; + string::size_type SlashPos; + if ((SlashPos = DllPath.rfind("\\")) != string::npos) + { + DllPath.resize(SlashPos + 1); + DllPath += DBG_HELP_DLL_FILENAME; + + HMODULE DllModule = LoadLibraryA(DllPath.c_str()); + if (DllModule) return DllModule; + } + } + + // Falls dies fehlgeschlagen ist, wird versucht die Version zu laden, die Windows uns anbietet, wenn wir keinen kompletten Pfad angeben. + return LoadLibraryA(DBG_HELP_DLL_FILENAME); + } +} + +BS_MemoryDumper::BS_MemoryDumper() : + m_DbghelpDLL(0) +{ + m_DbghelpDLL = LoadDbghelpDLL(); + if (m_DbghelpDLL) + { + m_MiniDumpWriteDump = reinterpret_cast<MINIDUMPWRITEDUMP>(GetProcAddress(m_DbghelpDLL, MINIDUMPWRITEDUMP_FUNCTIONNAME)); + if (!m_MiniDumpWriteDump) + { + BS_LOG_ERRORLN("Your version of \"%s\" is too old. Dumping is not possible.", DBG_HELP_DLL_FILENAME); + } + } + else + { + BS_LOG_ERRORLN("Could not load \"%s\". Dumping is not possible.", DBG_HELP_DLL_FILENAME); + } +} + +// ----------------------------------------------------------------------------- + +BS_MemoryDumper::~BS_MemoryDumper() +{ + // DLL-Entladen + if (m_DbghelpDLL) FreeLibrary(m_DbghelpDLL); +} + + +// ----------------------------------------------------------------------------- + +namespace +{ + bool EnsureOutputDirectoryExists() + { + // Windows.h definiert ein Makro CreateDirectory. Damit wir die Methode CreateDirectory aufrufen können müssen wir das Makro entfernen. + #ifdef CreateDirectory + #undef CreateDirectory + #endif + + return BS_FileSystemUtil::GetInstance().CreateDirectory( + BS_FileSystemUtil::GetInstance().GetUserdataDirectory() + + BS_FileSystemUtil::GetInstance().GetPathSeparator() + + DUMPS_DIRECTORYNAME); + } + + string CreateOutputFilename() + { + // Aktuelle Zeit bestimmen. + time_t Time = time(0); + tm * Timeinfo = localtime(&Time); + + // Den Ausgabedateinamen in folgender Form erstellen: + // <BenutzerdatenVerzeichnis>\dumps\YYYY-MM-DD HH-MM.dmp + ostringstream oss; + oss << BS_FileSystemUtil::GetInstance().GetUserdataDirectory() << BS_FileSystemUtil::GetInstance().GetPathSeparator() + << DUMPS_DIRECTORYNAME << BS_FileSystemUtil::GetInstance().GetPathSeparator() + << setfill('0') + << setw(4) << (Timeinfo->tm_year + 1900) << "-" + << setw(2) << (Timeinfo->tm_mon + 1) << "-" + << setw(2) << Timeinfo->tm_mday << " " + << setw(2) << Timeinfo->tm_hour << "-" + << setw(2) << Timeinfo->tm_min << "-" + << BS_Debugtools::GetVersionID() + << DUMPFILE_EXTENSION; + return oss.str(); + } +} + +bool BS_MemoryDumper::WriteDump(_EXCEPTION_POINTERS * ExceptionInfoPtr, string & Filename) +{ + // Dumpen ist nur möglich, wenn zuvor die dbghelp.dll geladen werden konnte. + if (!m_DbghelpDLL) + { + BS_LOG_ERRORLN("Cannot write dump because \"%s\" could not be loaded previously.", DBG_HELP_DLL_FILENAME); + return false; + } + + // Temporäre Datei erstellen, die den Dump aufnehmen soll + HANDLE File; + Filename = CreateOutputFilename(); + if (!EnsureOutputDirectoryExists() || + (File = CreateFile(Filename.c_str(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0)) != INVALID_HANDLE_VALUE) + { + _MINIDUMP_EXCEPTION_INFORMATION ExInfo; + + ExInfo.ThreadId = GetCurrentThreadId(); + ExInfo.ExceptionPointers = ExceptionInfoPtr; + ExInfo.ClientPointers = 0; + + // Dump schreiben + bool Result = m_MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), File, MiniDumpNormal, &ExInfo, 0, 0) != 0; + if (!Result) + { + BS_LOG_ERRORLN("MiniDumpWriteDump() failed. Memory dump could not be created."); + DeleteFileA(Filename.c_str()); + } + + CloseHandle(File); + return Result; + } + else + { + BS_LOG_ERRORLN("Could not create a file to accomodate the memory dump."); + return false; + } +} diff --git a/engines/sword25/kernel/debug/memorydumper.h b/engines/sword25/kernel/debug/memorydumper.h new file mode 100755 index 0000000000..5fac12e830 --- /dev/null +++ b/engines/sword25/kernel/debug/memorydumper.h @@ -0,0 +1,57 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_MEMORYDUMPER_H +#define BS_MEMORYDUMPER_H + + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <dbghelp.h> +#include <string> + +#include "kernel/common.h" + + +// ----------------------------------------------------------------------------- +// Class declaration +// ----------------------------------------------------------------------------- + +class BS_MemoryDumper +{ +public: + BS_MemoryDumper(); + ~BS_MemoryDumper(); + + bool WriteDump(_EXCEPTION_POINTERS * ExceptionInfoPtr, std::string & Filename); + +private: + typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType, + CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam); + MINIDUMPWRITEDUMP m_MiniDumpWriteDump; + HMODULE m_DbghelpDLL; +}; + +#endif diff --git a/engines/sword25/kernel/filesystemutil.h b/engines/sword25/kernel/filesystemutil.h new file mode 100755 index 0000000000..63f76617c0 --- /dev/null +++ b/engines/sword25/kernel/filesystemutil.h @@ -0,0 +1,102 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +/* + Die Klasse BS_FileSystemUtil stellt einen Wrapper für dateisystemspezifische Operationen dar, die nicht über die C/C++ Bibliotheken + abgedeckt werden. + + Jede unterstützte Plattform muss dieses Interface implementieren und die Singleton-Methode BS_FileSystemUtil::GetInstance() + implementieren. +*/ + +#ifndef BS_FILESYSTEMUTIL_H +#define BS_FILESYSTEMUTIL_H + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "kernel/common.h" +#include "kernel/bs_stdint.h" + +#include "kernel/memlog_off.h" +#include <string> +#include <vector> +#include <ctime> +#include "kernel/memlog_on.h" + +// ----------------------------------------------------------------------------- +// Klassendefinition +// ----------------------------------------------------------------------------- + +class BS_FileSystemUtil +{ +public: + static BS_FileSystemUtil & GetInstance(); + virtual ~BS_FileSystemUtil() {}; + + /* + Diese Funktion gibt den Namen des Verzeichnisses zurück, in dem sämtliche Benutzerdaten gespeichert werden sollen. + + Dieses sind z.B. Screenshots, Spielstände, Konfigurationsdateien, Logdateien, ... + Unter Windows Vista ist dieses beispielsweise: C:\Users\Malte\Documents\Broken Sword 2.5 + Falls dieses Verzeichnis noch nicht existiert, wird es automatisch erstellt. + + @return Gibt den Namen des Verzeichnisses für Benutzerdaten zurück. + */ + virtual std::string GetUserdataDirectory() = 0; + /* + @return Gibt den Pfadtrenner zurück (z.B. \ unter Windows und / unter Linux). + */ + virtual std::string GetPathSeparator() = 0; + /* + @param Filename der Pfad zu einer Datei. + @return Gibt die Größe der angegebenen Datei zurück. Falls die Größe nicht bestimmt werden konnte, oder die Datei nicht existiert wird -1 zurückgegeben. + */ + virtual uint64_t GetFileSize(const std::string & Filename) = 0; + /* + @param Filename der Pfad zu einer Datei. + @return Gibt den Zeitstempel des Zeitpunktes zurück an dem die Datei zuletzt verändert wurde. Bei einem Fehler wird 0 zurückgegeben. + */ + virtual time_t GetFileTime(const std::string & Filename) = 0; + /* + @param Filename der Pfad zu einer Datei. + @return Gibt true zurück, wenn die Datei existiert. + */ + virtual bool FileExists(const std::string & Filename) = 0; + /* + Diese Funktion erstellt einen Verzeichnis mit sämtlichen Unterverzeichnissen. + + Wenn des Parameter "\b\c\d\e" ist und der Pfad "\b\c" bereits existiert, werden die Verzeichnisse + "d" und "e" erstellt. + + @param DirectoryName der Name des zu erstellenden Verzeichnisses. + @return Gibt true zurück, wenn die Verzeichnisse erstellt werden konnten, ansonsten false. + */ + virtual bool CreateDirectory(const std::string & DirectoryName) = 0; + /* + Erstellt eine Liste aller Dateinamen in einem Verzeichnis. + + @param Directory das zu durchsuchende Verzeichnis. + @return Gibt einen vector mit allen gefundenen Dateinamen zurück. + */ + virtual std::vector<std::string> GetFilesInDirectory(const std::string & Path) = 0; +}; + +#endif diff --git a/engines/sword25/kernel/filesystemutil_win32.cpp b/engines/sword25/kernel/filesystemutil_win32.cpp new file mode 100755 index 0000000000..2b4ce49f3d --- /dev/null +++ b/engines/sword25/kernel/filesystemutil_win32.cpp @@ -0,0 +1,256 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "filesystemutil.h" +#define WIN32_LEAN_AND_MEAN +#include <Windows.h> +#include <ShlObj.h> +#include <memory.h> + +using namespace std; + +#define BS_LOG_PREFIX "FILESYSTEMUTILWIN32" + +// ----------------------------------------------------------------------------- +// Konstanten und Hilfsfunktionen +// ----------------------------------------------------------------------------- + +namespace +{ + const char * DIRECTORY_NAME = "Broken Sword 2.5"; + + // ------------------------------------------------------------------------- + + string GetAbsolutePath(const string & Path) + { + char Buffer[MAX_PATH]; + if (!::GetFullPathNameA(Path.c_str(), MAX_PATH, Buffer, 0)) + { + // Bei der Ausführung von GetFullPathNameA() ist ein Fehler aufgetreten. + // Wir können an dieser Stelle nichts andere machen, als einen leeren String zurückzugeben. + BS_LOG_ERRORLN("A call to GetFullPathNameA() failed."); + return ""; + } + + // Ergebnis zurückgeben. + return string(Buffer); + } +} + +// ----------------------------------------------------------------------------- +// Klassendefinition +// ----------------------------------------------------------------------------- + +class BS_FileSystemUtilWin32 : public BS_FileSystemUtil +{ +public: + virtual string GetUserdataDirectory() + { + // Die C++ Dateisystemfunktionen können leider nicht mit Unicode-Dateinamen umgehen. + // Für uns ist das problematisch, wenn wir in das %APPDATA%-Verzeichnis eines Benutzers zugreifen wollen, + // dessen Name Unicode-Zeichen enthält, die sich mit der aktuellen Codepage nicht als ANSI-String darstellen + // lassen. + // Wir behelfen uns damit, dass wir das Verzeichnis als Unicode-String abfragen, in die kurze + // Verzeichnisdarstellung konvertieren und das Ergebnis in einen ANSI-String konvertieren. + // Kurze Dateinamen sollten keine Unicode-Zeichen enthalten, ich habe allerdings keine offizielle Aussage dazu + // gefunden. + + WCHAR PathBuffer[MAX_PATH]; + + // Das %APPDATA%-Verzeichnis erfragen. + if (::SHGetSpecialFolderPathW(0, PathBuffer, CSIDL_APPDATA, FALSE) == FALSE) + { + BS_LOG_ERRORLN("SHGetSpecialFolderPathW() failed"); + return ""; + } + + // Die kurze Variante des Verzeichnisses erfragen. + if (::GetShortPathNameW(PathBuffer, PathBuffer, MAX_PATH) == 0) + { + BS_LOG_ERRORLN("GetShortPathNameW() failed"); + return ""; + } + + // Die Verzeichnisangabe in einen ANSI-String konvertieren. + char AnsiPathBuffer[MAX_PATH]; + BOOL UsedDefaultChar = FALSE; + if (::WideCharToMultiByte(CP_ACP, 0, PathBuffer, -1, AnsiPathBuffer, MAX_PATH, 0, &UsedDefaultChar) == 0) + { + BS_LOG_ERRORLN("WideCharToMultiByte() failed"); + return ""; + } + + // Falls bei der Konvertierung ein zum Einsatz kam, ist das Ergebnis nicht eindeutig und damit nicht + // verwendbar. + if (UsedDefaultChar) + { + BS_LOG_ERRORLN("Conversion from unicode to ANSI is ambiguous."); + return ""; + } + + // Verzeichnis zurückgeben. + return string(AnsiPathBuffer) + "\\" + DIRECTORY_NAME; + } + + virtual string GetPathSeparator() + { + return string("\\"); + } + + virtual uint64_t GetFileSize(const std::string & Filename) + { + WIN32_FILE_ATTRIBUTE_DATA fileAttributeData; + // Dateiattribute einlesen. + if (::GetFileAttributesExA(Filename.c_str(), GetFileExInfoStandard, &fileAttributeData) != 0) + { + // Die Dateigröße wird von Windows in zwei 32-Bit Zahlen angegeben. Diese werden an dieser Stelle in eine 64-Bit Zahl umgewandelt. + uint64_t fileSize = fileAttributeData.nFileSizeHigh; + fileSize <<= 32; + fileSize |= fileAttributeData.nFileSizeLow; + return fileSize; + } + else + { + return -1; + } + } + + virtual time_t GetFileTime(const std::string & Filename) + { + WIN32_FILE_ATTRIBUTE_DATA fileAttributeData; + if (::GetFileAttributesExA(Filename.c_str(), GetFileExInfoStandard, &fileAttributeData) != 0) + { + __int64 timestamp; + memcpy(×tamp, &fileAttributeData.ftLastWriteTime, sizeof(FILETIME)); + return (timestamp - 0x19DB1DED53E8000) / 10000000; + } + else + { + return 0; + } + } + + virtual bool FileExists(const std::string & Filename) + { + return ::GetFileAttributesA(Filename.c_str()) != INVALID_FILE_ATTRIBUTES; + } + + // Windows.h enthält ein Makro mit dem Namen CreateDirectory. Dieses muss entfernt werden bevor die Definition von + // unserem CreateDirectory() folgt. + #ifdef CreateDirectory + #undef CreateDirectory + #endif + + virtual bool CreateDirectory(const string & DirectoryName) + { + return CreateDirectoryRecursive(GetAbsolutePath(DirectoryName)); + } + + virtual vector<string> GetFilesInDirectory(const std::string & Directory) + { + vector<string> Result; + + // Suchstring erstellen, dabei muss der leere String (aktuelles Verzeichnis) gesondert behandelt werden. + string SearchPattern; + if (Directory.empty()) + SearchPattern = "*"; + else + SearchPattern = Directory + "\\*"; + + // Die erste Datei suchen. + WIN32_FIND_DATAA FindData; + HANDLE FindHandle = ::FindFirstFileA(SearchPattern.c_str(), &FindData); + + while (FindHandle != INVALID_HANDLE_VALUE) + { + // Verzeichnisse ignorieren. + // Beim erstellen des Ergebnispfades muss wieder der Sonderfall der leeren Verzeichnisangabe berücksichtigt werden. + if (!(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + Result.push_back(FindData.cFileName); + + // Solange weitermachen, bis keine Dateien mehr gefunden werden. + if (FindNextFileA(FindHandle, &FindData) == 0) + { + FindClose(&FindHandle); + break; + } + } + + return Result; + } + +private: + bool CreateDirectoryRecursive(string & DirectoryName) + { + // + // http://www.codeguru.com/Cpp/W-P/files/article.php/c4439/ + // (c) Assaf Tzur-El + // + + DWORD attr; + int pos; + bool result = true; + + // Check for trailing slash: + pos = DirectoryName.find_last_of("\\"); + if (DirectoryName.length() == pos + 1) // last character is "\" + { + DirectoryName.resize(pos); + } + + // Look for existing object: + attr = ::GetFileAttributesA(DirectoryName.c_str()); + if (0xFFFFFFFF == attr) // doesn't exist yet - create it! + { + pos = DirectoryName.find_last_of("\\"); + if (0 < pos) + { + // Create parent dirs: + result = CreateDirectoryRecursive(DirectoryName.substr(0, pos)); + } + // Create node: + result = result && ::CreateDirectoryA(DirectoryName.c_str(), NULL); + } + else if (!(FILE_ATTRIBUTE_DIRECTORY & attr)) + { // object already exists, but is not a dir + ::SetLastError(ERROR_FILE_EXISTS); + result = false; + } + + return result; + } +}; + +// ----------------------------------------------------------------------------- +// Singleton-Methode der Elternklasse +// Hiermit wird sichergestellt, dass wenn immer diese Datei kompiliert wird, +// die Singleton-Methode der Oberklasse diese Klasse instanziiert. +// Unterscheidung zwischen den Plattformen wird so nur durch Linken gegen andere +// Dateien realisiert. +// ----------------------------------------------------------------------------- + +BS_FileSystemUtil & BS_FileSystemUtil::GetInstance() +{ + static BS_FileSystemUtilWin32 Instance; + return Instance; +} diff --git a/engines/sword25/kernel/hashmap.h b/engines/sword25/kernel/hashmap.h new file mode 100755 index 0000000000..0aa663332c --- /dev/null +++ b/engines/sword25/kernel/hashmap.h @@ -0,0 +1,34 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_HASHMAP_H +#define BS_HASHMAP_H + +// stdext::hash_map wird erst seit VC7 untersützt, bei älteren Microsoft-Compilern wird auf std::map zurückgegriffen +#include "kernel/memlog_off.h" +#if _MSC_VER >= 1300 +#include <hash_map> +#define BS_Hashmap stdext::hash_map +#else +#include <map> +#define BS_Hashmap std::map +#endif +#include "kernel/memlog_on.h" + +#endif diff --git a/engines/sword25/kernel/inputpersistenceblock.cpp b/engines/sword25/kernel/inputpersistenceblock.cpp new file mode 100755 index 0000000000..dd0e6437c3 --- /dev/null +++ b/engines/sword25/kernel/inputpersistenceblock.cpp @@ -0,0 +1,191 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#define BS_LOG_PREFIX "INPUTPERSISTENCEBLOCK" + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "inputpersistenceblock.h" + +using namespace std; + +// ----------------------------------------------------------------------------- +// Construction / Destruction +// ----------------------------------------------------------------------------- + +BS_InputPersistenceBlock::BS_InputPersistenceBlock(const void * Data, unsigned int DataLength) : + m_Data(static_cast<const unsigned char *>(Data), static_cast<const unsigned char *>(Data) + DataLength), + m_ErrorState(NONE) +{ + m_Iter = m_Data.begin(); +} + +// ----------------------------------------------------------------------------- + +BS_InputPersistenceBlock::~BS_InputPersistenceBlock() +{ + if (m_Iter != m_Data.end()) BS_LOG_WARNINGLN("Persistence block was not read to the end."); +} + +// ----------------------------------------------------------------------------- +// Reading +// ----------------------------------------------------------------------------- + +void BS_InputPersistenceBlock::Read(signed int & Value) +{ + if (CheckMarker(SINT_MARKER)) + { + RawRead(&Value, sizeof(signed int)); + Value = ConvertEndianessFromStorageToSystem(Value); + } + else + { + Value = 0; + } +} + +// ----------------------------------------------------------------------------- + +void BS_InputPersistenceBlock::Read(unsigned int & Value) +{ + if (CheckMarker(UINT_MARKER)) + { + RawRead(&Value, sizeof(unsigned int)); + Value = ConvertEndianessFromStorageToSystem(Value); + } + else + { + Value = 0; + } +} + +// ----------------------------------------------------------------------------- + +void BS_InputPersistenceBlock::Read(float & Value) +{ + if (CheckMarker(FLOAT_MARKER)) + { + RawRead(&Value, sizeof(float)); + Value = ConvertEndianessFromStorageToSystem(Value); + } + else + { + Value = 0.0f; + } +} + +// ----------------------------------------------------------------------------- + +void BS_InputPersistenceBlock::Read(bool & Value) +{ + if (CheckMarker(BOOL_MARKER)) + { + unsigned int UIntBool; + RawRead(&UIntBool, sizeof(float)); + UIntBool = ConvertEndianessFromStorageToSystem(UIntBool); + Value = UIntBool == 0 ? false : true; + } + else + { + Value = 0.0f; + } +} + +// ----------------------------------------------------------------------------- + +void BS_InputPersistenceBlock::Read(std::string & Value) +{ + Value = ""; + + if (CheckMarker(STRING_MARKER)) + { + unsigned int Size; + Read(Size); + + if (CheckBlockSize(Size)) + { + Value = std::string(reinterpret_cast<const char *>(&*m_Iter), Size); + m_Iter += Size; + } + } +} + +// ----------------------------------------------------------------------------- + +void BS_InputPersistenceBlock::Read(vector<unsigned char> & Value) +{ + if (CheckMarker(BLOCK_MARKER)) + { + unsigned int Size; + Read(Size); + + if (CheckBlockSize(Size)) + { + Value = vector<unsigned char>(m_Iter, m_Iter + Size); + m_Iter += Size; + } + } +} + +// ----------------------------------------------------------------------------- + +void BS_InputPersistenceBlock::RawRead(void * DestPtr, size_t Size) +{ + if (CheckBlockSize(Size)) + { + memcpy(DestPtr, &*m_Iter, Size); + m_Iter += Size; + } +} + +// ----------------------------------------------------------------------------- + +bool BS_InputPersistenceBlock::CheckBlockSize(int Size) +{ + if (m_Data.end() - m_Iter >= Size) + { + return true; + } + else + { + m_ErrorState = END_OF_DATA; + BS_LOG_ERRORLN("Unexpected end of persistence block."); + return false; + } +} + +// ----------------------------------------------------------------------------- + +bool BS_InputPersistenceBlock::CheckMarker(unsigned char Marker) +{ + if (!IsGood() || !CheckBlockSize(1)) return false; + + if (*m_Iter++ == Marker) + { + return true; + } + else + { + m_ErrorState = OUT_OF_SYNC; + BS_LOG_ERRORLN("Wrong type marker found in persistence block."); + return false; + } +} diff --git a/engines/sword25/kernel/inputpersistenceblock.h b/engines/sword25/kernel/inputpersistenceblock.h new file mode 100755 index 0000000000..3a1335943b --- /dev/null +++ b/engines/sword25/kernel/inputpersistenceblock.h @@ -0,0 +1,71 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_INPUTPERSISTENCEBLOCK_H +#define BS_INPUTPERSISTENCEBLOCK_H + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "kernel/common.h" +#include "kernel/persistenceblock.h" +#include "kernel/memlog_off.h" +#include <vector> +#include "kernel/memlog_on.h" + + +// ----------------------------------------------------------------------------- +// Class declaration +// ----------------------------------------------------------------------------- + +class BS_InputPersistenceBlock : public BS_PersistenceBlock +{ +public: + enum ErrorState + { + NONE, + END_OF_DATA, + OUT_OF_SYNC, + }; + + BS_InputPersistenceBlock(const void * Data, unsigned int DataLength); + virtual ~BS_InputPersistenceBlock(); + + void Read(signed int & Value); + void Read(unsigned int & Value); + void Read(float & Value); + void Read(bool & Value); + void Read(std::string & Value); + void Read(std::vector<unsigned char> & Value); + + bool IsGood() const { return m_ErrorState == NONE; } + ErrorState GetErrorState() const { return m_ErrorState; } + +private: + bool CheckMarker(unsigned char Marker); + bool CheckBlockSize(int Size); + void RawRead(void * DestPtr, size_t Size); + + std::vector<unsigned char> m_Data; + std::vector<unsigned char>::const_iterator m_Iter; + ErrorState m_ErrorState; +}; + +#endif diff --git a/engines/sword25/kernel/kernel.cpp b/engines/sword25/kernel/kernel.cpp new file mode 100755 index 0000000000..e6d8ec2872 --- /dev/null +++ b/engines/sword25/kernel/kernel.cpp @@ -0,0 +1,412 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <windows.h> +#include <psapi.h> +#pragma comment(lib, "psapi.lib") + +#include <math.h> +#include <algorithm> + +#include "kernel.h" +#include "timer.h" +#include "service_ids.h" + +#include "gfx/graphicengine.h" +#include "sfx/soundengine.h" +#include "input/inputengine.h" +#include "package/packagemanager.h" +#include "script/script.h" +#include "fmv/movieplayer.h" +#include "persistenceservice.h" +#include "cpuinfo.h" + +#define BS_LOG_PREFIX "KERNEL" + +BS_Kernel * BS_Kernel::_Instance = 0; + +// Konstruktion / Destruktion +// -------------------------- +BS_Kernel::BS_Kernel() : + _pWindow(NULL), + _Running(false), + _pResourceManager(NULL), + _InitSuccess(false) +{ + // TODO: + // Messagebox ausgeben wenn nicht gelogged werden kann -> log.txt schreibgeschützt + BS_LOGLN("created."); + + // CPU-Daten in die Log-Datei schreiben + const BS_CPUInfo & CI = BS_CPUInfo::GetInstance(); + BS_LOGLN("CPU detected (vendor name: \"%s\", CPU name: \"%s\").", CI.GetVendorString().c_str(), CI.GetCPUName().c_str()); + BS_LOGLN("CPU features: %s%s%s%s%s.", + CI.IsMMXSupported() ? "MMX" : "", + CI.IsSSESupported() ? " SSE" : "", + CI.IsSSE2Supported() ? " SSE2" : "", + CI.Is3DNowSupported() ? " 3DNow!" : "", + CI.Is3DNowExtSupported() ? " 3DNow!Ext" : ""); + + // Sicherstellen, dass der Prozessor über MMX verfügt + if (!CI.IsMMXSupported()) + { + BS_LOG_ERRORLN("MMX support needed."); + return; + } + + // Feststellen, ob der Timer unterstützt wird. + if (!BS_Timer::IsTimerAvaliable()) + { + BS_LOG_ERRORLN("This machine doesn't support a performance counter."); + return; + } + + // Die BS_SERVICE_TABLE auslesen und kernelinterne Strukturen vorbereiten + for (unsigned int i = 0; i < BS_SERVICE_COUNT; i++) + { + // Ist die Superclass schon registriert? + Superclass* pCurSuperclass = NULL; + std::vector<Superclass*>::iterator Iter; + for (Iter = _SuperclassList.begin(); Iter != _SuperclassList.end(); ++Iter) + if ((*Iter)->GetIdentifier() == BS_SERVICE_TABLE[i].SuperclassIdentifier) + { + pCurSuperclass = *Iter; + break; + } + + // Falls die Superclass noch nicht registriert war, wird dies jetzt gemacht + if (!pCurSuperclass) + _SuperclassList.push_back(new Superclass(this, BS_SERVICE_TABLE[i].SuperclassIdentifier)); + } + + // Fensterobjekt erstellen + _pWindow = BS_Window::CreateBSWindow(0,0,0,0,false); + if (!_pWindow) + { + BS_LOG_ERRORLN("Failed to create the window."); + } + else + BS_LOGLN("Window created."); + + // Resource-Manager erstellen + _pResourceManager = new BS_ResourceManager(this); + + // Random-Number-Generator initialisieren + srand(GetMilliTicks()); + + // Die Skriptengine initialisieren + // Die Skriptengine muss bereits von Kernel und nicht vom Benutzer gestartet werden, damit der Kernel seine Funktionen bei seiner Erzeugung + // registrieren kann. + BS_ScriptEngine * pScript = static_cast<BS_ScriptEngine *>(NewService("script", "lua")); + if (!pScript || !pScript->Init()) + { + _InitSuccess = false; + return; + } + + // Scriptbindings des Kernels registrieren + if (!_RegisterScriptBindings()) + { + BS_LOG_ERRORLN("Script bindings could not be registered."); + _InitSuccess = false; + return; + } + BS_LOGLN("Script bindings registered."); + + _InitSuccess = true; +} + +BS_Kernel::~BS_Kernel() +{ + // Services in umgekehrter Reihenfolge der Erstellung endladen. + while (!_ServiceCreationOrder.empty()) + { + Superclass * superclass = GetSuperclassByIdentifier(_ServiceCreationOrder.top()); + if (superclass) superclass->DisconnectService(); + _ServiceCreationOrder.pop(); + } + + // Superclasslist leeren + while (_SuperclassList.size()) + { + delete _SuperclassList.back(); + _SuperclassList.pop_back(); + } + + // Fensterobjekt freigeben + delete _pWindow; + BS_LOGLN("Window destroyed."); + + // Resource-Manager freigeben + delete _pResourceManager; + + BS_LOGLN("destroyed."); +} + +// Service Methoden +// ---------------- +BS_Kernel::Superclass::Superclass (BS_Kernel* pKernel, const std::string& Identifier) : + _pKernel(pKernel), + _Identifier(Identifier), + _ServiceCount(0), + _ActiveService(NULL) +{ + for (unsigned int i = 0; i < BS_SERVICE_COUNT; i++) + if (BS_SERVICE_TABLE[i].SuperclassIdentifier == _Identifier) + _ServiceCount++; +} + +BS_Kernel::Superclass::~Superclass() +{ + DisconnectService(); +} + +std::string BS_Kernel::Superclass::GetServiceIdentifier(unsigned int Number) +{ + if (Number > _ServiceCount) return NULL; + + unsigned int CurServiceOrd = 0; + for (unsigned int i = 0; i < BS_SERVICE_COUNT; i++) + { + if (BS_SERVICE_TABLE[i].SuperclassIdentifier == _Identifier) + if (Number == CurServiceOrd) + return BS_SERVICE_TABLE[i].ServiceIdentifier; + else + CurServiceOrd++; + } + + return std::string(""); +} + +BS_Service* BS_Kernel::Superclass::NewService(const std::string& ServiceIdentifier) +{ + for (unsigned int i = 0; i < BS_SERVICE_COUNT; i++) + if (BS_SERVICE_TABLE[i].SuperclassIdentifier == _Identifier && + BS_SERVICE_TABLE[i].ServiceIdentifier == ServiceIdentifier) + { + BS_Service* NewService = BS_SERVICE_TABLE[i].CreateMethod(_pKernel); + + if (NewService) + { + DisconnectService(); + BS_LOGLN("Service '%s' created from superclass '%s'.", ServiceIdentifier.c_str(), _Identifier.c_str()); + _ActiveService = NewService; + _ActiveServiceName = BS_SERVICE_TABLE[i].ServiceIdentifier; + return _ActiveService; + } + else + { + BS_LOG_ERRORLN("Failed to create service '%s' from superclass '%s'.", ServiceIdentifier.c_str(), _Identifier.c_str()); + return NULL; + } + } + + BS_LOG_ERRORLN("Service '%s' is not avaliable from superclass '%s'.", ServiceIdentifier.c_str(), _Identifier.c_str()); + return NULL; +} + +bool BS_Kernel::Superclass::DisconnectService() +{ + if (_ActiveService) + { + delete _ActiveService; + _ActiveService = 0; + BS_LOGLN("Active service '%s' disconnected from superclass '%s'.", _ActiveServiceName.c_str(), _Identifier.c_str()); + return true; + } + + return false; +} + +BS_Kernel::Superclass* BS_Kernel::GetSuperclassByIdentifier(const std::string & Identifier) +{ + std::vector<Superclass*>::iterator Iter; + for (Iter = _SuperclassList.begin(); Iter != _SuperclassList.end(); ++Iter) + { + if ((*Iter)->GetIdentifier() == Identifier) + return *Iter; + } + + // BS_LOG_ERRORLN("Superclass '%s' does not exist.", Identifier.c_str()); + return NULL; +} + +unsigned int BS_Kernel::GetSuperclassCount() +{ + return _SuperclassList.size(); +} + +std::string BS_Kernel::GetSuperclassIdentifier(unsigned int Number) +{ + if (Number > _SuperclassList.size()) return NULL; + + unsigned int CurSuperclassOrd = 0; + std::vector<Superclass*>::iterator Iter; + for (Iter = _SuperclassList.begin(); Iter != _SuperclassList.end(); ++Iter) + { + if (CurSuperclassOrd == Number) + return ((*Iter)->GetIdentifier()); + + CurSuperclassOrd++; + } + + return std::string(""); +} + +unsigned int BS_Kernel::GetServiceCount(const std::string & SuperclassIdentifier) +{ + Superclass* pSuperclass; + if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return 0; + + return pSuperclass->GetServiceCount(); + +} + +std::string BS_Kernel::GetServiceIdentifier(const std::string & SuperclassIdentifier, unsigned int Number) +{ + Superclass* pSuperclass; + if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return NULL; + + return (pSuperclass->GetServiceIdentifier(Number)); +} + +BS_Service* BS_Kernel::NewService(const std::string& SuperclassIdentifier, const std::string& ServiceIdentifier) +{ + Superclass* pSuperclass; + if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return NULL; + + // Die Reihenfolge merken, in der Services erstellt werden, damit sie später in umgekehrter Reihenfolge entladen werden können. + _ServiceCreationOrder.push(SuperclassIdentifier); + + return pSuperclass->NewService(ServiceIdentifier); +} + +bool BS_Kernel::DisconnectService(const std::string& SuperclassIdentifier) +{ + Superclass* pSuperclass; + if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return false; + + return pSuperclass->DisconnectService(); +} + +BS_Service* BS_Kernel::GetService(const std::string& SuperclassIdentifier) +{ + Superclass* pSuperclass; + if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return NULL; + + return (pSuperclass->GetActiveService()); +} + +std::string BS_Kernel::GetActiveServiceIdentifier(const std::string& SuperclassIdentifier) +{ + Superclass * pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier); + if (!pSuperclass) return std::string(""); + + return (pSuperclass->GetActiveServiceName()); +} + +// ----------------------------------------------------------------------------- + +int BS_Kernel::GetRandomNumber(int Min, int Max) +{ + BS_ASSERT(Min <= Max); + unsigned int MaxInternal = (Min - Max + 1) < 0 ? - (Min - Max + 1) : (Min - Max + 1); + return (rand() % MaxInternal) + Min; +} + +// Timer Methoden +// -------------- +unsigned int BS_Kernel::GetMilliTicks() +{ + return BS_Timer::GetMilliTicks(); +} + +uint64_t BS_Kernel::GetMicroTicks() +{ + return BS_Timer::GetMicroTicks(); +} + +// Sonstige Methoden +// ----------------- + +size_t BS_Kernel::GetUsedMemory() +{ + PROCESS_MEMORY_COUNTERS pmc; + pmc.cb = sizeof(pmc); + if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) + { + return pmc.WorkingSetSize; + } + else + { + BS_LOG_ERRORLN("Call to GetProcessMemoryInfo() failed. Error code: %d", GetLastError()); + return 0; + } +} + +// ----------------------------------------------------------------------------- + +BS_GraphicEngine * BS_Kernel::GetGfx() +{ + return static_cast<BS_GraphicEngine *>(GetService("gfx")); +} + +// ----------------------------------------------------------------------------- + +BS_SoundEngine * BS_Kernel::GetSfx() +{ + return static_cast<BS_SoundEngine *>(GetService("sfx")); +} + +// ----------------------------------------------------------------------------- + +BS_InputEngine * BS_Kernel::GetInput() +{ + return static_cast<BS_InputEngine *>(GetService("input")); +} + +// ----------------------------------------------------------------------------- + +BS_PackageManager * BS_Kernel::GetPackage() +{ + return static_cast<BS_PackageManager *>(GetService("package")); +} + +// ----------------------------------------------------------------------------- + +BS_ScriptEngine * BS_Kernel::GetScript() +{ + return static_cast<BS_ScriptEngine *>(GetService("script")); +} + +// ----------------------------------------------------------------------------- + +BS_MoviePlayer * BS_Kernel::GetFMV() +{ + return static_cast<BS_MoviePlayer *>(GetService("fmv")); +} + +// ----------------------------------------------------------------------------- + +void BS_Kernel::Sleep(unsigned int Msecs) const +{ + ::Sleep(Msecs); +} diff --git a/engines/sword25/kernel/kernel.h b/engines/sword25/kernel/kernel.h new file mode 100755 index 0000000000..539ce7a9fb --- /dev/null +++ b/engines/sword25/kernel/kernel.h @@ -0,0 +1,344 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +/* + BS_Kernel + --------- + Dies ist die Hauptklasse der Engine. + Diese Klasse erzeugt und verwaltet alle anderen Enginelemente, wie Soundengine, Graphikengine... + Es ist nicht notwendig alle Enginenelemente einzeln freizugeben, dieses wird von der Kernelklasse übernommen. + + Autor: Malte Thiesen +*/ + +#ifndef _BS_KERNEL_H +#define _BS_KERNEL_H + +// Includes +#include "memlog_off.h" +#include <vector> +#include <stack> +#include <string> +#include "memlog_on.h" + +#include "common.h" +#include "bs_stdint.h" +#include "window.h" +#include "resmanager.h" + + +// Klassendefinition +class BS_Service; +class BS_GraphicEngine; +class BS_ScriptEngine; +class BS_SoundEngine; +class BS_InputEngine; +class BS_PackageManager; +class BS_MoviePlayer; + +/** + @brief Dies ist die Hauptklasse der Engine. + + Diese Klasse erzeugt und verwaltet alle anderen Engineelemente, wie Soundengine, Graphikengine...<br> + Es ist nicht notwendig alle Enginenelemente einzeln freizugeben, dieses wird von der Kernelklasse übernommen. +*/ +class BS_Kernel +{ +public: + // Fenster Methoden + // ---------------- + /** + @brief Gibt einen Pointer auf das Fensterobjekt zurück. + @return Gibt einen Pointer auf das Fensterobjekt zurück. + */ + BS_Window* GetWindow() {return _pWindow; } + + // Service Methoden + // ---------------- + + /** + @brief Erzeugt einen neuen Service der angegebenen Superclass mit dem übergebenen Identifier. + @param SuperclassIdentifier der Name der Superclass des Services<br> + z.B: "sfx", "gfx", "package" ... + @param ServiceIdentifier der Name des Services<br> + Für die Superclass "sfx" könnten das z.B. "fmod" oder "directsound" sein. + @return Gibt einen Pointer auf den Service zurück, oder NULL wenn der Service nicht erstellt werden konnte. + @remark Alle Services müssen in service_ids.h eingetragen sein, sonst können sie hier nicht erstellt werden. + */ + BS_Service* NewService(const std::string & SuperclassIdentifier, const std::string & ServiceIdentifier); + /** + @brief Beendet den aktuellen Service einer Superclass. + @param SuperclassIdentfier der Name der Superclass dessen aktiver Service beendet werden soll<br> + z.B: "sfx", "gfx", "package" ... + @return Gibt bei Erfolg true zurück und false wenn die Superclass nicht existiert oder wenn kein Service aktiv war. + */ + bool DisconnectService(const std::string & SuperclassIdentifier); + + /** + @brief Gibt einen Pointer auf das momentan aktive Serviceobjekt einer Superclass zurück. + @param SuperclassIdentfier der Name der Superclass<br> + z.B: "sfx", "gfx", "package" ... + @return Gibt einen Pointer auf den Service zurück, oder NULL wenn kein Service aktiv war. + */ + BS_Service* GetService(const std::string& SuperclassIdentifier); + + /** + @brief Gibt den Namen des aktuell aktiven Serviceobjektes einer Superclass zurück. + @param SuperclassIdentfier der Name der Superclass<br> + z.B: "sfx", "gfx", "package" ... + @return Gibt den Namen des Serviceobjektes zurück, oder einen leeren String, wenn ein Fehler aufgetreten ist. + */ + std::string GetActiveServiceIdentifier(const std::string& SuperclassIdentifier); + + /** + @brief Gibt die Anzahl der Registrierten Superclasses zurück. + @return Gibt die Anzahl der Registrierten Superclasses zurück. + */ + unsigned int GetSuperclassCount(); + + // Gibt den Identifier der mit Number bezeichneten Superclass zurück + /** + @brief Gibt den Identifier einer Superclass zurück. + @param Number die Nummer der Superclass, dessen Bezeichner man erfahren möchte<br> + Hierbei ist zu beachten, dass die erste Superclass die Nummer 0 erhält. Number muss also eine Zahl zwischen + 0 und GetSuperclassCount() - 1 sein. + @return Gibt den Identifier der Superclass zurück. + @remark Die Anzahl der Superclasses kann man mit GetSuperclassCount() erfahren. + */ + std::string GetSuperclassIdentifier(unsigned int Number); + + // Gibt die Anzahl der für die mit SuperclassIdentifier bezeichneten Superclass vorhandenen + // Services zurück + /** + @brief Gibt die Anzahl an Services zurück, die in einer Superclass registriert sind. + @param SuperclassIdentifier der Name der Superclass<br> + z.B: "sfx", "gfx", "package" ... + @return Gibt die Anzahl an Services zurück, die in der Superclass registriert sind. + */ + unsigned int GetServiceCount(const std::string & SuperclassIdentifier); + + /** + @brief Gibt den Identifier eines Services in einer Superclass zurück. + @param SuperclassIdentifier der Name der Superclass<br> + z.B: "sfx", "gfx", "package" ... + @param Number die Nummer des Services, dessen Bezeichner man erfahren will.<br> + Hierbei ist zu beachten, dass der erste Service die Nummer 0 erhält. Number muss also eine Zahl zwischen + 0 und GetServiceCount() - 1 sein. + @return Gibt den Identifier des Services zurück + @remark Die Anzahl der Services in einer Superclass kann man mit GetServiceCount() erfahren. + */ + std::string GetServiceIdentifier(const std::string & SuperclassIdentifier, unsigned int Number); + /** + @brief Gibt die vergangene Zeit seit dem Systemstart in Millisekunden zurück. + */ + unsigned int GetMilliTicks(); + /** + @brief Gibt die vergangene Zeit seit dem Systemstart in Microsekunden zurück. + @remark Diese Methode sollte nur verwendet werden, falls GetMilliTick() für den gewünschten Einsatz zu ungenau ist. + */ + uint64_t GetMicroTicks(); + /** + @brief Gibt an, ob die Konstruktion erfolgreich war. + @return Gibt true zurück, wenn die Konstruktion erfolgreich war. + */ + bool GetInitSuccess() { return _InitSuccess; } + /** + @brief Gibt einen Pointer auf den BS_ResourceManager zurück. + */ + BS_ResourceManager* GetResourceManager() { return _pResourceManager; } + /** + @brief Gibt zurück wie viel Speicher von diesem Prozess belegt ist. + */ + size_t GetUsedMemory(); + /** + @brief Gibt eine Zufallszahl zurück. + @param Min der minimale Wert, den die Zufallszahl haben darf + @param Max der maximale Wert, den die Zufallszahl haben darf + @return Gibt eine Zufallszahl zurück, die zwischen Min und Max liegt. + */ + int GetRandomNumber(int Min, int Max); + /** + @brief Gibt einen Pointer auf den aktiven Gfx-Service zurück oder NULL wenn kein Gfx-Service aktiv. + */ + BS_GraphicEngine * GetGfx(); + /** + @brief Gibt einen Pointer auf den aktiven Sfx-Service zurück oder NULL wenn kein Sfx-Service aktiv. + */ + BS_SoundEngine * GetSfx(); + /** + @brief Gibt einen Pointer auf den aktiven Input-Service zurück oder NULL wenn kein Input-Service aktiv. + */ + BS_InputEngine * GetInput(); + /** + @brief Gibt einen Pointer auf den aktiven Package-Service zurück oder NULL wenn kein Package-Service aktiv. + */ + BS_PackageManager * GetPackage(); + /** + @brief Gibt einen Pointer auf den aktiven Script-Service zurück oder NULL wenn kein Script-Service aktiv. + */ + BS_ScriptEngine * GetScript(); + /** + @brief Gibt einen Pointer auf den aktiven FMV-Service zurück oder NULL wenn kein FMV-Service aktiv. + */ + BS_MoviePlayer * GetFMV(); + + /** + @brief Stoppt den Prozess für eine gewisse Zeit. + @param Msecs Zeit in Millisekunden, die der Prozess gestoppt werden soll. + @remark Es wird nicht garantiert, dass der Prozess genau nach der angegebenen Zeit wieder aktiv wird. + */ + void Sleep(unsigned int Msecs) const; + + /** + @brief Gibt das einzige Exemplar des Kernels zurück (Singleton). + */ + + static BS_Kernel * GetInstance() + { + if (!_Instance) _Instance = new BS_Kernel(); + return _Instance; + } + + /** + @brief Zerstört das einzige Exemplar des Kernels. + @remark Diese Methode darf nur zum Beenden des System aufgerufen werden. Nachfolgende Aufrufe sämtlicher Kernelmethoden liefern + undefinierte Ergebnisse. + */ + + static void DeleteInstance() + { + if (_Instance) + { + delete(_Instance); + _Instance = 0; + } + } + + /** + @brief Löst eine Schutzverletzung aus. + @remark Diese Methode dient zum Testen des Crash-Handlings. + */ + void Crash() const + { + __asm + { + xor eax, eax + mov [eax], 0 + } + } + +private: + + // ----------------------------------------------------------------------------- + // Konstruktion / Destruktion + // Private da Singleton + // ----------------------------------------------------------------------------- + + BS_Kernel(); + virtual ~BS_Kernel(); + + // ----------------------------------------------------------------------------- + // Singleton-Exemplar + // ----------------------------------------------------------------------------- + static BS_Kernel * _Instance; + + // Service Daten + // ------------- + class Superclass + { + private: + BS_Kernel* _pKernel; + unsigned int _ServiceCount; + std::string _Identifier; + BS_Service* _ActiveService; + std::string _ActiveServiceName; + + public: + Superclass (BS_Kernel* pKernel, const std::string& Identifier); + ~Superclass(); + + unsigned int GetServiceCount() const { return _ServiceCount; } + std::string GetIdentifier() const { return _Identifier; } + BS_Service* GetActiveService() const { return _ActiveService; } + std::string GetActiveServiceName() const { return _ActiveServiceName; } + std::string GetServiceIdentifier(unsigned int Number); + BS_Service* NewService(const std::string& ServiceIdentifier); + bool DisconnectService(); + }; + + std::vector<Superclass*> _SuperclassList; + std::stack<std::string> _ServiceCreationOrder; + Superclass* GetSuperclassByIdentifier(const std::string& Identifier); + + bool _InitSuccess; // Gibt an, ob die Konstruktion erfolgreich war + bool _Running; // Gibt an, ob die Applikation im nächsten Durchlauf des Main-Loops noch weiterlaufen soll + + // Fenster Variablen + // ----------------- + BS_Window* _pWindow; + + /* + // CPU-Feature Variablen und Methoden + // ---------------------------------- + enum _CPU_FEATURES_BITMASKS + { + _MMX_BITMASK = (1 << 23), + _SSE_BITMASK = (1 << 25), + _SSE2_BITMASK = (1 << 26), + _3DNOW_BITMASK = (1 << 30), + _3DNOWEXT_BITMASK = (1 << 31) + }; + + bool _DetectCPU(); + + bool _MMXPresent; + bool _SSEPresent; + bool _SSE2Present; + bool _3DNowPresent; + bool _3DNowExtPresent; + CPU_TYPES _CPUType; + std::string _CPUVendorID; + */ + + // Resourcemanager + // --------------- + BS_ResourceManager* _pResourceManager; + + bool _RegisterScriptBindings(); +}; + +// Dies ist nur eine kleine Klasse, die die Daten eines Services verwaltet. +// Ist ein wenig unschön, ich weiss, aber mit std::string ließ sich dass nicht mit einer +// einfachen struct bewerkstelligen. +class BS_ServiceInfo +{ +public: + BS_ServiceInfo(const std::string& SuperclassIdentifier, const std::string& ServiceIdentifier, BS_Service* (*CreateMethod)(BS_Kernel*)) + { + this->SuperclassIdentifier = SuperclassIdentifier; + this->ServiceIdentifier = ServiceIdentifier; + this->CreateMethod = CreateMethod; + }; + + std::string SuperclassIdentifier; + std::string ServiceIdentifier; + BS_Service* (*CreateMethod)(BS_Kernel*); +}; + +#endif diff --git a/engines/sword25/kernel/kernel_script.cpp b/engines/sword25/kernel/kernel_script.cpp new file mode 100755 index 0000000000..4436e05e55 --- /dev/null +++ b/engines/sword25/kernel/kernel_script.cpp @@ -0,0 +1,775 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "common.h" +#include "kernel.h" +#include "filesystemutil.h" +#include "window.h" +#include "resmanager.h" +#include "persistenceservice.h" +#include "wincodegenerator.h" +#include "debug/debugtools.h" +#include "script/script.h" +#include "script/luabindhelper.h" + +// ----------------------------------------------------------------------------- + +static int DisconnectService(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + + lua_pushboolean(L, pKernel->DisconnectService(luaL_checkstring(L, 1))); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetActiveServiceIdentifier(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + + lua_pushstring(L, pKernel->GetActiveServiceIdentifier(luaL_checkstring(L,1)).c_str()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetSuperclassCount(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + + lua_pushnumber(L, pKernel->GetSuperclassCount()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetSuperclassIdentifier(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + + lua_pushstring(L, pKernel->GetSuperclassIdentifier(static_cast<unsigned int>(luaL_checknumber(L,1))).c_str()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetServiceCount(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + + lua_pushnumber(L, pKernel->GetServiceCount(luaL_checkstring(L, 1))); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetServiceIdentifier(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + + lua_pushstring(L, pKernel->GetServiceIdentifier(luaL_checkstring(L, 1), static_cast<unsigned int>(luaL_checknumber(L, 2))).c_str()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetMilliTicks(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + + lua_pushnumber(L, pKernel->GetMilliTicks()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetTimer(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + + lua_pushnumber(L, static_cast<lua_Number>(pKernel->GetMicroTicks()) / 1000000.0); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int StartService(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + + lua_pushbooleancpp(L, pKernel->NewService(luaL_checkstring(L, 1), luaL_checkstring(L, 2)) != NULL); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int Sleep(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + pKernel->Sleep(static_cast<unsigned int>(luaL_checknumber(L, 1) * 1000)); + return 0; +} + +// ----------------------------------------------------------------------------- + +static int Crash(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + pKernel->Crash(); + return 0; +} + +// ----------------------------------------------------------------------------- + +static int ExecuteFile(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_ScriptEngine * pSE = static_cast<BS_ScriptEngine *>(pKernel->GetService("script")); + BS_ASSERT(pSE); + + lua_pushbooleancpp(L, pSE->ExecuteFile(luaL_checkstring(L, 1))); + + return 0; +} + +// ----------------------------------------------------------------------------- + +static int GetUserdataDirectory(lua_State * L) +{ + lua_pushstring(L, BS_FileSystemUtil::GetInstance().GetUserdataDirectory().c_str()); + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetPathSeparator(lua_State * L) +{ + lua_pushstring(L, BS_FileSystemUtil::GetInstance().GetPathSeparator().c_str()); + return 1; +} + +// ----------------------------------------------------------------------------- + +static int FileExists(lua_State * L) +{ + lua_pushbooleancpp(L, BS_FileSystemUtil::GetInstance().FileExists(luaL_checkstring(L, 1))); + return 1; +} + +// ----------------------------------------------------------------------------- + +static int CreateDirectory(lua_State * L) +{ + lua_pushbooleancpp(L, BS_FileSystemUtil::GetInstance().CreateDirectory(luaL_checkstring(L, 1))); + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetWinCode(lua_State * L) +{ + lua_pushstring(L, BS_WinCodeGenerator::GetWinCode().c_str()); + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetSubversionRevision(lua_State * L) +{ + lua_pushnumber(L, BS_Debugtools::GetSubversionRevision()); + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetUsedMemory(lua_State * L) +{ + lua_pushnumber(L, BS_Kernel::GetInstance()->GetUsedMemory()); + return 1; +} + +// ----------------------------------------------------------------------------- + +static const char * KERNEL_LIBRARY_NAME = "Kernel"; + +static const luaL_reg KERNEL_FUNCTIONS[] = +{ + "DisconnectService", DisconnectService, + "GetActiveServiceIdentifier", GetActiveServiceIdentifier, + "GetSuperclassCount", GetSuperclassCount, + "GetSuperclassIdentifier", GetSuperclassIdentifier, + "GetServiceCount", GetServiceCount, + "GetServiceIdentifier", GetServiceIdentifier, + "GetMilliTicks", GetMilliTicks, + "GetTimer", GetTimer, + "StartService", StartService, + "Sleep", Sleep, + "Crash", Crash, + "ExecuteFile", ExecuteFile, + "GetUserdataDirectory", GetUserdataDirectory, + "GetPathSeparator", GetPathSeparator, + "FileExists", FileExists, + "CreateDirectory", CreateDirectory, + "GetWinCode", GetWinCode, + "GetSubversionRevision", GetSubversionRevision, + "GetUsedMemory", GetUsedMemory, + 0, 0, +}; + +// ----------------------------------------------------------------------------- + +static int IsVisible(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + lua_pushbooleancpp(L, pWindow->IsVisible()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int SetVisible(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + pWindow->SetVisible(lua_tobooleancpp(L, 1)); + + return 0; +} + +// ----------------------------------------------------------------------------- + +static int GetX(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + lua_pushnumber(L, pWindow->GetX()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetY(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + lua_pushnumber(L, pWindow->GetY()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int SetX(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + pWindow->SetX(static_cast<int>(luaL_checknumber(L, 1))); + + return 0; +} + +// ----------------------------------------------------------------------------- + +static int SetY(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + pWindow->SetY(static_cast<int>(luaL_checknumber(L, 1))); + + return 0; +} + +// ----------------------------------------------------------------------------- + +static int GetClientX(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + lua_pushnumber(L, pWindow->GetClientX()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetClientY(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + lua_pushnumber(L, pWindow->GetClientY()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetWidth(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + lua_pushnumber(L, pWindow->GetWidth()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetHeight(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + lua_pushnumber(L, pWindow->GetHeight()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int SetWidth(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + pWindow->SetWidth(static_cast<int>(luaL_checknumber(L, 1))); + + return 0; +} + +// ----------------------------------------------------------------------------- + +static int SetHeight(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + pWindow->SetHeight(static_cast<int>(luaL_checknumber(L, 1))); + + return 0; +} + +// ----------------------------------------------------------------------------- + +static int GetTitle(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + lua_pushstring(L, pWindow->GetTitle().c_str()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int SetTitle(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + pWindow->SetTitle(luaL_checkstring(L, 1)); + + return 0; +} + +// ----------------------------------------------------------------------------- + +static int ProcessMessages(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + lua_pushbooleancpp(L, pWindow->ProcessMessages()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int CloseWanted(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + lua_pushbooleancpp(L, pWindow->CloseWanted()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int WaitForFocus(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + lua_pushbooleancpp(L, pWindow->WaitForFocus()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int HasFocus(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_Window * pWindow = pKernel->GetWindow(); + BS_ASSERT(pWindow); + + lua_pushbooleancpp(L, pWindow->HasFocus()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static const char * WINDOW_LIBRARY_NAME = "Window"; + +static const luaL_reg WINDOW_FUNCTIONS[] = +{ + "IsVisible", IsVisible, + "SetVisible", SetVisible, + "GetX", GetX, + "SetX", SetX, + "GetY", GetY, + "SetY", SetY, + "GetClientX", GetClientX, + "GetClientY", GetClientY, + "GetWidth", GetWidth, + "GetHeight", GetHeight, + "SetWidth", SetWidth, + "SetHeight", SetHeight, + "GetTitle", GetTitle, + "SetTitle", SetTitle, + "ProcessMessages", ProcessMessages, + "CloseWanted", CloseWanted, + "WaitForFocus", WaitForFocus, + "HasFocus", HasFocus, + 0, 0, +}; + +// ----------------------------------------------------------------------------- + +static int PrecacheResource(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_ResourceManager * pResource = pKernel->GetResourceManager(); + BS_ASSERT(pResource); + + lua_pushbooleancpp(L, pResource->PrecacheResource(luaL_checkstring(L, 1))); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int ForcePrecacheResource(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_ResourceManager * pResource = pKernel->GetResourceManager(); + BS_ASSERT(pResource); + + lua_pushbooleancpp(L, pResource->PrecacheResource(luaL_checkstring(L, 1), true)); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetMaxMemoryUsage(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_ResourceManager * pResource = pKernel->GetResourceManager(); + BS_ASSERT(pResource); + + lua_pushnumber(L, pResource->GetMaxMemoryUsage()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int SetMaxMemoryUsage(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_ResourceManager * pResource = pKernel->GetResourceManager(); + BS_ASSERT(pResource); + + pResource->SetMaxMemoryUsage(static_cast<unsigned int>(lua_tonumber(L, 1))); + + return 0; +} + +// ----------------------------------------------------------------------------- + +static int EmptyCache(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_ResourceManager * pResource = pKernel->GetResourceManager(); + BS_ASSERT(pResource); + + pResource->EmptyCache(); + + return 0; +} + +// ----------------------------------------------------------------------------- + +static int IsLogCacheMiss(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_ResourceManager * pResource = pKernel->GetResourceManager(); + BS_ASSERT(pResource); + + lua_pushbooleancpp(L, pResource->IsLogCacheMiss()); + + return 1; +} + +// ----------------------------------------------------------------------------- + +static int SetLogCacheMiss(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_ResourceManager * pResource = pKernel->GetResourceManager(); + BS_ASSERT(pResource); + + pResource->SetLogCacheMiss(lua_tobooleancpp(L, 1)); + + return 0; +} + +// ----------------------------------------------------------------------------- + +static int DumpLockedResources(lua_State * L) +{ + BS_Kernel * pKernel = BS_Kernel::GetInstance(); + BS_ASSERT(pKernel); + BS_ResourceManager * pResource = pKernel->GetResourceManager(); + BS_ASSERT(pResource); + + pResource->DumpLockedResources(); + + return 0; +} + +// ----------------------------------------------------------------------------- + +static const char * RESOURCE_LIBRARY_NAME = "Resource"; + +static const luaL_reg RESOURCE_FUNCTIONS[] = +{ + "PrecacheResource", PrecacheResource, + "ForcePrecacheResource", ForcePrecacheResource, + "GetMaxMemoryUsage", GetMaxMemoryUsage, + "SetMaxMemoryUsage", SetMaxMemoryUsage, + "EmptyCache", EmptyCache, + "IsLogCacheMiss", IsLogCacheMiss, + "SetLogCacheMiss", SetLogCacheMiss, + "DumpLockedResources", DumpLockedResources, + 0, 0, +}; + +// ----------------------------------------------------------------------------- + +static int ReloadSlots(lua_State * L) +{ + BS_PersistenceService::GetInstance().ReloadSlots(); + lua_pushnil(L); + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetSlotCount(lua_State * L) +{ + lua_pushnumber(L, BS_PersistenceService::GetInstance().GetSlotCount()); + return 1; +} + +// ----------------------------------------------------------------------------- + +static int IsSlotOccupied(lua_State * L) +{ + lua_pushbooleancpp(L, BS_PersistenceService::GetInstance().IsSlotOccupied(static_cast<unsigned int>(luaL_checknumber(L, 1)) - 1)); + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetSavegameDirectory(lua_State * L) +{ + lua_pushstring(L, BS_PersistenceService::GetInstance().GetSavegameDirectory().c_str()); + return 1; +} + +// ----------------------------------------------------------------------------- + +static int IsSavegameCompatible(lua_State * L) +{ + lua_pushbooleancpp(L, BS_PersistenceService::GetInstance().IsSavegameCompatible(static_cast<unsigned int>(luaL_checknumber(L, 1)) - 1)); + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetSavegameDescription(lua_State * L) +{ + lua_pushstring(L, BS_PersistenceService::GetInstance().GetSavegameDescription(static_cast<unsigned int>(luaL_checknumber(L, 1)) - 1).c_str()); + return 1; +} + +// ----------------------------------------------------------------------------- + +static int GetSavegameFilename(lua_State * L) +{ + lua_pushstring(L, BS_PersistenceService::GetInstance().GetSavegameFilename(static_cast<unsigned int>(luaL_checknumber(L, 1)) - 1).c_str()); + return 1; +} + +// ----------------------------------------------------------------------------- + +static int LoadGame(lua_State * L) +{ + lua_pushbooleancpp(L, BS_PersistenceService::GetInstance().LoadGame(static_cast<unsigned int>(luaL_checknumber(L, 1)) - 1)); + return 1; +} + +// ----------------------------------------------------------------------------- + +static int SaveGame(lua_State * L) +{ + lua_pushbooleancpp(L, BS_PersistenceService::GetInstance().SaveGame(static_cast<unsigned int>(luaL_checknumber(L, 1)) - 1, luaL_checkstring(L, 2))); + return 1; +} + +// ----------------------------------------------------------------------------- + +static const char * PERSISTENCE_LIBRARY_NAME = "Persistence"; + +static const luaL_reg PERSISTENCE_FUNCTIONS[] = +{ + "ReloadSlots", ReloadSlots, + "GetSlotCount", GetSlotCount, + "IsSlotOccupied", IsSlotOccupied, + "GetSavegameDirectory", GetSavegameDirectory, + "IsSavegameCompatible", IsSavegameCompatible, + "GetSavegameDescription", GetSavegameDescription, + "GetSavegameFilename", GetSavegameFilename, + "LoadGame", LoadGame, + "SaveGame", SaveGame, + 0, 0, +}; + +// ----------------------------------------------------------------------------- + +bool BS_Kernel::_RegisterScriptBindings() +{ + BS_ScriptEngine * pScript = static_cast<BS_ScriptEngine *>(GetService("script")); + BS_ASSERT(pScript); + lua_State * L = static_cast<lua_State *>(pScript->GetScriptObject()); + BS_ASSERT(L); + + if (!BS_LuaBindhelper::AddFunctionsToLib(L, KERNEL_LIBRARY_NAME, KERNEL_FUNCTIONS)) return false; + if (!BS_LuaBindhelper::AddFunctionsToLib(L, WINDOW_LIBRARY_NAME, WINDOW_FUNCTIONS)) return false; + if (!BS_LuaBindhelper::AddFunctionsToLib(L, RESOURCE_LIBRARY_NAME, RESOURCE_FUNCTIONS)) return false; + if (!BS_LuaBindhelper::AddFunctionsToLib(L, PERSISTENCE_LIBRARY_NAME, PERSISTENCE_FUNCTIONS)) return false; + + return true; +} diff --git a/engines/sword25/kernel/log.cpp b/engines/sword25/kernel/log.cpp new file mode 100755 index 0000000000..bf1d5147b1 --- /dev/null +++ b/engines/sword25/kernel/log.cpp @@ -0,0 +1,224 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#include <stdio.h> +#include <stdarg.h> +#include <string> + +#include "filesystemutil.h" +#include "log.h" +#include "debug/debugtools.h" + +// Konstanten +static const char* BF_LOG_FILENAME = "log.txt"; +static const size_t LOG_BUFFERSIZE = 1024 * 16; + +// Logging soll nur stattfinden wenn es aktiviert ist +#ifdef BS_ACTIVATE_LOGGING + +FILE* BS_Log::_LogFile = NULL; +bool BS_Log::_LineBegin = true; +const char* BS_Log::_Prefix = NULL; +const char* BS_Log::_File = NULL; +int BS_Log::_Line = 0; +bool BS_Log::_AutoNewline = false; +std::vector<BS_Log::LOG_LISTENER_CALLBACK> BS_Log::_LogListener; + +bool BS_Log::_CreateLog() +{ + // Logfile öffnen + BS_FileSystemUtil::GetInstance().CreateDirectory(BS_FileSystemUtil::GetInstance().GetUserdataDirectory()); + _LogFile = fopen((BS_FileSystemUtil::GetInstance().GetUserdataDirectory() + "\\" + BF_LOG_FILENAME).c_str(), "w"); + + if (_LogFile) + { + // Sicherstellen, dass es beim Beenden geschlossen wird + atexit(_CloseLog); + + // Titelzeile in das Logfile schreiben + Log("Broken Sword 2.5 Engine - Build: %s - %s - VersionID: %s\n", __DATE__, __TIME__, BS_Debugtools::GetVersionID()); + Log("-----------------------------------------------------------------------------------------------------\n"); + + return true; + } + + // Log-File konnte nicht erstellt werden + return false; +} + +void BS_Log::_CloseLog() +{ + if (_LogFile) fclose(_LogFile); +} + +void BS_Log::Log(const char* Format, ...) +{ + char Message[LOG_BUFFERSIZE]; + + // Nachricht erzeugen + va_list ArgList; + va_start(ArgList, Format); + _vsnprintf(Message, sizeof(Message), Format, ArgList); + + // Nachricht loggen + _WriteLog(Message); + + _FlushLog(); +} + +void BS_Log::LogPrefix(const char* Prefix, const char* Format, ...) +{ + char Message[LOG_BUFFERSIZE]; + char ExtFormat[LOG_BUFFERSIZE]; + + // Falls die Ausgabe am Anfang einer neuen Zeile aufgehört hat, muss die neue Ausgabe mit dem Präfix + // beginnen + ExtFormat[0] = 0; + if (_LineBegin) + { + _snprintf(ExtFormat, sizeof(ExtFormat), "%s%s: ", ExtFormat, Prefix); + _LineBegin = false; + } + // Formatstring zeilenweise durchgehen und an jeden Zeilenanfang das Präfix setzen + for (;;) + { + const char* NextLine = strstr(Format, "\n"); + if (!NextLine || *(NextLine + strlen("\n")) == 0) + { + _snprintf(ExtFormat, sizeof(ExtFormat), "%s%s", ExtFormat, Format); + if (NextLine) _LineBegin = true; + break; + } + else + { + strncat(ExtFormat, Format, (NextLine - Format) + strlen("\n")); + _snprintf(ExtFormat, sizeof(ExtFormat), "%s%s: ", ExtFormat, Prefix); + } + + Format = NextLine + strlen("\n"); + } + + // Nachricht erzeugen + va_list ArgList; + va_start(ArgList, Format); + _vsnprintf(Message, sizeof(Message), ExtFormat, ArgList); + + // Nachricht schreiben + _WriteLog(Message); + + _FlushLog(); +} + +void BS_Log::LogDecorated(const char* Format, ...) +{ + // Nachricht erzeugen + char Message[LOG_BUFFERSIZE]; + va_list ArgList; + va_start(ArgList, Format); + _vsnprintf(Message, sizeof(Message), Format, ArgList); + + // Zweiten Prefix erzeugen, falls gewünscht + char SecondaryPrefix[1024]; + if (_File && _Line) + _snprintf(SecondaryPrefix, sizeof(SecondaryPrefix), "(file: %s, line: %d) - ", _File, _Line); + + // Nachricht zeilenweise ausgeben und an jeden Zeilenanfang das Präfix setzen + char* MessageWalker = Message; + for (;;) + { + char* NextLine = strstr(MessageWalker, "\n"); + if (NextLine) + { + *NextLine = 0; + if (_LineBegin) + { + _WriteLog(_Prefix); + if (_File && _Line) + _WriteLog(SecondaryPrefix); + } + _WriteLog(MessageWalker); + _WriteLog("\n"); + MessageWalker = NextLine + sizeof("\n") - 1; + _LineBegin = true; + } + else + { + if (_LineBegin) + { + _WriteLog(_Prefix); + if (_File && _Line) + _WriteLog(SecondaryPrefix); + } + _WriteLog(MessageWalker); + _LineBegin = false; + break; + } + } + + // Falls gewünscht, wird ans Ende der Nachricht automatisch ein Newline angehängt. + if (_AutoNewline) + { + _WriteLog("\n"); + _LineBegin = true; + } + + // Pseudoparameter zurücksetzen + _Prefix = NULL; + _File = 0; + _Line = 0; + _AutoNewline = false; + + _FlushLog(); +} + +int BS_Log::_WriteLog(const char* Message) +{ + if (!_LogFile) if (!_CreateLog()) return false; + + std::vector<LOG_LISTENER_CALLBACK>::iterator Iter = _LogListener.begin(); + for (; Iter != _LogListener.end(); ++Iter) + (*Iter)(Message); + + fprintf(_LogFile, Message); + + return true; +} + +void BS_Log::_FlushLog() +{ + fflush(_LogFile); +} + +void (*BS_LogPtr)(const char *, ...) = BS_Log::Log; +extern "C" +{ + void BS_Log_C(const char* Message) + { + BS_LogPtr(Message); + } +} + +#else + +extern "C" +{ + void BS_Log_C(const char* Message) {}; +} + +#endif diff --git a/engines/sword25/kernel/log.h b/engines/sword25/kernel/log.h new file mode 100755 index 0000000000..867b19d70e --- /dev/null +++ b/engines/sword25/kernel/log.h @@ -0,0 +1,123 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_LOG_H +#define BS_LOG_H + +// Includes +#include "memlog_off.h" +#include <stdio.h> +#include <vector> +#include <string> +#include <algorithm> +#include "memlog_on.h" + +#include "common.h" + +// Logging soll nur stattfinden wenn es aktiviert ist +#ifdef BS_ACTIVATE_LOGGING + +// Logging-Makros +#define BS_LOG BS_Log::SetPrefix(BS_LOG_PREFIX ": "), BS_Log::LogDecorated +#define BS_LOGLN BS_Log::SetPrefix(BS_LOG_PREFIX ": "), BS_Log::SetAutoNewline(true), BS_Log::LogDecorated +#define BS_LOG_WARNING BS_Log::SetPrefix(BS_LOG_PREFIX ": WARNING - "), BS_Log::LogDecorated +#define BS_LOG_WARNINGLN BS_Log::SetPrefix(BS_LOG_PREFIX ": WARNING - "), BS_Log::SetAutoNewline(true), BS_Log::LogDecorated +#define BS_LOG_ERROR BS_Log::SetPrefix(BS_LOG_PREFIX ": ERROR - "), BS_Log::LogDecorated +#define BS_LOG_ERRORLN BS_Log::SetPrefix(BS_LOG_PREFIX ": ERROR - "), BS_Log::SetAutoNewline(true), BS_Log::LogDecorated +#define BS_LOG_EXTERROR BS_Log::SetPrefix(BS_LOG_PREFIX ": ERROR "), BS_Log::SetFile(__FILE__), BS_Log::SetLine(__LINE__), BS_Log::LogDecorated +#define BS_LOG_EXTERRORLN BS_Log::SetPrefix(BS_LOG_PREFIX ": ERROR "), BS_Log::SetFile(__FILE__), BS_Log::SetLine(__LINE__), BS_Log::SetAutoNewline(true), BS_Log::LogDecorated + +// Die Version der Logging-Klasse mit aktiviertem Logging +class BS_Log +{ +public: + static void Clear(); + static void Log(const char* Format, ...); + static void LogPrefix(const char* Prefix, const char* Format, ...); + static void LogDecorated(const char* Format, ...); + + static void SetPrefix(const char* Prefix) { _Prefix = Prefix; } + static void SetFile(const char* File) { _File = File; } + static void SetLine(int Line) { _Line = Line; } + static void SetAutoNewline(bool AutoNewline) { _AutoNewline = AutoNewline; } + + typedef void (*LOG_LISTENER_CALLBACK)(const char *); + static void RegisterLogListener(LOG_LISTENER_CALLBACK Callback) { _LogListener.push_back(Callback); } + static bool IsListenerRegistered(LOG_LISTENER_CALLBACK Callback) { return std::find(_LogListener.begin(), _LogListener.end(), Callback) != _LogListener.end(); } + +private: + static FILE* _LogFile; + static bool _LineBegin; + static const char* _Prefix; + static const char* _File; + static int _Line; + static bool _AutoNewline; + static std::vector<LOG_LISTENER_CALLBACK> _LogListener; + + static bool _CreateLog(); + static void _CloseLog(); + + static int _WriteLog(const char* Message); + static void _FlushLog(); +}; + +// Hilfsfunktion, die es C-Funktionen ermöglicht zu loggen (wird für Lua gebraucht). +extern "C" +{ + void BS_Log_C(const char* Message); +} + +#else + +// Logging-Makros +#define BS_LOG +#define BS_LOGLN +#define BS_LOG_WARNING +#define BS_LOG_WARNINGLN +#define BS_LOG_ERROR +#define BS_LOG_ERRORLN +#define BS_LOG_EXTERROR +#define BS_LOG_EXTERRORLN + +// Die Version der Logging-Klasse mit deaktiviertem Logging +class BS_Log +{ +public: + // Die Log Funktionen werden zu do-nothing Funktionen und wird daher vom Compiler (hoffentlich) rausoptimiert + static void Log(const char* Text, ...) {}; + static void LogPrefix(const char* Prefix, const char* Format, ...) {}; + static void LogDecorated(const char* Format, ...) {}; + + static void SetPrefix(const char* Prefix) {}; + static void SetFile(const char* File) {}; + static void SetLine(int Line) {}; + static void SetAutoNewline(bool AutoNewline) {}; + + typedef void (*LOG_LISTENER_CALLBACK)(const char *); + static void RegisterLogListener(LOG_LISTENER_CALLBACK Callback) {}; +}; + +extern "C" +{ + void BS_Log_C(const char* Message); +} + +#endif + +#endif diff --git a/engines/sword25/kernel/md5.cpp b/engines/sword25/kernel/md5.cpp new file mode 100755 index 0000000000..a336f3ab61 --- /dev/null +++ b/engines/sword25/kernel/md5.cpp @@ -0,0 +1,385 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +// The code in this file is based in part on code from Aladdin +// Enterprises released under the following terms: +// +// Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// L. Peter Deutsch +// ghost@aladdin.com + +#include "md5.h" + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} + +BS_MD5::BS_MD5() +{ + md5_init(&m_state); +} + +void BS_MD5::Update(const unsigned char * Buffer, unsigned int Length) +{ + md5_append(&m_state, Buffer, Length); +} + +void BS_MD5::GetDigest(unsigned char Digest[16]) +{ + md5_finish(&m_state, Digest); +} diff --git a/engines/sword25/kernel/md5.h b/engines/sword25/kernel/md5.h new file mode 100755 index 0000000000..d2ac60afc8 --- /dev/null +++ b/engines/sword25/kernel/md5.h @@ -0,0 +1,56 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_MD5_H +#define BS_MD5_H + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "kernel/common.h" +#include "kernel/bs_stdint.h" + +// ----------------------------------------------------------------------------- +// Klassendefinition +// ----------------------------------------------------------------------------- + +typedef uint8_t md5_byte_t; /* 8-bit byte */ +typedef uint32_t md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +class BS_MD5 +{ +public: + BS_MD5(); + + void Update(const unsigned char * Buffer, unsigned int Length); + void GetDigest(unsigned char Digest[16]); + +private: + md5_state_t m_state; +}; + +#endif diff --git a/engines/sword25/kernel/memleaks.cpp b/engines/sword25/kernel/memleaks.cpp new file mode 100755 index 0000000000..646465f5b2 --- /dev/null +++ b/engines/sword25/kernel/memleaks.cpp @@ -0,0 +1,130 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifdef BS_MEMLOG + +// Die folgende Zeile stellt sicher, dass alle Objekte in dieser Datei vor allen anderen erstellt und nach allen anderen +// zerstört werden. +// Damit wird sichergestellt, dass z.B. Singletons nicht fälschlicherweise als Memory-Leaks erkannt werden. +// TODO Visual C++ 8 kommt mit der aktuellen Implementation nicht klar und stürzt sowohl beim Start aus auch beim +// Beenden ab. Es muss eine Alternativimplementation her. An sichersten ist es wohl, wenn gar keine STL-Objekte benutzt +// werden. +// #pragma warning (disable : 4074) +// #pragma init_seg(compiler) + +#include "filesystemutil.h" + +#include "memlog_off.h" +#include <vector> +#include <algorithm> +#include <string> + +#include <stdio.h> +#include <string.h> + +typedef struct +{ + unsigned int address; + unsigned int size; + std::string file; + unsigned int line; +} ALLOC_INFO; + +static const char * MEMLEAK_LOG_FILE = "memory_leaks.txt"; +static const unsigned int BUCKET_COUNT = 1021; +std::vector< std::vector<ALLOC_INFO> > TrackData(BUCKET_COUNT); + +static unsigned int TotalSize = 0; + +// Diese Klasse stellt sicher, dass beim Programmende, das Memory-Leak Log geschrieben wird. +static class LeakDumper +{ +public: + LeakDumper() : OutputFilename(BS_FileSystemUtil::GetInstance().GetUserdataDirectory() + "\\" + MEMLEAK_LOG_FILE) + { + // Sicherstellen, dass das Ausgabeverzeichnis für die Datei existiert. + BS_FileSystemUtil::GetInstance().CreateDirectory(BS_FileSystemUtil::GetInstance().GetUserdataDirectory()); + } + + ~LeakDumper() + { + DumpUnfreed(OutputFilename.c_str()); + } + + std::string OutputFilename; +} LeakDumperInstance; + +void DumpUnfreed(const char * OutputFilename) +{ + FILE * Log = fopen(OutputFilename, "w"); + fputs("MEMORY LEAK REPORT:\n----------------------\n", Log); + std::vector< std::vector<ALLOC_INFO> >::iterator BucketIter = TrackData.begin(); + for (; BucketIter != TrackData.end(); ++BucketIter) + { + std::vector<ALLOC_INFO>::iterator Iter = (*BucketIter).begin(); + for (; Iter != (*BucketIter).end(); ++Iter) + { + ALLOC_INFO & CurItem = (*Iter); + fprintf(Log, "%-50s LINE:%d ADDRESS:0x%x SIZE:%d\n", + CurItem.file.c_str(), + CurItem.line, + CurItem.address, + CurItem.size); + } + } + + fprintf(Log, "----------------------\nTotal unfreed bytes: %d\n", TotalSize); + + fclose(Log); +} + +void AddTrack(unsigned int addr, unsigned int asize, const char *fname, unsigned int lnum) +{ + std::vector<ALLOC_INFO> & CurBucket = TrackData[(addr >> 3) % BUCKET_COUNT]; + ALLOC_INFO Info; + Info.address = addr; + Info.size = asize; + Info.file = fname; + Info.line = lnum; + CurBucket.push_back(Info); + + TotalSize += asize; +} + +void RemoveTrack(unsigned int addr) +{ + if (addr != 0 && TrackData.size() == BUCKET_COUNT) + { + std::vector<ALLOC_INFO> & CurBucket = TrackData[(addr >> 3) % BUCKET_COUNT]; + std::vector<ALLOC_INFO>::iterator Iter = CurBucket.begin(); + for (; Iter != CurBucket.end(); ++Iter) + { + if ((*Iter).address == addr) + { + TotalSize -= (*Iter).size; + + std::swap(*Iter, CurBucket.back()); + CurBucket.pop_back(); + return; + } + } + } +} + +#endif diff --git a/engines/sword25/kernel/memleaks.h b/engines/sword25/kernel/memleaks.h new file mode 100755 index 0000000000..c4193e3b74 --- /dev/null +++ b/engines/sword25/kernel/memleaks.h @@ -0,0 +1,60 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BF_MEMLEAKS_H +#define BF_MEMLEAKS_H + +#ifdef BS_MEMLOG + +#ifdef _MSC_VER +#pragma warning(disable : 4291) +#endif + +#include "memlog_off.h" + +#include <malloc.h> + +void DumpUnfreed(const char * OutputFilename); +void AddTrack(unsigned int addr, unsigned int asize, const char *fname, unsigned int lnum); +void RemoveTrack(unsigned int addr); + +inline void * __cdecl operator new(unsigned int size, const char *file, int line) +{ + void *ptr = malloc(size); + if (ptr) AddTrack((unsigned int)ptr, size, file, line); + return(ptr); +}; + +inline void __cdecl operator delete(void *p) +{ + RemoveTrack((unsigned int)p); + free(p); +}; + +inline void __cdecl operator delete[](void *p) +{ + RemoveTrack((unsigned int)p); + free(p); +}; + +#endif + +#include "memlog_on.h" + +#endif diff --git a/engines/sword25/kernel/memlog_off.h b/engines/sword25/kernel/memlog_off.h new file mode 100755 index 0000000000..81acac991f --- /dev/null +++ b/engines/sword25/kernel/memlog_off.h @@ -0,0 +1,27 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +// Deaktivieren der Memory-Leak Detektion + +#ifdef BS_MEMLOG + #ifdef new + #undef new + #undef DEBUG_NEW + #endif +#endif diff --git a/engines/sword25/kernel/memlog_on.h b/engines/sword25/kernel/memlog_on.h new file mode 100755 index 0000000000..7dbdd81330 --- /dev/null +++ b/engines/sword25/kernel/memlog_on.h @@ -0,0 +1,27 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +// Aktivieren der Memory-Leak Detektion + +#ifdef BS_MEMLOG + #ifndef DEBUG_NEW + #define DEBUG_NEW new(__FILE__, __LINE__) + #endif + #define new DEBUG_NEW +#endif diff --git a/engines/sword25/kernel/objectregistry.h b/engines/sword25/kernel/objectregistry.h new file mode 100755 index 0000000000..e1c80c8b36 --- /dev/null +++ b/engines/sword25/kernel/objectregistry.h @@ -0,0 +1,182 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_OBJECTREGISTRY_H +#define BS_OBJECTREGISTRY_H + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "kernel/common.h" +#include "kernel/hashmap.h" + +// ----------------------------------------------------------------------------- +// Klassendeklaration +// ----------------------------------------------------------------------------- + +template<typename T> +class BS_ObjectRegistry +{ +public: + BS_ObjectRegistry() : m_NextHandle(1) {}; + + // ------------------------------------------------------------------------- + + unsigned int RegisterObject(T * ObjectPtr) + { + // Null-Pointer können nicht registriert werden. + if (ObjectPtr == 0) + { + LogErrorLn("Cannot register a null pointer."); + return 0; + } + + // Falls das Objekt bereits registriert wurde, wird eine Warnung ausgeben und das Handle zurückgeben. + unsigned int Handle = FindHandleByPtr(ObjectPtr); + if (Handle != 0) + { + LogWarningLn("Tried to register a object that was already registered."); + return Handle; + } + // Ansonsten wird das Objekt in beide Maps eingetragen und das neue Handle zurückgeben. + else + { + m_Handle2PtrMap[m_NextHandle] = ObjectPtr; + m_Ptr2HandleMap[ObjectPtr] = m_NextHandle; + + return m_NextHandle++; + } + } + + // ----------------------------------------------------------------------------- + + unsigned int RegisterObject(T * ObjectPtr, unsigned int Handle) + { + // Null-Pointer und Null-Handle können nicht registriert werden. + if (ObjectPtr == 0 || Handle == 0) + { + LogErrorLn("Cannot register a null pointer or a null handle."); + return 0; + } + + // Falls das Objekt bereits registriert wurde, wird ein Fehler ausgegeben und 0 zurückgeben. + unsigned int HandleTest = FindHandleByPtr(ObjectPtr); + if (HandleTest != 0) + { + LogErrorLn("Tried to register a object that was already registered."); + return 0; + } + // Falls das Handle bereits vergeben ist, wird ein Fehler ausgegeben und 0 zurückgegeben. + else if (FindPtrByHandle(Handle) != 0) + { + LogErrorLn("Tried to register a handle that is already taken."); + return 0; + } + // Ansonsten wird das Objekt in beide Maps eingetragen und das gewünschte Handle zurückgeben. + else + { + m_Handle2PtrMap[Handle] = ObjectPtr; + m_Ptr2HandleMap[ObjectPtr] = Handle; + + // Falls das vergebene Handle größer oder gleich dem nächsten automatische vergebenen Handle ist, wird das nächste automatisch + // vergebene Handle erhöht. + if (Handle >= m_NextHandle) m_NextHandle = Handle + 1; + + return Handle; + } + } + + // ----------------------------------------------------------------------------- + + void DeregisterObject(T * ObjectPtr) + { + unsigned int Handle = FindHandleByPtr(ObjectPtr); + + if (Handle != 0) + { + // Registriertes Objekt aus beiden Maps entfernen. + m_Handle2PtrMap.erase(FindHandleByPtr(ObjectPtr)); + m_Ptr2HandleMap.erase(ObjectPtr); + } + else + { + LogWarningLn("Tried to remove a object that was not registered."); + } + } + + // ----------------------------------------------------------------------------- + + T * ResolveHandle(unsigned int Handle) + { + // Zum Handle gehöriges Objekt in der Hash-Map finden. + T * ObjectPtr = FindPtrByHandle(Handle); + + // Pointer zurückgeben. Im Fehlerfall ist dieser 0. + return ObjectPtr; + } + + // ----------------------------------------------------------------------------- + + unsigned int ResolvePtr(T * ObjectPtr) + { + // Zum Pointer gehöriges Handle in der Hash-Map finden. + unsigned int Handle = FindHandleByPtr(ObjectPtr); + + // Handle zurückgeben. Im Fehlerfall ist dieses 0. + return Handle; + } + +protected: + typedef BS_Hashmap<unsigned int, T *> HANDLE2PTR_MAP; + typedef BS_Hashmap<T *, unsigned int> PTR2HANDLE_MAP; + + HANDLE2PTR_MAP m_Handle2PtrMap; + PTR2HANDLE_MAP m_Ptr2HandleMap; + unsigned int m_NextHandle; + + // ----------------------------------------------------------------------------- + + T * FindPtrByHandle(unsigned int Handle) + { + // Zum Handle gehörigen Pointer finden. + HANDLE2PTR_MAP::const_iterator it = m_Handle2PtrMap.find(Handle); + + // Pointer zurückgeben, oder, falls keiner gefunden wurde, 0 zurückgeben. + return (it != m_Handle2PtrMap.end()) ? it->second : 0; + } + + // ----------------------------------------------------------------------------- + + unsigned int FindHandleByPtr(T * ObjectPtr) + { + // Zum Pointer gehöriges Handle finden. + PTR2HANDLE_MAP::const_iterator it = m_Ptr2HandleMap.find(ObjectPtr); + + // Handle zurückgeben, oder, falls keines gefunden wurde, 0 zurückgeben. + return (it != m_Ptr2HandleMap.end()) ? it->second : 0; + } + + // ----------------------------------------------------------------------------- + + virtual void LogErrorLn(const char * Message) const = 0; + virtual void LogWarningLn(const char * Message) const = 0; +}; + +#endif diff --git a/engines/sword25/kernel/outputpersistenceblock.cpp b/engines/sword25/kernel/outputpersistenceblock.cpp new file mode 100755 index 0000000000..37410caaec --- /dev/null +++ b/engines/sword25/kernel/outputpersistenceblock.cpp @@ -0,0 +1,123 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#define BS_LOG_PREFIX "OUTPUTPERSISTENCEBLOCK" + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "outputpersistenceblock.h" + +// ----------------------------------------------------------------------------- +// Constants +// ----------------------------------------------------------------------------- + +namespace +{ + const unsigned int INITIAL_BUFFER_SIZE = 1024 * 64; +} + +// ----------------------------------------------------------------------------- +// Construction / Destruction +// ----------------------------------------------------------------------------- + +BS_OutputPersistenceBlock::BS_OutputPersistenceBlock() +{ + m_Data.reserve(INITIAL_BUFFER_SIZE); +} + +// ----------------------------------------------------------------------------- +// Writing +// ----------------------------------------------------------------------------- + +void BS_OutputPersistenceBlock::Write(signed int Value) +{ + WriteMarker(SINT_MARKER); + Value = ConvertEndianessFromSystemToStorage(Value); + RawWrite(&Value, sizeof(Value)); +} + +// ----------------------------------------------------------------------------- + +void BS_OutputPersistenceBlock::Write(unsigned int Value) +{ + WriteMarker(UINT_MARKER); + Value = ConvertEndianessFromSystemToStorage(Value); + RawWrite(&Value, sizeof(Value)); +} + +// ----------------------------------------------------------------------------- + +void BS_OutputPersistenceBlock::Write(float Value) +{ + WriteMarker(FLOAT_MARKER); + Value = ConvertEndianessFromSystemToStorage(Value); + RawWrite(&Value, sizeof(Value)); +} + +// ----------------------------------------------------------------------------- + +void BS_OutputPersistenceBlock::Write(bool Value) +{ + WriteMarker(BOOL_MARKER); + + unsigned int UIntBool = Value ? 1 : 0; + UIntBool = ConvertEndianessFromSystemToStorage(UIntBool); + RawWrite(&UIntBool, sizeof(UIntBool)); +} + +// ----------------------------------------------------------------------------- + +void BS_OutputPersistenceBlock::Write(const std::string & String) +{ + WriteMarker(STRING_MARKER); + + Write(String.size()); + RawWrite(String.c_str(), String.size()); +} + +// ----------------------------------------------------------------------------- + +void BS_OutputPersistenceBlock::Write(const void * BufferPtr, size_t Size) +{ + WriteMarker(BLOCK_MARKER); + + Write(Size); + RawWrite(BufferPtr, Size); +} + +// ----------------------------------------------------------------------------- + +void BS_OutputPersistenceBlock::WriteMarker(unsigned char Marker) +{ + m_Data.push_back(Marker); +} + +// ----------------------------------------------------------------------------- + +void BS_OutputPersistenceBlock::RawWrite(const void * DataPtr, size_t Size) +{ + if (Size > 0) + { + unsigned int OldSize = m_Data.size(); + m_Data.resize(OldSize + Size); + memcpy(&m_Data[OldSize], DataPtr, Size); + } +} diff --git a/engines/sword25/kernel/outputpersistenceblock.h b/engines/sword25/kernel/outputpersistenceblock.h new file mode 100755 index 0000000000..1f730769b8 --- /dev/null +++ b/engines/sword25/kernel/outputpersistenceblock.h @@ -0,0 +1,60 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_OUTPUTPERSISTENCEBLOCK_H +#define BS_OUTPUTPERSISTENCEBLOCK_H + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "kernel/common.h" +#include "kernel/persistenceblock.h" +#include "kernel/memlog_off.h" +#include <vector> +#include "kernel/memlog_on.h" + + +// ----------------------------------------------------------------------------- +// Class declaration +// ----------------------------------------------------------------------------- + +class BS_OutputPersistenceBlock : public BS_PersistenceBlock +{ +public: + BS_OutputPersistenceBlock(); + + void Write(signed int Value); + void Write(unsigned int Value); + void Write(float Value); + void Write(bool Value); + void Write(const std::string & String); + void Write(const void * BufferPtr, size_t Size); + + const void * GetData() const { return &m_Data[0]; } + unsigned int GetDataSize() const { return m_Data.size(); } + +private: + void WriteMarker(unsigned char Marker); + void RawWrite(const void * DataPtr, size_t Size); + + std::vector<unsigned char> m_Data; +}; + +#endif diff --git a/engines/sword25/kernel/persistable.h b/engines/sword25/kernel/persistable.h new file mode 100755 index 0000000000..b1b8bd2c12 --- /dev/null +++ b/engines/sword25/kernel/persistable.h @@ -0,0 +1,36 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_PERSISTABLE_H +#define BS_PERSISTABLE_H + +class BS_OutputPersistenceBlock; +class BS_InputPersistenceBlock; + +class BS_Persistable +{ +public: + virtual ~BS_Persistable() {}; + + virtual bool Persist(BS_OutputPersistenceBlock & Writer) = 0; + virtual bool Unpersist(BS_InputPersistenceBlock & Reader) = 0; + +}; + +#endif diff --git a/engines/sword25/kernel/persistenceblock.h b/engines/sword25/kernel/persistenceblock.h new file mode 100755 index 0000000000..4dda4388b7 --- /dev/null +++ b/engines/sword25/kernel/persistenceblock.h @@ -0,0 +1,112 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_PERSISTENCEBLOCK_H +#define BS_PERSISTENCEBLOCK_H + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "kernel/common.h" + + +// ----------------------------------------------------------------------------- +// Class definition +// ----------------------------------------------------------------------------- + +class BS_PersistenceBlock +{ +public: + static unsigned int GetSInt32Size() { return sizeof(signed int) + sizeof(unsigned char); } + static unsigned int GetUInt32Size() { return sizeof(unsigned int) + sizeof(unsigned char); } + static unsigned int GetFloat32Size() { return sizeof(float) + sizeof(unsigned char); } + static unsigned int GetBoolSize() { return sizeof(unsigned char) + sizeof(unsigned char); } + static unsigned int GetStringSize(const std::string & String) { return static_cast<unsigned int>(sizeof(unsigned int) + String.size() + sizeof(unsigned char)); } + +protected: + enum + { + SINT_MARKER, + UINT_MARKER, + FLOAT_MARKER, + STRING_MARKER, + BOOL_MARKER, + BLOCK_MARKER, + }; + + // ----------------------------------------------------------------------------- + // Endianess Conversions + // ----------------------------------------------------------------------------- + // + // Alles wird in Little Endian gespeichert. + // Auf Big Endian-Systemen muss die Bytereihenfolge daher vor dem Speichern und nach dem Einlesen gespeicherter Werte vertauscht werden. + // + + template<typename T> + static T ConvertEndianessFromSystemToStorage(T Value) + { + if (IsBigEndian()) ReverseByteOrder(&Value); + return Value; + } + + template<typename T> + static T ConvertEndianessFromStorageToSystem(T Value) + { + if (IsBigEndian()) ReverseByteOrder(&Value); + return Value; + } + +private: + static bool IsBigEndian() + { + unsigned int Dummy = 1; + unsigned char * DummyPtr = reinterpret_cast<unsigned char *>(&Dummy); + return DummyPtr[0] == 0; + } + + template<typename T> + static void Swap(T & One, T & Two) + { + T Temp = One; + One = Two; + Two = Temp; + } + + static void ReverseByteOrder(void * Ptr) + { + // Kehrt die Bytereihenfolge des 32-Bit Wortes um auf das Ptr zeigt. + unsigned char * CharPtr = static_cast<unsigned char *>(Ptr); + Swap(CharPtr[0], CharPtr[3]); + Swap(CharPtr[1], CharPtr[2]); + } +}; + +// ----------------------------------------------------------------------------- +// Compile time asserts +// ----------------------------------------------------------------------------- + +#define CTASSERT(ex) typedef char ctassert_type[(ex) ? 1 : -1]; +CTASSERT(sizeof(unsigned char) == 1); +CTASSERT(sizeof(signed int) == 4); +CTASSERT(sizeof(unsigned int) == 4); +CTASSERT(sizeof(float) == 4); +#undef CTASSERT + +#endif diff --git a/engines/sword25/kernel/persistenceservice.cpp b/engines/sword25/kernel/persistenceservice.cpp new file mode 100755 index 0000000000..51491a6c2b --- /dev/null +++ b/engines/sword25/kernel/persistenceservice.cpp @@ -0,0 +1,459 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "kernel.h" +#include "persistenceservice.h" +#include "inputpersistenceblock.h" +#include "outputpersistenceblock.h" +#include "filesystemutil.h" +#include "gfx/graphicengine.h" +#include "sfx/soundengine.h" +#include "input/inputengine.h" +#include "math/regionregistry.h" +#include "script/script.h" +#include "debug/debugtools.h" +#include "util/zlib/zlib.h" + +#include "kernel/memlog_off.h" +#include <sstream> +#include <fstream> +#include <algorithm> +#include <locale> +#include "kernel/memlog_on.h" + +using namespace std; + +#define BS_LOG_PREFIX "PERSISTENCESERVICE" + +// ----------------------------------------------------------------------------- +// Konstanten und Hilfsfunktionen +// ----------------------------------------------------------------------------- + +namespace +{ + const char * SAVEGAME_EXTENSION = ".b25s"; + const char * SAVEGAME_DIRECTORY = "saves"; + const char * FILE_MARKER = "BS25SAVEGAME"; + const unsigned int SLOT_COUNT = 18; + const unsigned int FILE_COPY_BUFFER_SIZE = 1024 * 10; + + // ------------------------------------------------------------------------- + + string GenerateSavegameFilename(unsigned int SlotID) + { + ostringstream oss; + oss << SlotID << SAVEGAME_EXTENSION; + return oss.str(); + } + + // ------------------------------------------------------------------------- + + string GenerateSavegamePath(unsigned int SlotID) + { + ostringstream oss; + oss << BS_PersistenceService::GetSavegameDirectory() << BS_FileSystemUtil::GetInstance().GetPathSeparator() << GenerateSavegameFilename(SlotID); + return oss.str(); + } + + // ------------------------------------------------------------------------- + + string FormatTimestamp(time_t Time) + { + // Zeitstempel in Einzelkomponenten auflösen. + tm * Timeinfo = localtime(&Time); + + // Zeitangabe im lokalen Format in einen String-Stream schreiben. + locale Locale(""); + ostringstream StringBuilder; + StringBuilder.imbue(Locale); + char * Pattern = "%x %X"; + use_facet<time_put<char> >(Locale).put(StringBuilder, + StringBuilder, StringBuilder.fill(), Timeinfo, Pattern, Pattern + strlen(Pattern)); + + // Formatierten String zurückgeben. + return StringBuilder.str(); + } +} + +// ----------------------------------------------------------------------------- +// Private Implementation (Pimpl-Pattern) +// ----------------------------------------------------------------------------- + +struct SavegameInformation +{ + bool IsOccupied; + bool IsCompatible; + string Description; + string Filename; + unsigned int GamedataLength; + unsigned int GamedataOffset; + unsigned int GamedataUncompressedLength; + + SavegameInformation() { Clear(); } + + void Clear() + { + IsOccupied = false; + IsCompatible = false; + Description = ""; + Filename = ""; + GamedataLength = 0; + GamedataOffset = 0; + GamedataUncompressedLength = 0; + } +}; + +struct BS_PersistenceService::Impl +{ + SavegameInformation m_SavegameInformations[SLOT_COUNT]; + + // ----------------------------------------------------------------------------- + + Impl() + { + ReloadSlots(); + } + + // ----------------------------------------------------------------------------- + + void ReloadSlots() + { + // Über alle Spielstanddateien iterieren und deren Infos einlesen. + for (unsigned int i = 0; i < SLOT_COUNT; ++i) + { + ReadSlotSavegameInformation(i); + } + } + + void ReadSlotSavegameInformation(unsigned int SlotID) + { + // Aktuelle Slotinformationen in den Ausgangszustand versetzen, er wird im Folgenden neu gefüllt. + SavegameInformation & CurSavegameInfo = m_SavegameInformations[SlotID]; + CurSavegameInfo.Clear(); + + // Den Dateinamen für den Spielstand des Slots generieren. + string Filename = GenerateSavegamePath(SlotID); + + // Feststellen, ob eine Spielstanddatei dieses Namens existiert. + if (BS_FileSystemUtil::GetInstance().FileExists(Filename)) + { + // Die Spielstanddatei öffnen. + ifstream File(Filename.c_str(), ifstream::binary); + if (File.good() && File.is_open()) + { + // Die Headerdaten einlesen. + string StoredMarker, StoredVersionID; + File >> StoredMarker >> StoredVersionID >> CurSavegameInfo.GamedataLength >> CurSavegameInfo.GamedataUncompressedLength; + + // Falls die Headerdaten gelesen werden konnten und der Marker stimmt, nehmen wir an eine gültige Spielstanddatei zu haben. + if (File.good() && StoredMarker == FILE_MARKER) + { + // Der Slot wird als belegt markiert. + CurSavegameInfo.IsOccupied = true; + // Speichern, ob der Spielstand kompatibel mit der aktuellen Engine-Version ist. + CurSavegameInfo.IsCompatible = (StoredVersionID == BS_Debugtools::GetVersionID()); + // Dateinamen des Spielstandes speichern. + CurSavegameInfo.Filename = GenerateSavegameFilename(SlotID); + // Die Beschreibung des Spielstandes besteht aus einer textuellen Darstellung des Änderungsdatums der Spielstanddatei. + CurSavegameInfo.Description = FormatTimestamp(BS_FileSystemUtil::GetInstance().GetFileTime(Filename)); + // Den Offset zu den gespeicherten Spieldaten innerhalb der Datei speichern. + // Dieses entspricht der aktuellen Position + 1, da nach der letzten Headerinformation noch ein Leerzeichen als trenner folgt. + CurSavegameInfo.GamedataOffset = static_cast<unsigned int>(File.tellg()) + 1; + } + + } + } + } +}; + +// ----------------------------------------------------------------------------- +// Construction / Destruction +// ----------------------------------------------------------------------------- + +BS_PersistenceService & BS_PersistenceService::GetInstance() +{ + static BS_PersistenceService Instance; + return Instance; +} + +// ----------------------------------------------------------------------------- + +BS_PersistenceService::BS_PersistenceService() : m_impl(new Impl) +{ +} + +// ----------------------------------------------------------------------------- + +BS_PersistenceService::~BS_PersistenceService() +{ + delete m_impl; +} + +// ----------------------------------------------------------------------------- +// Implementation +// ----------------------------------------------------------------------------- + +void BS_PersistenceService::ReloadSlots() +{ + m_impl->ReloadSlots(); +} + +// ----------------------------------------------------------------------------- + +unsigned int BS_PersistenceService::GetSlotCount() +{ + return SLOT_COUNT; +} + +// ----------------------------------------------------------------------------- + +std::string BS_PersistenceService::GetSavegameDirectory() +{ + return BS_FileSystemUtil::GetInstance().GetUserdataDirectory() + BS_FileSystemUtil::GetInstance().GetPathSeparator() + SAVEGAME_DIRECTORY; +} + +// ----------------------------------------------------------------------------- + +namespace +{ + bool CheckSlotID(unsigned int SlotID) + { + // Überprüfen, ob die Slot-ID zulässig ist. + if (SlotID >= SLOT_COUNT) + { + BS_LOG_ERRORLN("Tried to access an invalid slot (%d). Only slot ids from 0 to %d are allowed.", SlotID, SLOT_COUNT - 1); + return false; + } + else + { + return true; + } + } +} + +// ----------------------------------------------------------------------------- + +bool BS_PersistenceService::IsSlotOccupied(unsigned int SlotID) +{ + if (!CheckSlotID(SlotID)) return false; + return m_impl->m_SavegameInformations[SlotID].IsOccupied; +} + +// ----------------------------------------------------------------------------- + +bool BS_PersistenceService::IsSavegameCompatible(unsigned int SlotID) +{ + if (!CheckSlotID(SlotID)) return false; + return m_impl->m_SavegameInformations[SlotID].IsCompatible; +} + +// ----------------------------------------------------------------------------- + +string & BS_PersistenceService::GetSavegameDescription(unsigned int SlotID) +{ + static string EmptyString; + if (!CheckSlotID(SlotID)) return EmptyString; + return m_impl->m_SavegameInformations[SlotID].Description; +} + +// ----------------------------------------------------------------------------- + +string & BS_PersistenceService::GetSavegameFilename(unsigned int SlotID) +{ + static string EmptyString; + if (!CheckSlotID(SlotID)) return EmptyString; + return m_impl->m_SavegameInformations[SlotID].Filename; +} + +// ----------------------------------------------------------------------------- + +bool BS_PersistenceService::SaveGame(unsigned int SlotID, const std::string & ScreenshotFilename) +{ + // Überprüfen, ob die Slot-ID zulässig ist. + if (SlotID >= SLOT_COUNT) + { + BS_LOG_ERRORLN("Tried to save to an invalid slot (%d). Only slot ids form 0 to %d are allowed.", SlotID, SLOT_COUNT - 1); + return false; + } + + // Dateinamen erzeugen. + string Filename = GenerateSavegamePath(SlotID).c_str(); + + try + { + // Sicherstellen, dass das Verzeichnis für die Spielstanddateien existiert. + BS_FileSystemUtil::GetInstance().CreateDirectory(GetSavegameDirectory()); + + // Spielstanddatei öffnen und die Headerdaten schreiben. + ofstream File(Filename.c_str(), ofstream::binary); + File << string(FILE_MARKER) << " " << string(BS_Debugtools::GetVersionID()) << " "; + if (!File.good()) + { + BS_LOG_ERRORLN("Unable to write header data to savegame file \"%s\".", Filename.c_str()); + throw 0; + } + + // Alle notwendigen Module persistieren. + BS_OutputPersistenceBlock Writer; + bool Success = true; + Success &= BS_Kernel::GetInstance()->GetScript()->Persist(Writer); + Success &= BS_RegionRegistry::GetInstance().Persist(Writer); + Success &= BS_Kernel::GetInstance()->GetGfx()->Persist(Writer); + Success &= BS_Kernel::GetInstance()->GetSfx()->Persist(Writer); + Success &= BS_Kernel::GetInstance()->GetInput()->Persist(Writer); + if (!Success) + { + BS_LOG_ERRORLN("Unable to persist modules for savegame file \"%s\".", Filename.c_str()); + throw 0; + } + + // Daten komprimieren. + vector<unsigned char> CompressionBuffer(Writer.GetDataSize() + (Writer.GetDataSize() + 500) / 1000 + 12); + uLongf CompressedLength = CompressionBuffer.size(); + if (compress2(&CompressionBuffer[0], &CompressedLength, reinterpret_cast<const Bytef *>(Writer.GetData()), Writer.GetDataSize(), 6) != Z_OK) + { + BS_LOG_ERRORLN("Unable to compress savegame data in savegame file \"%s\".", Filename.c_str()); + throw 0; + } + + // Länge der komprimierten Daten und der unkomprimierten Daten in die Datei schreiben. + File << CompressedLength << " " << Writer.GetDataSize() << " "; + + // Komprimierte Daten in die Datei schreiben. + File.write(reinterpret_cast<char *>(&CompressionBuffer[0]), CompressedLength); + if (!File.good()) + { + BS_LOG_ERRORLN("Unable to write game data to savegame file \"%s\".", Filename.c_str()); + throw 0; + } + + // Screenshotdatei an die Datei anfügen. + if (BS_FileSystemUtil::GetInstance().FileExists(ScreenshotFilename)) + { + ifstream ScreenshotFile(ScreenshotFilename.c_str(), ifstream::binary); + + vector<char> Buffer(FILE_COPY_BUFFER_SIZE); + while (ScreenshotFile.good()) + { + ScreenshotFile.read(&Buffer[0], Buffer.size()); + File.write(&Buffer[0], ScreenshotFile.gcount()); + } + } + else + { + BS_LOG_WARNINGLN("The screenshot file \"%s\" does not exist. Savegame is written without a screenshot.", Filename.c_str()); + } + + // Savegameinformationen für diesen Slot aktualisieren. + m_impl->ReadSlotSavegameInformation(SlotID); + } + catch(...) + { + BS_LOG_ERRORLN("An error occured while create savegame file \"%s\".", Filename.c_str()); + + // Es ist ein Fehler aufgetreten, die Spielstanddatei wird gelöscht, da sie keinen konsistenten Zustand besitzt. + ::remove(Filename.c_str()); + + // Misserfolg signalisieren. + return false; + } + + // Erfolg signalisieren. + return true; +} + +// ----------------------------------------------------------------------------- + +bool BS_PersistenceService::LoadGame(unsigned int SlotID) +{ + // Überprüfen, ob die Slot-ID zulässig ist. + if (SlotID >= SLOT_COUNT) + { + BS_LOG_ERRORLN("Tried to load from an invalid slot (%d). Only slot ids form 0 to %d are allowed.", SlotID, SLOT_COUNT - 1); + return false; + } + + SavegameInformation & CurSavegameInfo = m_impl->m_SavegameInformations[SlotID]; + + // Überprüfen, ob der Slot belegt ist. + if (!CurSavegameInfo.IsOccupied) + { + BS_LOG_ERRORLN("Tried to load from an empty slot (%d).", SlotID); + return false; + } + + // Überprüfen, ob der Spielstand im angegebenen Slot mit der aktuellen Engine-Version kompatibel ist. + // Im Debug-Modus wird dieser Test übersprungen. Für das Testen ist es hinderlich auf die Einhaltung dieser strengen Bedingung zu bestehen, + // da sich die Versions-ID bei jeder Codeänderung mitändert. +#ifndef DEBUG + if (!CurSavegameInfo.IsCompatible) + { + BS_LOG_ERRORLN("Tried to load a savegame (%d) that is not compatible with this engine version.", SlotID); + return false; + } +#endif + + vector<unsigned char> UncompressedDataBuffer(CurSavegameInfo.GamedataUncompressedLength); + { + // Komprimierte gespeicherte Spieldaten laden. + vector<unsigned char> CompressedDataBuffer(CurSavegameInfo.GamedataLength); + { + ifstream File(GenerateSavegamePath(SlotID).c_str(), ifstream::binary); + File.seekg(CurSavegameInfo.GamedataOffset); + File.read(reinterpret_cast<char *>(&CompressedDataBuffer[0]), CurSavegameInfo.GamedataLength); + if (!File.good()) + { + BS_LOG_ERRORLN("Unable to load the gamedata from the savegame file \"%s\".", CurSavegameInfo.Filename.c_str()); + return false; + } + } + + // Spieldaten dekomprimieren. + uLongf UncompressedBufferSize = UncompressedDataBuffer.size(); + if (uncompress(reinterpret_cast<Bytef *>(&UncompressedDataBuffer[0]), &UncompressedBufferSize, + reinterpret_cast<Bytef *>(&CompressedDataBuffer[0]), CompressedDataBuffer.size()) != Z_OK) + { + BS_LOG_ERRORLN("Unable to decompress the gamedata from savegame file \"%s\".", CurSavegameInfo.Filename.c_str()); + return false; + } + } + + BS_InputPersistenceBlock Reader(&UncompressedDataBuffer[0], UncompressedDataBuffer.size()); + + // Einzelne Engine-Module depersistieren. + bool Success = true; + Success &= BS_Kernel::GetInstance()->GetScript()->Unpersist(Reader); + // Muss unbedingt nach Script passieren. Da sonst die bereits wiederhergestellten Regions per Garbage-Collection gekillt werden. + Success &= BS_RegionRegistry::GetInstance().Unpersist(Reader); + Success &= BS_Kernel::GetInstance()->GetGfx()->Unpersist(Reader); + Success &= BS_Kernel::GetInstance()->GetSfx()->Unpersist(Reader); + Success &= BS_Kernel::GetInstance()->GetInput()->Unpersist(Reader); + + if (!Success) + { + BS_LOG_ERRORLN("Unable to unpersist the gamedata from savegame file \"%s\".", CurSavegameInfo.Filename.c_str()); + return false; + } + + return true; +} diff --git a/engines/sword25/kernel/persistenceservice.h b/engines/sword25/kernel/persistenceservice.h new file mode 100755 index 0000000000..2693f4cc2e --- /dev/null +++ b/engines/sword25/kernel/persistenceservice.h @@ -0,0 +1,71 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_PERSISTENCESERVICE_H +#define BS_PERSISTENCESERVICE_H + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "kernel/common.h" +#include "kernel/memlog_off.h" +#include <string> +#include <vector> +#include "kernel/memlog_on.h" + +// ----------------------------------------------------------------------------- +// Class declaration +// ----------------------------------------------------------------------------- + +class BS_PersistenceService +{ +public: + BS_PersistenceService(); + virtual ~BS_PersistenceService(); + + // ----------------------------------------------------------------------------- + // Singleton-Methode + // ----------------------------------------------------------------------------- + + static BS_PersistenceService & GetInstance(); + + + // ----------------------------------------------------------------------------- + // Interface + // ----------------------------------------------------------------------------- + + static unsigned int GetSlotCount(); + static std::string GetSavegameDirectory(); + + void ReloadSlots(); + bool IsSlotOccupied(unsigned int SlotID); + bool IsSavegameCompatible(unsigned int SlotID); + std::string & GetSavegameDescription(unsigned int SlotID); + std::string & GetSavegameFilename(unsigned int SlotID); + + bool SaveGame(unsigned int SlotID, const std::string & ScreenshotFilename); + bool LoadGame(unsigned int SlotID); + +private: + struct Impl; + Impl * m_impl; +}; + +#endif diff --git a/engines/sword25/kernel/resmanager.cpp b/engines/sword25/kernel/resmanager.cpp new file mode 100755 index 0000000000..2e7ab87af0 --- /dev/null +++ b/engines/sword25/kernel/resmanager.cpp @@ -0,0 +1,293 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#include "resmanager.h" + +#include "resource.h" +#include "resservice.h" +#include "string.h" +#include "../package/packagemanager.h" + +#define BS_LOG_PREFIX "RESOURCEMANAGER" + +BS_ResourceManager::~BS_ResourceManager() +{ + // Alle ungelockten Resourcen freigeben. + EmptyCache(); + + // Alle übriggebliebenen Resourcen sind nicht freigegeben worden, daher Warnungen ausgeben und freigeben. + std::list<BS_Resource*>::iterator Iter = m_Resources.begin(); + for (; Iter != m_Resources.end(); ++Iter) + { + BS_LOG_WARNINGLN("Resource \"%s\" was not released.", (*Iter)->GetFileName().c_str()); + + // Lock-Count auf 0 setzen. + while ((*Iter)->GetLockCount() > 0) { (*Iter)->Release(); }; + + // Resource freigeben. + delete(*Iter); + } +} + +BS_Resource* BS_ResourceManager::GetResourceByOrdinal(int Ord) const +{ + // Überprüfen ob der Index Ord innerhald der Listengrenzen liegt. + if (Ord < 0 || Ord >= GetResourceCount()) + { + BS_LOG_ERRORLN("Resource ordinal (%d) out of bounds (0 - %d).", Ord, GetResourceCount() - 1); + return NULL; + } + + // Liste durchlaufen und die Resource mit dem gewünschten Index zurückgeben. + int CurOrd = 0; + std::list<BS_Resource*>::const_iterator Iter = m_Resources.begin(); + for (; Iter != m_Resources.end(); ++Iter, ++CurOrd) + { + if (CurOrd == Ord) + return (*Iter); + } + + // Die Ausführung sollte nie an diesem Punkt ankommen. + BS_LOG_EXTERRORLN("Execution reached unexpected point."); + return NULL; +} + +bool BS_ResourceManager::RegisterResourceService(BS_ResourceService* pService) +{ + if(!pService) + { + BS_LOG_ERRORLN("Can't register NULL resource service."); + return false; + } + + m_ResourceServices.push_back(pService); + + return true; +} + +void BS_ResourceManager::DeleteResourcesIfNecessary() +{ + // Falls noch genügend Speicher frei ist, oder keine Ressourcen geladen sind, kann die Funktion vorzeitig beendet werden. + if (m_KernelPtr->GetUsedMemory() < m_MaxMemoryUsage || m_Resources.empty()) return; + + // Solange Ressourcen löschen, bis der Speichernutzung des Prozesses unter den festgelegten Maximalwert fällt. + // Dabei wird die Liste von Hinten nach vorne durchlaufen um zunächst jene Resourcen freizugeben, deren + // Benutzung lange zurückliegt und sich somit am Ende der Liste befinden. + std::list<BS_Resource*>::iterator Iter = m_Resources.end(); + do + { + --Iter; + + // Die Resource darf nur freigegeben werden, wenn sie nicht gelockt ist. + if ((*Iter)->GetLockCount() == 0) Iter = DeleteResource(*Iter); + } while(Iter != m_Resources.begin() && m_KernelPtr->GetUsedMemory() > m_MaxMemoryUsage); +} + +void BS_ResourceManager::EmptyCache() +{ + // Resourcenliste durchlaufen und alle nicht gelockten Resourcen freigeben + std::list<BS_Resource*>::iterator Iter = m_Resources.begin(); + while (Iter != m_Resources.end()) + { + if ((*Iter)->GetLockCount() == 0) + { + // Resource entfernen + Iter = DeleteResource(*Iter); + } + else + ++Iter; + } +} + +BS_Resource* BS_ResourceManager::RequestResource(const std::string& FileName) +{ + // Absoluten, eindeutigen Pfad zur Datei erzeugen. + std::string UniqueFileName = GetUniqueFileName(FileName); + if (UniqueFileName == "") + return NULL; + + // Feststellen, ob die Resource schon geladen ist. + // Wenn die Resource gefunden wurde wird sie an die Spitze der Resourcenliste gestellt, gelockt und zurückgegeben. + { + BS_Resource* pResource = GetResource(UniqueFileName); + if (pResource) + { + MoveToFront(pResource); + (pResource)->AddReference(); + return pResource; + } + } + + // Die Resource wurde nicht gefunden, muss also noch geladen werden. + if (m_LogCacheMiss) BS_LOG_WARNINGLN("\"%s\" was not precached.", UniqueFileName.c_str()); + + BS_Resource* pResource; + if (pResource = LoadResource(UniqueFileName)) + { + pResource->AddReference(); + return pResource; + } + + return NULL; +} + +bool BS_ResourceManager::PrecacheResource(const std::string& FileName, bool ForceReload) +{ + // Absoluten, eindeutigen Pfad zur Datei erzeugen. + std::string UniqueFileName = GetUniqueFileName(FileName); + if (UniqueFileName == "") + return false; + + BS_Resource * ResourcePtr = GetResource(UniqueFileName); + + if (ForceReload && ResourcePtr) + { + if (ResourcePtr->GetLockCount()) + { + BS_LOG_ERRORLN("Could not force precaching of \"%s\". The resource is locked.", FileName.c_str()); + return false; + } + else + { + DeleteResource(ResourcePtr); + ResourcePtr = 0; + } + } + + if (!ResourcePtr && LoadResource(UniqueFileName) == NULL) + { + BS_LOG_ERRORLN("Could not precache \"%s\",", FileName.c_str()); + return false; + } + + return true; +} + +void BS_ResourceManager::MoveToFront(BS_Resource* pResource) +{ + // Resource aus der Liste löschen + m_Resources.erase(pResource->_Iterator); + // Resource an die Spitze der Liste setzen + m_Resources.push_front(pResource); + // Iterator aktualisieren + pResource->_Iterator = m_Resources.begin(); +} + +BS_Resource* BS_ResourceManager::LoadResource(const std::string& FileName) +{ + // ResourceService finden, der die Resource laden kann. + for(unsigned int i = 0; i < m_ResourceServices.size(); ++i) + { + if (m_ResourceServices[i]->CanLoadResource(FileName)) + { + // Falls mehr Speicher belegt ist als gewünscht, muss Speicher freigegeben werden. + DeleteResourcesIfNecessary(); + + // Resource laden + BS_Resource* pResource; + if (!(pResource = m_ResourceServices[i]->LoadResource(FileName))) + { + BS_LOG_ERRORLN("Responsible service could not load resource \"%s\".", FileName.c_str()); + return NULL; + } + + // Resource an die Spitze der Resourcenliste stellen. + m_Resources.push_front(pResource); + pResource->_Iterator = m_Resources.begin(); + + // Resource in die Hashtabelle eintragen + m_ResourceHashTable[pResource->GetFileNameHash() % HASH_TABLE_BUCKETS].push_front(pResource); + + return pResource; + } + } + + BS_LOG_ERRORLN("Could not find a service that can load \"%s\".", FileName.c_str()); + return NULL; +} + +std::string BS_ResourceManager::GetUniqueFileName(const std::string& FileName) const +{ + // Pointer auf den PackageManager holen + BS_PackageManager* pPackage = (BS_PackageManager*) m_KernelPtr->GetService("package"); + if (!pPackage) + { + BS_LOG_ERRORLN("Could not get package manager."); + return std::string(""); + } + + // Absoluten Pfad der Datei bekommen und somit die Eindeutigkeit des Dateinamens sicherstellen + std::string UniqueFileName = pPackage->GetAbsolutePath(FileName); + if (UniqueFileName == "") + BS_LOG_ERRORLN("Could not create absolute file name for \"%s\".", FileName.c_str()); + + return UniqueFileName; +} + +std::list<BS_Resource*>::iterator BS_ResourceManager::DeleteResource(BS_Resource* pResource) +{ + // Resource aus der Hash-Tabelle entfernen + m_ResourceHashTable[pResource->GetFileNameHash() % HASH_TABLE_BUCKETS].remove(pResource); + + BS_Resource* pDummy = pResource; + + // Resource aus der Resourcenliste löschen + std::list<BS_Resource*>::iterator Result = m_Resources.erase(pResource->_Iterator); + + // Resource freigeben + delete(pDummy); + + // Iterator zurückgeben + return Result; +} + +BS_Resource* BS_ResourceManager::GetResource(const std::string& UniqueFileName) const +{ + // Feststellen, ob die Resource schon geladen ist. + const std::list<BS_Resource*>& HashBucket = m_ResourceHashTable[BS_String::GetHash(UniqueFileName) % HASH_TABLE_BUCKETS]; + { + std::list<BS_Resource*>::const_iterator Iter = HashBucket.begin(); + for (; Iter != HashBucket.end(); ++Iter) + { + // Wenn die Resource gefunden wurde wird sie zurückgegeben. + if ((*Iter)->GetFileName() == UniqueFileName) + return *Iter; + } + } + + // Resource wurde nicht gefunden, ist also nicht geladen + return NULL; +} + +void BS_ResourceManager::DumpLockedResources() +{ + for (std::list<BS_Resource*>::iterator Iter = m_Resources.begin(); Iter != m_Resources.end(); ++Iter) + { + if ((*Iter)->GetLockCount() > 0) + { + BS_LOGLN("%s", (*Iter)->GetFileName().c_str()); + } + } +} + +void BS_ResourceManager::SetMaxMemoryUsage(unsigned int MaxMemoryUsage) +{ + m_MaxMemoryUsage = MaxMemoryUsage; + DeleteResourcesIfNecessary(); +}
\ No newline at end of file diff --git a/engines/sword25/kernel/resmanager.h b/engines/sword25/kernel/resmanager.h new file mode 100755 index 0000000000..6bc6b2a8f8 --- /dev/null +++ b/engines/sword25/kernel/resmanager.h @@ -0,0 +1,185 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_RESOURCEMANAGER_H +#define BS_RESOURCEMANAGER_H + +// Includes +#include "memlog_off.h" +#include <vector> +#include <list> +#include "memlog_on.h" + +#include "common.h" + +// Klassendefinition +class BS_ResourceService; +class BS_Resource; +class BS_Kernel; + +class BS_ResourceManager +{ +friend BS_Kernel; + +public: + /** + @brief Fordert eine Resource an. + @param FileName Dateiname + @return Gibt die Resource zurück, falls erfolgreich, sonst NULL + */ + BS_Resource* RequestResource(const std::string& FileName); + + /** + @brief Lädt eine Resource in den Cache. + @param FileName der Dateiname der zu cachenden Resource + @param ForceReload gibt an, ob die Datei auch neu geladen werden soll, wenn sie bereits geladen wurde. + Dies ist nützlich bei Dateien, die sich in der Zwischenzeit verändert haben. + @return Gibt true zurück, wenn das Caching durchgeführt werden konnte, ansonsten false. + */ + bool PrecacheResource(const std::string& FileName, bool ForceReload = false); + + /** + @brief Gibt die Anzahl der geladenen Resourcen zurück. + */ + int GetResourceCount() const { return static_cast<int>(m_Resources.size()); } + + /** + @brief Gibt einen Pointer auf eine Resource anhand deren laufender Nummer zurück. + @param Ord laufende Nummer der Resource. Dieser Wert muss zwischen 0 und GetResourceCount() - 1 liegen. + @return Gibt einen Pointer auf die Resource zurück wenn erfolgreich, ansonsten NULL. + @remark Diese Methode ist nicht auf Geschwindigkeit optimiert und sollte nur für Debugzwecke eingesetzt werden. + */ + BS_Resource* GetResourceByOrdinal(int Ord) const; + + /** + @brief RegisterResourceService Diese Methode wird vom Konstruktor von + BS_ResourceService aufgerufen und trägt somit alle + Resource-Services in einen Liste des + Resource-Managers ein. + @param pService welches Service + @return gibt true zurück, falls erfolgreich + */ + bool RegisterResourceService(BS_ResourceService* pService); + + /** + * @brief gibt alle Resourcen frei, die nicht gelocked sind. + **/ + void EmptyCache(); + + /** + @brief Gibt zurück wie viel Speicher die Engine maximal belegen soll. + */ + int GetMaxMemoryUsage() const { return m_MaxMemoryUsage; } + + /** + @brief Legt fest wie viel Speicher die Engine maximal belegen soll. + + Wenn dieser Wert überschritten wird, werden Resourcen entladen um Platz zu schaffen. Dieser Wert ist als Richtgröße zu verstehen und nicht als feste Grenze. + Es ist unter KEINEN Umständen garantiert, dass die Engine tatsächlich nur so viel Speicher benutzt. + */ + void SetMaxMemoryUsage(unsigned int MaxMemoryUsage); + + /** + @brief Gibt an, ob eine Warnung ins Log geschrieben wird, wenn ein Cache-Miss auftritt. + Der Standardwert ist "false". + */ + bool IsLogCacheMiss() const { return m_LogCacheMiss; } + + /** + @brief Legt fest, ob eine Warnung ins Log geschrieben wird, wenn in Cache-Miss auftritt. + @param Flag wenn "true" wird die Warnung in Zukunft ausgegeben, ansonsten nicht. + */ + void SetLogCacheMiss(bool Flag) { m_LogCacheMiss = Flag; } + + /** + @brief Schreibt die Namen aller gelockten Resourcen in die Log-Datei. + */ + void DumpLockedResources(); + +private: + /** + @brief Erzeugt einen neuen Resource-Manager. + @param pKernel ein Pointer auf den Kernel. + @remark Nur der BS_Kernel darf Exemplare dieser Klasse erzeugen. Daher ist der Konstruktor private. + */ + BS_ResourceManager(BS_Kernel* pKernel) : + m_KernelPtr(pKernel), + m_MaxMemoryUsage(100000000), + m_LogCacheMiss(false) + {}; + virtual ~BS_ResourceManager(); + + enum + { + HASH_TABLE_BUCKETS = 256 + }; + + /** + @brief Verschiebt eine Resource an die Spitze der Resourcenliste. + @param pResource die Resource + */ + void MoveToFront(BS_Resource* pResource); + + /** + @brief Lädt eine Resource und aktualisiert m_UsedMemory. + + Die Resource darf nicht bereits geladen sein. + + @param FileName der absolute und eindeutige Dateiname der zu ladenen Resource + @return Gibt einen Pointer auf die geladene Resource zurück wenn das Laden erfolgreich war, ansonsten NULL. + */ + BS_Resource* LoadResource(const std::string& FileName); + + /** + @brief Gibt zu einer Datei ihren absoluten, eindeutigen Pfad zurück. + @param FileName der Dateiname + @return Der absolute, eindeutige Pfad zur Datei inklusive des Dateinamens.<br> + Gibt einen leeren std::string zurück, wenn der Pfad nicht erzeugt werden konnte. + */ + std::string GetUniqueFileName(const std::string& FileName) const; + + /** + @brief Löscht eine Resource entfernt sie aus den Listen und aktualisiert m_UsedMemory. + @param pResource die zu löschende Resource + @return Gibt einen Iterator zurück, der auf die nächste Resource in der Resourcenliste zeigt. + */ + std::list<BS_Resource*>::iterator DeleteResource(BS_Resource* pResource); + + /** + @brief Holt einen Pointer zu einer geladenen Resource. + @param UniqueFileName der absolute, eindeutige Pfad zur Datei inklusive des Dateinamens. + @return Gibt einen Pointer auf die angeforderte Resource zurück, oder NULL, wenn die Resourcen nicht geladen ist. + */ + BS_Resource* GetResource(const std::string& UniqueFileName) const; + + /** + @brief Löscht solange Resourcen, bis der Prozess unter das angegebene Maximun an Speicherbelegung kommt. + */ + void DeleteResourcesIfNecessary(); + + BS_Kernel* m_KernelPtr; + unsigned int m_MaxMemoryUsage; + std::vector<BS_ResourceService*> m_ResourceServices; + std::list<BS_Resource*> m_Resources; + std::list<BS_Resource*> m_ResourceHashTable[HASH_TABLE_BUCKETS]; + bool m_LogCacheMiss; +}; + +#endif + diff --git a/engines/sword25/kernel/resource.cpp b/engines/sword25/kernel/resource.cpp new file mode 100755 index 0000000000..bfcd5a54b4 --- /dev/null +++ b/engines/sword25/kernel/resource.cpp @@ -0,0 +1,45 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#include "resource.h" +#include "string.h" +#include "kernel.h" +#include "package/packagemanager.h" + +#define BS_LOG_PREFIX "RESOURCE" + +BS_Resource::BS_Resource(const std::string& FileName, RESOURCE_TYPES Type) : + _Type(Type), + _RefCount(0) +{ + BS_ASSERT(BS_Kernel::GetInstance()->GetService("package")); + + _FileName = static_cast<BS_PackageManager *>(BS_Kernel::GetInstance()->GetService("package"))->GetAbsolutePath(FileName); + _FileNameHash = BS_String::GetHash(FileName); +}; + +void BS_Resource::Release() +{ + if (_RefCount) + { + --_RefCount; + } + else + BS_LOG_WARNINGLN("Released unlocked resource \"%s\".", _FileName.c_str()); +} diff --git a/engines/sword25/kernel/resource.h b/engines/sword25/kernel/resource.h new file mode 100755 index 0000000000..5d6c542b07 --- /dev/null +++ b/engines/sword25/kernel/resource.h @@ -0,0 +1,97 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_RESOURCE_H +#define BS_RESOURCE_H + +#include "memlog_off.h" +#include <list> +#include "memlog_on.h" + +#include "common.h" + +class BS_Kernel; +class BS_ResourceManager; + +class BS_Resource +{ +friend BS_ResourceManager; + +public: + enum RESOURCE_TYPES + { + TYPE_UNKNOWN, + TYPE_BITMAP, + TYPE_ANIMATION, + TYPE_SOUND, + TYPE_FONT + }; + + BS_Resource(const std::string& UniqueFileName, RESOURCE_TYPES Type); + + /** + * @brief `Lockt' die Resource, verhindert, dass sie freigegeben wird. + * @remarks Die Resource wird bereits `gelockt' initialisiert, sie muss also nach dem Anfordern nur + * gelockt werden, wenn sie mehrfach verwendet wird. + **/ + + void AddReference() { ++_RefCount; } + + /** + * @brief Hebt ein vorhergehendes `lock' auf. + * @remarks Die Resource kann ruhig öfter freigegeben als `gelockt' werden, auch wenn das nicht gerade empfehlenswert ist. + **/ + + void Release(); + + /** + * @brief Gibt die Anzahl der aktuellen `locks' zurück. + * @return Die Zahl der `locks'. + **/ + + int GetLockCount() const { return _RefCount; } + + /** + @brief Gibt den absoluten, eindeutigen Dateinamen der Resource zurück. + */ + + const std::string & GetFileName() const { return _FileName; } + + /** + @brief Gibt den Hash des Dateinames der Resource zurück. + */ + unsigned int GetFileNameHash() const { return _FileNameHash; } + + /** + @brief Gibt den Typ der Ressource zurück. + */ + unsigned int GetType() const { return _Type; } + +protected: + virtual ~BS_Resource() {}; + +private: + std::string _FileName; //!< Der absolute Dateiname + unsigned int _FileNameHash; //!< Der Hashwert des Dateinames + unsigned int _RefCount; //!< Anzahl an Locks + unsigned int _Type; //!< Der Typ der Resource + std::list<BS_Resource*>::iterator _Iterator; //!< Der Iterator zeigt auf Position der Resource in der LRU-Liste +}; + +#endif diff --git a/engines/sword25/kernel/resservice.h b/engines/sword25/kernel/resservice.h new file mode 100755 index 0000000000..d2369c0d87 --- /dev/null +++ b/engines/sword25/kernel/resservice.h @@ -0,0 +1,110 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_RESOURCESERVICE_H +#define BS_RESOURCESERVICE_H + +// Includes +#include "common.h" +#include "service.h" +#include "kernel.h" +#include "resmanager.h" + +class BS_Resource; + +class BS_ResourceService : public BS_Service +{ +public: + BS_ResourceService(BS_Kernel* pKernel) : BS_Service(pKernel) + { + BS_ResourceManager* pResource = pKernel->GetResourceManager(); + pResource->RegisterResourceService(this); + } + + virtual ~BS_ResourceService() {} + + + /** + @brief Lädt eine Resource. + @param FileName Dateiname + @return gibt die Resource zurück, falls erfolgreich, ansonsten NULL + */ + virtual BS_Resource* LoadResource(const std::string& FileName) = 0; + + /** + @brief CanLoadResource prüft, ob Resource vom Service geladen werden kann. + @param FileName Dateiname + @return true, falls Service Resource laden kann + @remark Überprüfung basiert auf dem Dateinamen, wenn das Dateiformat nicht passt, gibts Ärger. + */ + virtual bool CanLoadResource(const std::string& FileName) = 0; + +protected: + // Hilfsmethoden für BS_ResourceService Klassen + + /** + @brief Vergleicht zwei Strings, wobei der zweite String die Wildcards * und ? enthalten darf + @param String der erste Vergleichsstring. Dieser darf keine Wildcards enthalten. + @param Pattern der zweite Vergleichsstring. Dieser darf die Wildcards * und ? enthalten. + @return Gibt true zurück, wenn der String auf das Pattern passt, ansonsten false. + */ + bool _WildCardStringMatch(const std::string& String, const std::string& Pattern) + { + return _WildCardStringMatchRecursion(String.c_str(), Pattern.c_str()); + } + +private: + bool _WildCardStringMatchRecursion(const char* String, const char* Pattern) + { + // Rekursionsabschlüsse: + // 1. Der Pattern-String enthält nur noch * -> TRUE + if (*Pattern == '*') + { + // Es muss mit einer Kopie von Pattern gearbeitet werden um den aktuellen Zustand nicht zu zerstören + char* PatternCopy = (char*) Pattern; + while (*PatternCopy == '*') { PatternCopy++; } + if (!*PatternCopy) return true; + } + // 2. Der String ist zuende, das Pattern aber noch nicht -> FALSE + if (!*String && *Pattern) return false; + // 3. Der String ist zuende, also ist auch das Pattern zuende (s.o.) -> TRUE + if (!*String) return true; + + // Rekursionsaufruf 1: + // Falls die beiden aktuellen Zeichen gleich sind, oder Pattern '?' ist wird das Ergebnis von restlichen String zurückgegeben + if (*String == *Pattern || *Pattern == '?') return _WildCardStringMatchRecursion(String + 1, Pattern + 1); + + // Falls nicht, wird untersucht ob ein '*' vorliegt + if (*Pattern == '*') + { + // Rekursionsaufruf 2: + // Zunächst wird das Ergebnis von String und Pattern + 1 untersucht... + if (_WildCardStringMatchRecursion(String, Pattern + 1)) return true; + // Falls das fehlschlägt, wird das Ergebnis von String + 1 Pattern zurückgegeben + else return _WildCardStringMatchRecursion(String + 1, Pattern); + // Die Rekursion kehrt also immer wieder an diese Stelle zurück, bis das Zeichen in String, + // dem nach dem '*' in Pattern entspricht + } + + // Wenn kein '*' in Pattern vorliegt, schlägt der Vergleich fehl + return false; + } +}; + +#endif diff --git a/engines/sword25/kernel/service.h b/engines/sword25/kernel/service.h new file mode 100755 index 0000000000..d79194f41a --- /dev/null +++ b/engines/sword25/kernel/service.h @@ -0,0 +1,57 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +/* + BS_Service + ------------- + Dies ist die Basisklasse für alle Services der Engine. + Ein Service ist ein wesentlicher Bestandteil des Engine, z.B. die Graphiksystem. + Das Servicesystem macht es möglich mehrere verschiedene Services für ein System zu haben, + und je nach Betriebssystem einen passenden auszuwählen. + Denkbar wären z.B. zwei Graphiksysteme von denen eines hardwarebeschleunigt ist und ein + anderes nicht. + Die Services werden zur Laufzeit über die Kernelmethode NewService und NIEMALS mit new erzeugt. + + Autor: Malte Thiesen +*/ + +#ifndef _BS_SERVICE_H +#define _BS_SERVICE_H + +// Includes +#include "common.h" + +// Klassendefinition +class BS_Kernel; + +class BS_Service +{ +private: + BS_Kernel* _pKernel; + +protected: + BS_Service(BS_Kernel* pKernel) : _pKernel(pKernel) {}; + + BS_Kernel* GetKernel() const { return _pKernel; } + +public: + virtual ~BS_Service(){}; +}; + +#endif
\ No newline at end of file diff --git a/engines/sword25/kernel/service_ids.h b/engines/sword25/kernel/service_ids.h new file mode 100755 index 0000000000..38ba941f5e --- /dev/null +++ b/engines/sword25/kernel/service_ids.h @@ -0,0 +1,56 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +/* + service_ids.h + ------------- + In dieser Datei sind alle Services verzeichnet. + JEDER neuer Service muss hier eingetragen werden, ansonsten kann er nicht mit + pKernel->NewService(..) instanziiert werden. + + Autor: Malte Thiesen +*/ + +#include "common.h" + +BS_Service * BS_OpenGLGfx_CreateObject(BS_Kernel* pKernel); +BS_Service * BS_PhysfsPackageManager_CreateObject(BS_Kernel* pKernel); +BS_Service * BS_StdWinInput_CreateObject(BS_Kernel* pKernel); +BS_Service * BS_FMODExSound_CreateObject(BS_Kernel* pKernel); +BS_Service * BS_LuaScriptEngine_CreateObject(BS_Kernel* pKernel); +BS_Service * BS_Geometry_CreateObject(BS_Kernel* pKernel); +BS_Service * BS_OggTheora_CreateObject(BS_Kernel* pKernel); + +// Services müssen in dieser Tabelle eingetragen werden +const BS_ServiceInfo BS_SERVICE_TABLE[] = +{ + // Die ersten beiden Parameter sind die Namen der Superclass und des Services. + // Der dritte Parameter ist die statische Methode der Klasse, die ein Objekt der Klasse erzeugt und zurückgibt. + // Beispiel: + // BS_ServiceInfo("Superclass", "Service", CreateMethod) + BS_ServiceInfo("gfx", "opengl", BS_OpenGLGfx_CreateObject), + BS_ServiceInfo("package", "physfs", BS_PhysfsPackageManager_CreateObject), + BS_ServiceInfo("input", "winapi", BS_StdWinInput_CreateObject), + BS_ServiceInfo("sfx", "fmodex", BS_FMODExSound_CreateObject), + BS_ServiceInfo("script", "lua", BS_LuaScriptEngine_CreateObject), + BS_ServiceInfo("geometry", "std", BS_Geometry_CreateObject), + BS_ServiceInfo("fmv", "oggtheora", BS_OggTheora_CreateObject), +}; + +const unsigned int BS_SERVICE_COUNT = sizeof(BS_SERVICE_TABLE) / sizeof(BS_ServiceInfo); diff --git a/engines/sword25/kernel/string.h b/engines/sword25/kernel/string.h new file mode 100755 index 0000000000..b6802d3075 --- /dev/null +++ b/engines/sword25/kernel/string.h @@ -0,0 +1,117 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_STRING +#define BS_STRING + +#include "memlog_off.h" +#include <string> +#include "memlog_on.h" + +namespace BS_String +{ + inline unsigned int GetHash(const std::string & Str) + { + unsigned int Result = 0; + + for (unsigned int i = 0; i < Str.size(); i++) + Result = ((Result << 5) - Result) + Str[i]; + + return Result; + } + + inline bool ToInt(const std::string & Str, int & Result) + { + std::string::const_iterator Iter = Str.begin(); + + // Whitespace überspringen + while (*Iter && (*Iter == ' ' || *Iter == '\t')) { ++Iter; } + if (Iter == Str.end()) return false; + + // Vorzeichen auslesen, wenn vorhanden + bool IsNegative = false; + if (*Iter == '-') + { + IsNegative = true; + ++Iter; + } + else if (*Iter == '+') + ++Iter; + + // Whitespace überspringen + while (*Iter && (*Iter == ' ' || *Iter == '\t')) { ++Iter; } + if (Iter ==Str.end()) return false; + + // String in Ganzzahl umwandeln + Result = 0; + while (Iter != Str.end()) + { + if (*Iter < '0' || *Iter > '9') + { + while (*Iter && (*Iter == ' ' || *Iter == '\t')) { ++Iter; } + if (Iter != Str.end()) return false; + break; + } + Result = (Result * 10) + (*Iter - '0'); + ++Iter; + } + + if (IsNegative) Result = -Result; + + return true; + } + + inline bool ToBool(const std::string & Str, bool & Result) + { + if (Str == "true" || + Str == "TRUE") + { + Result = true; + return true; + } + else if (Str == "false" || + Str == "FALSE") + { + Result = false; + return true; + } + + return false; + } + + inline void ToLower(std::string & Str) + { + static const unsigned char LowerCaseMap[256] = + { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, + 64,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,91,92,93,94,95, + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, + 192,193,194,195,228,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,246,215,216,217,218,219,252,221,222,223, + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 + }; + + for (unsigned int i = 0; i < Str.size(); ++i) + Str[i] = LowerCaseMap[Str[i]]; + } +} + +#endif diff --git a/engines/sword25/kernel/timer.cpp b/engines/sword25/kernel/timer.cpp new file mode 100755 index 0000000000..22eb1200f3 --- /dev/null +++ b/engines/sword25/kernel/timer.cpp @@ -0,0 +1,53 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +#include "timer.h" + +#define BS_LOG_PREFIX "BS_TIMER" + +uint64_t BS_Timer::GetMicroTicks() +{ + HANDLE ThreadID = ::GetCurrentThread(); + + DWORD_PTR OldAffinityMask = ::SetThreadAffinityMask(ThreadID, 1); + + uint64_t Frequency; + ::QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&Frequency)); + + uint64_t Tick; + ::QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&Tick)); + + ::SetThreadAffinityMask(ThreadID, OldAffinityMask); + + return Tick * 1000000 / Frequency; +} + +unsigned int BS_Timer::GetMilliTicks() +{ + return (unsigned int)(GetMicroTicks() / 1000); +} + +bool BS_Timer::IsTimerAvaliable() +{ + LARGE_INTEGER Dummy; + return ::QueryPerformanceFrequency(&Dummy) ? true : false; +} diff --git a/engines/sword25/kernel/timer.h b/engines/sword25/kernel/timer.h new file mode 100755 index 0000000000..52a7d70a83 --- /dev/null +++ b/engines/sword25/kernel/timer.h @@ -0,0 +1,69 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +/* + BS_Timer + -------- + Eine Klasse zum Auslesen des Systemtimers. + + Autor: Malte Thiesen +*/ + +#ifndef BS_TIMER_H +#define BS_TIMER_H + +// Includes +#include "common.h" +#include "bs_stdint.h" + +/** + @brief Eine Klasse zum Auslesen des Systemtimers. + + Vor der Benutzung sollte immer mit IsTimerAvaliable() getestet werden, ob der Timer von der benutzten + Hardware unterstützt wird. +*/ +class BS_Timer +{ +public: + /** + @brief List den Systemtimer in Microsekunden aus. + @return Die Zahl vergangener Microsekunden seit dem Systemstart.<br> + */ + static uint64_t GetMicroTicks(); + + /** + @brief List den Systemtimer in Millisekunden aus. + @return Die Zahl vergangener Millisekunden seit dem Systemstart.<br> + */ + static unsigned int GetMilliTicks(); + + /** + @brief Prüft, ob der Timer auf der vorhandenen Hardware existiert. + @return Gibt false zurück, fals der Timer auf der vorhandenen Hardware nicht existiert. + */ + static bool IsTimerAvaliable(); + +private: + /** + @brief Initialisiert den Timer. + */ + static void Init(); +}; + +#endif diff --git a/engines/sword25/kernel/win32window.cpp b/engines/sword25/kernel/win32window.cpp new file mode 100755 index 0000000000..b5c519f206 --- /dev/null +++ b/engines/sword25/kernel/win32window.cpp @@ -0,0 +1,425 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#include "win32window.h" +#include "../../projects/resource.h" + +#include "kernel/kernel.h" +#include "input/inputengine.h" + +bool BS_Win32Window::_ClassRegistered = false; + +#define BS_LOG_PREFIX "WIN32WINDOW" + + +// Konstanten +// ---------- +static const UINT WINDOW_STYLE = WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; +static const UINT WINDOW_STYLE_EX = 0; +static const UINT WINDOW_MAX_MESSAGES = 50; + +// Konstruktion/Destruktion +// ------------------------ +BS_Win32Window::BS_Win32Window(int X, int Y, int Width, int Height, bool Visible) +{ + const char WINDOW_CLASS[] = "BSEngine-Class"; + + // Von negativen Fall ausgehen + _InitSuccess = false; + + // Fensterklasse registrieren falls nötig + if (!_ClassRegistered) + { + //Fensterklasse + WNDCLASSEX wndclass; + + //Werte der Fensterklasse festlegen + ZeroMemory(&wndclass, sizeof(WNDCLASSEX)); + wndclass.cbSize = sizeof(WNDCLASSEX); + wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wndclass.lpfnWndProc = BS_Win32Window::WindowProc; + wndclass.hInstance = GetModuleHandle(NULL); + wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION); + wndclass.hIconSm = LoadIcon(NULL,IDI_APPLICATION); + wndclass.hCursor = NULL; + wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + wndclass.lpszClassName = WINDOW_CLASS; + + //Fensterklasse registrieren + if (!RegisterClassEx(&wndclass)) return; + + _ClassRegistered = true; + } + + //Fenster erstellen + if (!(_Window=CreateWindowEx( + WINDOW_STYLE_EX, // Erweiterte Darstellungsflags + WINDOW_CLASS, // Registrierter Fenstername + "", // Kein Fenstertitel + WINDOW_STYLE, // Darstellungsflags + 0,0, // Default-Position + 0,0, // Default-Grösse + NULL, // Kein Parent-Fenster + NULL, // Kein Menü + GetModuleHandle(NULL), // Instance-Handle + NULL))) + return; + + // Fensterposition und Fenstergröße setzen + SetWidth(Width); + SetHeight(Height); + SetX(X); + SetY(Y); + + // Fenstersichtbarkeit setzen + SetVisible(Visible); + + // Icon setzen + HICON hIcon = LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON1)); + if (hIcon) + { + SendMessage(_Window, WM_SETICON, ICON_BIG, (LPARAM) hIcon); + SendMessage(_Window, WM_SETICON, ICON_SMALL, (LPARAM) hIcon); + } + + // Erfolg signalisieren + _InitSuccess = true; + _WindowAlive = true; + _CloseWanted = false; +} + +BS_Win32Window::~BS_Win32Window() +{ + // Fenster zerstören, falls dies nicht ohnehin schon passiert ist + if (_WindowAlive) DestroyWindow(_Window); +} + +// Get-Methoden +// ------------ +int BS_Win32Window::GetX() +{ + RECT Rect; + GetWindowRect(_Window, &Rect); + return Rect.left; +} + +int BS_Win32Window::GetY() +{ + RECT Rect; + GetWindowRect(_Window, &Rect); + return Rect.top; +} + +int BS_Win32Window::GetClientX() +{ + POINT Point = {0, 0}; + ClientToScreen(_Window, &Point); + return Point.x; +} + +int BS_Win32Window::GetClientY() +{ + POINT Point = {0, 0}; + ClientToScreen(_Window, &Point); + return Point.y; +} + +int BS_Win32Window::GetWidth() +{ + RECT Rect; + GetClientRect(_Window, &Rect); + return Rect.right - Rect.left; +} + +int BS_Win32Window::GetHeight() +{ + RECT Rect; + GetClientRect(_Window, &Rect); + return Rect.bottom - Rect.top; +} + +std::string BS_Win32Window::GetTitle() +{ + char String[512]; + if (GetWindowText(_Window, String, sizeof(String))) + return std::string(String); + + return std::string(""); +} + +bool BS_Win32Window::IsVisible() +{ + return IsWindowVisible(_Window) ? true : false; +} + +bool BS_Win32Window::HasFocus() +{ + return GetForegroundWindow() == _Window ? true : false; +} + +UINT BS_Win32Window::GetWindowHandle() +{ + return (UINT)_Window; +} + +// Set Methoden +// ------------ + +void BS_Win32Window::SetX(int X) +{ + int RealX; + if (X == -1) + { + RECT Rect; + GetWindowRect(_Window, &Rect); + RealX = (GetSystemMetrics(SM_CXSCREEN) - (Rect.right - Rect.left)) / 2; + } + else + RealX = X; + + SetWindowPos(_Window, NULL, RealX, GetY(), 0, 0, SWP_NOSIZE | SWP_NOZORDER); +} + +void BS_Win32Window::SetY(int Y) +{ + int RealY; + if (Y == -1) + { + RECT Rect; + GetWindowRect(_Window, &Rect); + RealY = (GetSystemMetrics(SM_CYSCREEN) - (Rect.bottom - Rect.top)) / 2; + } + else + RealY = Y; + + SetWindowPos(_Window, NULL, GetX(), RealY, 0, 0, SWP_NOSIZE | SWP_NOZORDER); +} + +void BS_Win32Window::SetWidth(int Width) +{ + RECT Rect = {0, 0, Width, GetHeight()}; + AdjustWindowRectEx(&Rect, WINDOW_STYLE, false, WINDOW_STYLE_EX); + SetWindowPos(_Window, NULL, 0, 0, Rect.right - Rect.left, Rect.bottom - Rect.top, SWP_NOMOVE | SWP_NOZORDER); +} + +void BS_Win32Window::SetHeight(int Height) +{ + RECT Rect = {0, 0, GetWidth(), Height}; + AdjustWindowRectEx(&Rect, WINDOW_STYLE, false, WINDOW_STYLE_EX); + SetWindowPos(_Window, NULL, 0, 0, Rect.right - Rect.left, Rect.bottom - Rect.top, SWP_NOMOVE | SWP_NOZORDER); +} + +void BS_Win32Window::SetVisible(bool Visible) +{ + ShowWindow(_Window, Visible ? SW_SHOW : SW_HIDE); +} + +void BS_Win32Window::SetTitle(std::string Title) +{ + SetWindowText(_Window, Title.c_str()); +} + +// Asynchroner Message-Loop +bool BS_Win32Window::ProcessMessages() +{ + for (UINT i = 0; i < WINDOW_MAX_MESSAGES; i++) + { + MSG msg; + if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) + { + if (msg.message == WM_QUIT) + { + _WindowAlive = false; + return false; + } + + // Alle Nachrichten zur Verarbeitung durch WindowProc vorbereiten + TranslateMessage(&msg); + // Nachricht an WindowProc übergeben + DispatchMessage(&msg); + } + else + return true; + } + + return true; +} + +// Synchroner Message-Loop +bool BS_Win32Window::WaitForFocus() +{ + MSG msg; + + // Fenster minimieren + ShowWindow(_Window, SW_MINIMIZE); + + for (;;) + { + // Auf Nachricht warten + WaitMessage(); + // Nachricht einlesen + GetMessage(&msg, NULL, 0, 0); + // Nachricht zur Verarbeitung durch WindowProc vorbereiten + TranslateMessage(&msg); + // Nachricht an WindowProc übergeben + DispatchMessage(&msg); + + // Überprüfen ob das Fenster geschlossen wurde + if (msg.message == WM_QUIT) + { + _WindowAlive = false; + return false; + } + + // Überprüfen, ob das Fenster den Focus wiedererlangt hat + if (HasFocus()) return true; + } +} + +// Die WindowProc aller Fenster der Klasse +LRESULT CALLBACK BS_Win32Window::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_PAINT: + ValidateRect(hwnd, NULL); + break; + + case WM_DESTROY: + // Das Fenster wird zerstört + PostQuitMessage(0); + break; + + case WM_CLOSE: + { + BS_Window * WindowPtr = BS_Kernel::GetInstance()->GetWindow(); + if (WindowPtr) { + WindowPtr->SetCloseWanted(true); + } + break; + } + + case WM_KEYDOWN: + { + // Tastendrücke, die für das Inputmodul interessant sind, werden diesem gemeldet. + BS_InputEngine * InputPtr = BS_Kernel::GetInstance()->GetInput(); + + if (InputPtr) + { + switch (wParam) + { + case VK_RETURN: + InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_ENTER); + break; + + case VK_LEFT: + InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_LEFT); + break; + + case VK_RIGHT: + InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_RIGHT); + break; + + case VK_HOME: + InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_HOME); + break; + + case VK_END: + InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_END); + break; + + case VK_BACK: + InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_BACKSPACE); + break; + + case VK_TAB: + InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_TAB); + break; + + case VK_INSERT: + InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_INSERT); + break; + + case VK_DELETE: + InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_DELETE); + break; + } + } + break; + } + + case WM_KEYUP: + case WM_SYSKEYUP: + // Alle Tastendrücke werden ignoriert, damit Windows per DefWindowProc() nicht darauf + // reagieren kann und damit unerwartete Seiteneffekte auslöst. + // Zum Beispiel würden ALT und F10 Tastendrücke das "Menü" aktivieren und somit den Message-Loop zum Stillstand bringen. + break; + + case WM_SYSCOMMAND: + // Verhindern, dass der Bildschirmschoner aktiviert wird, während das Spiel läuft + if (wParam != SC_SCREENSAVE) return DefWindowProc(hwnd,uMsg,wParam,lParam); + break; + + case WM_CHAR: + { + unsigned char theChar = static_cast<unsigned char>(wParam & 0xff); + + // Alle Zeichen, die keine Steuerzeichen sind, werden als Buchstaben dem Input-Service mitgeteilt. + if (theChar >= 32) + { + BS_InputEngine * InputPtr = BS_Kernel::GetInstance()->GetInput(); + if (InputPtr) InputPtr->ReportCharacter(theChar); + } + } + break; + + case WM_SETCURSOR: + { + // Der Systemcursor wird in der Client-Area des Fensters nicht angezeigt, jedoch in der nicht Client-Area, damit der Benutzer das Fenster wie gewohnt + // schließen und verschieben kann. + + // Koordinaten des Cursors in der Client-Area berechnen. + POINT pt; + GetCursorPos(&pt); + ScreenToClient(hwnd, &pt); + + // Feststellen, ob sich der Cursor in der Client-Area befindet. + // Get client rect + RECT rc; + GetClientRect(hwnd, &rc); + + // See if cursor is in client area + if(PtInRect(&rc, pt)) + // In der Client-Area keinen Cursor anzeigen. + SetCursor(NULL); + else + // Ausserhalb der Client-Area den Cursor anzeigen. + SetCursor(LoadCursor(NULL, IDC_ARROW)); + + return TRUE; + } + break; + + default: + // Um alle anderen Vorkommnisse kümmert sich Windows + return DefWindowProc(hwnd,uMsg,wParam,lParam); + } + + return 0; +} diff --git a/engines/sword25/kernel/win32window.h b/engines/sword25/kernel/win32window.h new file mode 100755 index 0000000000..e37892a0cb --- /dev/null +++ b/engines/sword25/kernel/win32window.h @@ -0,0 +1,77 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +/* + BS_Win32Window + ------------- + Implementation des BS_Window Interfaces für Win32. + Zu den einzelnen Methoden bitte "window.h" konsultieren. + + Autor: Malte Thiesen +*/ + +#ifndef _BS_WIN32WINDOW_H +#define _BS_WIN32WINDOW_H + +// Includes +#include "memlog_off.h" +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include "memlog_on.h" + +#include "common.h" +#include "window.h" + +// Klassendefinition +class BS_Win32Window : public BS_Window +{ +public: + BS_Win32Window(int X, int Y, int Width, int Height, bool Visible); + virtual ~BS_Win32Window(); + + bool IsVisible(); + void SetVisible(bool Visible); + int GetX(); + void SetX(int X); + int GetY(); + void SetY(int X); + int GetClientX(); + int GetClientY(); + int GetWidth(); + void SetWidth(int Width); + int GetHeight(); + void SetHeight(int Height); + std::string GetTitle(); + void SetTitle(std::string Title); + bool HasFocus(); + UINT GetWindowHandle(); + bool WaitForFocus(); + bool ProcessMessages(); + +private: + static bool _ClassRegistered; + bool _WindowAlive; + HWND _Window; + int _ClientXDelta; + int _ClientYDelta; + + static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +}; + +#endif
\ No newline at end of file diff --git a/engines/sword25/kernel/wincodegenerator.cpp b/engines/sword25/kernel/wincodegenerator.cpp new file mode 100755 index 0000000000..1c264ad17b --- /dev/null +++ b/engines/sword25/kernel/wincodegenerator.cpp @@ -0,0 +1,72 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "md5.h" +#include "wincodegenerator.h" +using namespace std; + +// ----------------------------------------------------------------------------- +// Hilfsfunktionen und Konstanten +// ----------------------------------------------------------------------------- + +namespace +{ + const char SECRET[] = "LSZNRVWQJHITMIEGESJMZAYVKGTCDT"; + + // ------------------------------------------------------------------------- + + string EncodeValue(unsigned int Value) + { + string Result; + + for (unsigned int i = 0; i < 7; ++i) + { + Result.push_back(65 + Value % 26); + Value /= 26; + } + + return Result; + } +} + +// ----------------------------------------------------------------------------- + +string BS_WinCodeGenerator::GetWinCode() +{ + // Die System-ID generieren und als String codieren. + string SystemID = EncodeValue(GetSystemID()); + + // Den Hashwert der System-ID mit dem geheimen String berechnen. + BS_MD5 md5; + string HashData = SystemID + SECRET; + md5.Update(reinterpret_cast<const unsigned char *>(&HashData[0]), HashData.size()); + unsigned char Digest[16]; + md5.GetDigest(Digest); + + // Die ersten 32-Bit des Digest werden aus dem Digest extrahiert. Zudem wird das oberste Bit ausmaskiert. + // So ist es einfacher den Code serverseitig zu überprüfen, da viele Scriptsprachen mit 32-Bit signed integern rechnen. + unsigned int ValidationHash = ((Digest[3] & 0x7f) << 24) + (Digest[2] << 16) + (Digest[1] << 8) + Digest[0]; + + // Der Code besteht aus der codierten System-ID und dem codierten Hash. + return SystemID + EncodeValue(ValidationHash); +} diff --git a/engines/sword25/kernel/wincodegenerator.h b/engines/sword25/kernel/wincodegenerator.h new file mode 100755 index 0000000000..7e1ee78bd0 --- /dev/null +++ b/engines/sword25/kernel/wincodegenerator.h @@ -0,0 +1,43 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_WIN_CODE_GENERATOR_H +#define BS_WIN_CODE_GENERATOR_H + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include <string> +#include "common.h" + +// ----------------------------------------------------------------------------- +// Klassendefinition +// ----------------------------------------------------------------------------- + +class BS_WinCodeGenerator +{ +public: + static std::string GetWinCode(); + +private: + static unsigned int GetSystemID(); +}; + +#endif diff --git a/engines/sword25/kernel/wincodegenerator_win32.cpp b/engines/sword25/kernel/wincodegenerator_win32.cpp new file mode 100755 index 0000000000..571a4b9f86 --- /dev/null +++ b/engines/sword25/kernel/wincodegenerator_win32.cpp @@ -0,0 +1,128 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <LMCons.h> + +#include <vector> +using namespace std; + +#include "md5.h" +#include "wincodegenerator.h" + +// ----------------------------------------------------------------------------- +// Hilfsfunktionen +// ----------------------------------------------------------------------------- + +namespace +{ + void AddedFixedDrivesEntropy(BS_MD5 & md5) + { + if (DWORD LogicalDrivesMask = ::GetLogicalDrives()) + { + // Über alle Laufwerke iterieren. + char CurrentDriveLetter[] = "A:\\"; + while (LogicalDrivesMask && CurrentDriveLetter[0] <= 'Z') + { + if (LogicalDrivesMask & 1) + { + // Nur feste Laufwerke werden betrachtet, ansonsten würde sich die System-ID ändern, wenn jemand einen USB-Stick ansteckt oder + // eine CD einlegt. + if (::GetDriveTypeA(CurrentDriveLetter) == DRIVE_FIXED) + { + // Laufwerksinformationen auslesen. + CHAR VolumeNameBuffer[MAX_PATH + 1]; + DWORD SerialNumber; + DWORD MaximumComponentLength; + DWORD FileSystemFlags; + CHAR FileSystemNameBuffer[MAX_PATH + 1]; + if (::GetVolumeInformationA(CurrentDriveLetter, + VolumeNameBuffer, sizeof(VolumeNameBuffer), + &SerialNumber, + &MaximumComponentLength, + &FileSystemFlags, + FileSystemNameBuffer, sizeof(FileSystemNameBuffer))) + { + // Als Entropie werden genutzt: Laufwerksbuchstabe, Laufwerksbezeichnung, Seriennummer und Dateisystemname. + md5.Update(reinterpret_cast<const unsigned char *>(CurrentDriveLetter), strlen(CurrentDriveLetter)); + md5.Update(reinterpret_cast<const unsigned char *>(VolumeNameBuffer), strlen(VolumeNameBuffer)); + md5.Update(reinterpret_cast<const unsigned char *>(&SerialNumber), sizeof(SerialNumber)); + md5.Update(reinterpret_cast<const unsigned char *>(FileSystemNameBuffer), strlen(FileSystemNameBuffer)); + } + } + } + + LogicalDrivesMask >>= 1; + ++CurrentDriveLetter[0]; + } + } + } + + // ------------------------------------------------------------------------- + + void AddUserNameEntropy(BS_MD5 & md5) + { + // Benutzernamen auslesen und als Entropie nutzen. + DWORD UserNameLength = UNLEN + 1; + CHAR UserName[UNLEN + 1]; + if (::GetUserNameA(UserName, &UserNameLength)) + { + md5.Update(reinterpret_cast<const unsigned char *>(&UserName[0]), strlen(UserName)); + } + } + + // ------------------------------------------------------------------------- + + void AddOSVersionEntropy(BS_MD5 & md5) + { + // Windows-Version auslesen und in die Einzelkomponenten MajorVersion, MinorVersion und Build aufspalten. + DWORD VersionInfo = ::GetVersion(); + + DWORD MajorVersion = (DWORD)(LOBYTE(LOWORD(VersionInfo))); + DWORD MinorVersion = (DWORD)(HIBYTE(LOWORD(VersionInfo))); + DWORD Build = 0; + if (VersionInfo < 0x80000000) Build = (DWORD)(HIWORD(VersionInfo)); + + // Diese drei Informationen als Entropie nutzen. + md5.Update(reinterpret_cast<const unsigned char *>(&MajorVersion), sizeof(DWORD)); + md5.Update(reinterpret_cast<const unsigned char *>(&MinorVersion), sizeof(DWORD)); + md5.Update(reinterpret_cast<const unsigned char *>(&Build), sizeof(DWORD)); + } +} + +// ----------------------------------------------------------------------------- + +unsigned int BS_WinCodeGenerator::GetSystemID() +{ + BS_MD5 md5; + + AddedFixedDrivesEntropy(md5); + AddUserNameEntropy(md5); + AddOSVersionEntropy(md5); + + unsigned char Digest[16]; + md5.GetDigest(Digest); + + return (Digest[3] << 24) + (Digest[2] << 16) + (Digest[1] << 8) + Digest[0]; +} diff --git a/engines/sword25/kernel/window.cpp b/engines/sword25/kernel/window.cpp new file mode 100755 index 0000000000..b7b6e0aa27 --- /dev/null +++ b/engines/sword25/kernel/window.cpp @@ -0,0 +1,53 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#include "window.h" + +// Alle Implementationen von BS_Window müssen hier eingetragen werden +#include "win32window.h" + +// Erstellt ein Fenster des GUI des aktuellen Betriebssystems +BS_Window* BS_Window::CreateBSWindow(int X, int Y, int Width, int Height, bool Visible) +{ + // Fenster erstellen + BS_Window* pWindow = (BS_Window*) new BS_Win32Window(X, Y, Width, Height, Visible); + + // Falls das Fenster erfolgreich initialisiert wurde, wird ein Pointer auf das Fensterobjekt + // zurückgegeben + if (pWindow->_InitSuccess) + return pWindow; + + // Ansonsten wird das Fensterobjekt zerstört und NULL zurückgegeben + delete pWindow; + return NULL; +} + +// Gibt True zurück wenn das Fenster WM_CLOSE empfangen hat - +// solange, bis RejectClose() aufgerufen wurde. +bool BS_Window::CloseWanted() +{ + bool result = _CloseWanted; + _CloseWanted = false; + return result; +} + +void BS_Window::SetCloseWanted(bool Wanted) +{ + _CloseWanted = Wanted; +} diff --git a/engines/sword25/kernel/window.h b/engines/sword25/kernel/window.h new file mode 100755 index 0000000000..6a6121f247 --- /dev/null +++ b/engines/sword25/kernel/window.h @@ -0,0 +1,176 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +/* + BS_Window + --------- + Simples Fensterklasseninterface. + Ist nur aus Gründen der Portabilität in einer Klasse gekapselt. + TODO: Für andere Betriebssysteme implementieren + + Autor: Malte Thiesen +*/ + +#ifndef _BS_WINDOW_H +#define _BS_WINDOW_H + +// Includes +#include "common.h" + +// Klassendefinition +/** + @brief Ein Simples Fensterklasseninterface. + + Fenster werden ausschließlich mit BS_Window::CreateBSWindow() erstellt. BS_Window wählt + dann selbständig die richtige Klasse für das Betriebssystem aus. +*/ +class BS_Window +{ +protected: + bool _InitSuccess; + bool _CloseWanted; + +public: + virtual ~BS_Window(){}; + + /** + @brief Gibt den Sichtbarkeitsstatus des Fensters zurück + @return Gibt true zurück wenn das Fenster sichtbar ist, andernfalls false + */ + virtual bool IsVisible() = 0; + /** + @brief Setzt den Sichtbarkeitsstatus des Fensters + @param Visible gibt an, ob das Fenster sichtbar oder versteckt sein soll + */ + virtual void SetVisible(bool Visible) = 0; + /** + @brief Gibt die Position des Fensters auf der X-Achse zurück + @return Gibt die Position des Fensters auf der X-Achse zurück + */ + virtual int GetX() = 0; + /** + @brief Setzt die Position des Fensters auf der X-Achse + @param X die X-Position des Fensters oder -1 für zentrierte Ausrichtung auf der X-Achse + */ + virtual void SetX(int X) = 0; + /** + @brief Gibt die Position des Fensters auf der Y-Achse zurück + @return Gibt die Position des Fensters auf der Y-Achse zurück + */ + virtual int GetY() = 0; + /** + @brief Setzt die Position des Fensters auf der Y-Achse + @param Y die Y-Position des Fensters oder -1 für zentrierte Ausrichtung auf der Y-Achse + */ + virtual void SetY(int X) = 0; + /** + @brief Gibt die Position des Fensterinhaltes auf der X-Achse zurück + @return Gibt die Position des Fensterinhaltes auf der X-Achse zurück + */ + virtual int GetClientX() = 0; + /** + @brief Gibt die Position des Fensterinhaltes auf der Y-Achse zurück + @return Gibt die Position des Fensterinhaltes auf der Y-Achse zurück + */ + virtual int GetClientY() = 0; + /** + @brief Gibt die Breite des Fensters ohne Rahmen zurück + @return Gibt die Breite des Fensters ohne Rahmen zurück + */ + virtual int GetWidth() = 0; + /** + @brief Setzt die Breite des Fensters ohne Rahmen + @param Width die Breite des Fensters ohne Rahmen + */ + virtual void SetWidth(int Width) = 0; + /** + @brief Gibt die Höhe des Fensters ohne Rahmen und Kopfzeile zurück + @return Gibt die Höhe des Fensters ohne Rahmen und Kopfzeile zurück + */ + virtual int GetHeight() = 0; + /** + @brief Setzt die Höhe des Fensters ohne Rahmen und Kopfzeile + @param Height die Höhe des Fensters ohne Rahmen und Kopfzeile + */ + virtual void SetHeight(int Height) = 0; + /** + @brief Gibt den Titel der Fensters zurück + @return Gibt den Titel des Fenster zurück + */ + virtual std::string GetTitle() = 0; + /** + @brief Setzt den Titel des Fensters + @param Title der neue Titel des Fensters + */ + virtual void SetTitle(std::string Title) = 0; + /** + @brief Arbeitet die Windowmessages des Fensters ab. + Diese Methode sollte während des Main-Loops aufgerufen werden. + @return Gibt false zurück, falls das Fenster geschlossen wurde. + */ + virtual bool ProcessMessages() = 0; + /** + @brief Pausiert die Applikation bis das Fenster wieder den Focus hat oder geschlossen wurde. + @return Gibt false zurück, falls das Fenster geschlossen wurde. + */ + virtual bool WaitForFocus() = 0; + /** + @brief Gibt zurück, ob das Fenster den Focus hat. + @return Gibt true zurück, wenn das Fenster den Focus hat, ansonsten false + */ + virtual bool HasFocus() = 0; + /** + @brief Gibt das Windowhandle, des Systems zurück. + @return Das Windowhandle des Fensters + @remark Wenn das Windowshandle benutzt wird, sind die entsprechenden Codeteile + natürlich nicht mehr portable. + */ + virtual unsigned int GetWindowHandle() = 0; + + + /** + @brief Setzt den Rückgabewert für den nächsten Aufruf von CloseWanted. Sollte vom + Fenster selbst verwendet werden, wenn es geschlossen werden möchte. Dieser + Mechanismus erlaubt den Scripten abzufragen, wann das Hauptfenster geschlossen + werden soll, und sich entsprechend zu beenden, bzw. beim Nutzer nachzufragen. + **/ + void SetCloseWanted(bool Wanted); + /** + @brief Gibt einmal den Wert des letztes Aufrufs von SetCloseWanted zurück, + und danach sofort wieder false, solange bis mit SetCloseWanted wieder + ein neuer Wert gesetzt wird. + **/ + bool CloseWanted(); + + + /** + @brief Erstellt eine neue Fensterinstanz. + @param X die X-Position des Fensters oder -1 für zentrierte Ausrichtung auf der X-Achse + @param Y des Y-Position des Fensters oder -1 für zentrierte Ausrichtung auf der Y-Achsen + @param Width die Breite des Fensters ohne Rahmen + @param Height die Höhe des Fensters ohne Rahmen und Kopfzeile + @param Visible gibt an, ob das Fenster dargestellt werden soll + @return Gibt einen Pointer auf ein Fensterobjekt zurück, oder NULL wenn das Erstellen + fehlgeschlagen ist. + @remark Das Fenster muss nach Benutzung mit delete freigegeben werden! + */ + static BS_Window* CreateBSWindow(int X, int Y, int Width, int Height, bool Visible); +}; + +#endif
\ No newline at end of file |