From c780148b77bd047b2291e2c2af7bec64163ec4ca Mon Sep 17 00:00:00 2001 From: Krzysztof Dermont Date: Thu, 11 Feb 2016 16:12:16 +0100 Subject: [PATCH] Fix crash related to TTF and PHYSFS TTF fonts will be loaded to memory instead of file pointers Added new class CSDLMemoryWrapper that loads data from PHYSFS into memory block and closes file after. This closes issues #519, #708 and #619 --- src/CMakeLists.txt | 1 + src/common/resources/resourcemanager.cpp | 5 ++ src/common/resources/resourcemanager.h | 2 + src/common/resources/sdl_file_wrapper.cpp | 8 +-- src/common/resources/sdl_memory_wrapper.cpp | 77 +++++++++++++++++++++ src/common/resources/sdl_memory_wrapper.h | 42 +++++++++++ src/graphics/engine/text.cpp | 6 +- 7 files changed, 132 insertions(+), 9 deletions(-) create mode 100644 src/common/resources/sdl_memory_wrapper.cpp create mode 100644 src/common/resources/sdl_memory_wrapper.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 35809a47..f0012b1f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -98,6 +98,7 @@ set(BASE_SOURCES common/resources/outputstreambuffer.cpp common/resources/resourcemanager.cpp common/resources/sdl_file_wrapper.cpp + common/resources/sdl_memory_wrapper.cpp common/resources/sndfile_wrapper.cpp common/restext.cpp common/settings.cpp diff --git a/src/common/resources/resourcemanager.cpp b/src/common/resources/resourcemanager.cpp index a81ac5de..dcf9ccb4 100644 --- a/src/common/resources/resourcemanager.cpp +++ b/src/common/resources/resourcemanager.cpp @@ -114,6 +114,11 @@ std::unique_ptr CResourceManager::GetSDLFileHandler(const std:: return MakeUnique(CleanPath(filename)); } +std::unique_ptr CResourceManager::GetSDLMemoryHandler(const std::string &filename) +{ + return MakeUnique(CleanPath(filename)); +} + std::unique_ptr CResourceManager::GetSNDFileHandler(const std::string &filename) { return MakeUnique(CleanPath(filename)); diff --git a/src/common/resources/resourcemanager.h b/src/common/resources/resourcemanager.h index 07870ec4..692ea95f 100644 --- a/src/common/resources/resourcemanager.h +++ b/src/common/resources/resourcemanager.h @@ -20,6 +20,7 @@ #pragma once #include "common/resources/sdl_file_wrapper.h" +#include "common/resources/sdl_memory_wrapper.h" #include "common/resources/sndfile_wrapper.h" #include @@ -41,6 +42,7 @@ public: static std::string GetSaveLocation(); static std::unique_ptr GetSDLFileHandler(const std::string &filename); + static std::unique_ptr GetSDLMemoryHandler(const std::string &filename); static std::unique_ptr GetSNDFileHandler(const std::string &filename); //! Check if file exists diff --git a/src/common/resources/sdl_file_wrapper.cpp b/src/common/resources/sdl_file_wrapper.cpp index 97cd2eef..c4f782fa 100644 --- a/src/common/resources/sdl_file_wrapper.cpp +++ b/src/common/resources/sdl_file_wrapper.cpp @@ -24,10 +24,6 @@ #include -namespace -{ - const Uint32 PHYSFS_RWOPS_TYPE = 0xc010b04f; -} CSDLFileWrapper::CSDLFileWrapper(const std::string& filename) : m_rwops(nullptr) @@ -52,7 +48,7 @@ CSDLFileWrapper::CSDLFileWrapper(const std::string& filename) return; } - m_rwops->type = PHYSFS_RWOPS_TYPE; //TODO: Documentation recommends to leave SDL_RWOPS_UNKNOWN here for application-defined RWops. Did that change in SDL2? + m_rwops->type = SDL_RWOPS_UNKNOWN; m_rwops->hidden.unknown.data1 = file; m_rwops->seek = SDLSeek; m_rwops->read = SDLRead; @@ -109,7 +105,7 @@ int CSDLFileWrapper::SDLCloseWithFreeRW(SDL_RWops *context) bool CSDLFileWrapper::CheckSDLContext(SDL_RWops *context) { - if (context->type != PHYSFS_RWOPS_TYPE) + if (context->type != SDL_RWOPS_UNKNOWN) { SDL_SetError("Wrong kind of RWops"); return false; diff --git a/src/common/resources/sdl_memory_wrapper.cpp b/src/common/resources/sdl_memory_wrapper.cpp new file mode 100644 index 00000000..d265d842 --- /dev/null +++ b/src/common/resources/sdl_memory_wrapper.cpp @@ -0,0 +1,77 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2015, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + + +#include "common/resources/sdl_memory_wrapper.h" + +#include "common/logger.h" + +#include + + +CSDLMemoryWrapper::CSDLMemoryWrapper(const std::string& filename) + : m_rwops(nullptr) +{ + if (!PHYSFS_isInit()) + { + GetLogger()->Error("PHYSFS not initialized!\n"); + return; + } + + PHYSFS_File *file = PHYSFS_openRead(filename.c_str()); + if (file == nullptr) + { + GetLogger()->Error("Error opening file with PHYSFS: \"%s\"\n", filename.c_str()); + return; + } + + PHYSFS_sint64 length = PHYSFS_fileLength(file); + m_buffer = new char[length]; + if (PHYSFS_read(file, m_buffer, 1, length) != length) + { + GetLogger()->Error("Unable to read data for \"%s\"\n", filename.c_str()); + PHYSFS_close(file); + return; + } + PHYSFS_close(file); + m_rwops = SDL_RWFromMem(m_buffer, length); + + if (m_rwops == nullptr) + { + GetLogger()->Error("Unable to allocate SDL_RWops for \"%s\"\n", filename.c_str()); + return; + } +} + +CSDLMemoryWrapper::~CSDLMemoryWrapper() +{ + SDL_FreeRW(m_rwops); + delete []m_buffer; +} + +SDL_RWops* CSDLMemoryWrapper::GetHandler() +{ + return m_rwops; +} + +bool CSDLMemoryWrapper::IsOpen() const +{ + return m_rwops != nullptr; +} + diff --git a/src/common/resources/sdl_memory_wrapper.h b/src/common/resources/sdl_memory_wrapper.h new file mode 100644 index 00000000..f446efdd --- /dev/null +++ b/src/common/resources/sdl_memory_wrapper.h @@ -0,0 +1,42 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2015, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + + +#pragma once + +#include + +#include + +class CSDLMemoryWrapper +{ +public: + CSDLMemoryWrapper(const std::string& filename); + ~CSDLMemoryWrapper(); + + CSDLMemoryWrapper(const CSDLMemoryWrapper&) = delete; + CSDLMemoryWrapper& operator=(const CSDLMemoryWrapper&) = delete; + + bool IsOpen() const; + SDL_RWops* GetHandler(); + +private: + SDL_RWops* m_rwops; + char *m_buffer; +}; diff --git a/src/graphics/engine/text.cpp b/src/graphics/engine/text.cpp index f5028d11..84f5f91c 100644 --- a/src/graphics/engine/text.cpp +++ b/src/graphics/engine/text.cpp @@ -60,11 +60,11 @@ struct MultisizeFont */ struct CachedFont { - std::unique_ptr fontFile; + std::unique_ptr fontFile; TTF_Font* font = nullptr; std::map cache; - CachedFont(std::unique_ptr fontFile, int pointSize) + CachedFont(std::unique_ptr fontFile, int pointSize) : fontFile(std::move(fontFile)) { font = TTF_OpenFontRW(this->fontFile->GetHandler(), 0, pointSize); @@ -980,7 +980,7 @@ CachedFont* CText::GetOrOpenFont(FontType font, float size) return m_lastCachedFont; } - auto file = CResourceManager::GetSDLFileHandler(mf->fileName); + auto file = CResourceManager::GetSDLMemoryHandler(mf->fileName); if (!file->IsOpen()) { m_error = std::string("Unable to open file");