diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..a7d0901 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,52 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build OpenVulkano", + "type": "shell", + "command": "cmake", + "args": [ + "--build", + "${workspaceFolder}/build", + "--target", + "openVulkanoCpp" + ], + "group": { + "kind": "build", + "isDefault": true + } + }, + + { + "label": "Build OpenVulkano examples", + "type": "shell", + "command": "cmake", + "args": [ + "--build", + "${workspaceFolder}/build", + "--target", + "OpenVulkano_Examples" + ], + "group": { + "kind": "build", + "isDefault": true + } + }, + + { + "label": "Build OpenVulkano tests", + "type": "shell", + "command": "cmake", + "args": [ + "--build", + "${workspaceFolder}/build", + "--target", + "OpenVulkano_Examples" + ], + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} \ No newline at end of file diff --git a/openVulkanoCpp/Host/ExeAppendedZipResourceLoader.cpp b/openVulkanoCpp/Host/ExeAppendedZipResourceLoader.cpp new file mode 100644 index 0000000..93a2365 --- /dev/null +++ b/openVulkanoCpp/Host/ExeAppendedZipResourceLoader.cpp @@ -0,0 +1,26 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "ExeAppendedZipResourceLoader.hpp" +#include "Base/Logger.hpp" +#include "IO/Archive/ArchiveReader.hpp" +#include "Base/Utils.hpp" +#include +#include + +namespace OpenVulkano +{ + Array ExeAppendedZipResourceLoader::GetResource(const std::string& resourceName) + { + ArchiveReader reader(GetCurrentExecutablePath(), nullptr, ArchiveType::ZIP); + if (auto data = reader.GetFile(resourceName)) + { + return data->second; + } + return {}; + } + +} \ No newline at end of file diff --git a/openVulkanoCpp/Host/ExeAppendedZipResourceLoader.hpp b/openVulkanoCpp/Host/ExeAppendedZipResourceLoader.hpp new file mode 100644 index 0000000..a5a1863 --- /dev/null +++ b/openVulkanoCpp/Host/ExeAppendedZipResourceLoader.hpp @@ -0,0 +1,24 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "Host/ResourceLoader.hpp" +#include "IO/FileDescription.hpp" +#include +#include + +namespace OpenVulkano +{ + class ExeAppendedZipResourceLoader : public ResourceLoader + { + public: + std::string GetResourcePath(const std::string& resourceName) final { return ""; } + Array GetResource(const std::string& resourceName) override; + protected: + virtual std::string GetCurrentExecutablePath() const = 0; + }; +} \ No newline at end of file diff --git a/openVulkanoCpp/Host/Linux/ExeAppendedZipResourceLoaderLinux.cpp b/openVulkanoCpp/Host/Linux/ExeAppendedZipResourceLoaderLinux.cpp new file mode 100644 index 0000000..e624bfb --- /dev/null +++ b/openVulkanoCpp/Host/Linux/ExeAppendedZipResourceLoaderLinux.cpp @@ -0,0 +1,26 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "ExeAppendedZipResourceLoaderLinux.hpp" +#include +#include + +namespace OpenVulkano +{ + namespace + { + void* HANDLE = ResourceLoader::RegisterResourceLoader(std::make_unique()); + } + + std::string OpenVulkano::ExeAppendedZipResourceLoaderLinux::GetCurrentExecutablePath() const + { + std::string path(PATH_MAX, '\0'); + ssize_t sz = readlink("/proc/self/exe", path.data(), path.capacity()); + path.resize(std::max(0, sz)); + return path; + } + +} diff --git a/openVulkanoCpp/Host/Linux/ExeAppendedZipResourceLoaderLinux.hpp b/openVulkanoCpp/Host/Linux/ExeAppendedZipResourceLoaderLinux.hpp new file mode 100644 index 0000000..73cfe31 --- /dev/null +++ b/openVulkanoCpp/Host/Linux/ExeAppendedZipResourceLoaderLinux.hpp @@ -0,0 +1,18 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "Host/ExeAppendedZipResourceLoader.hpp" + +namespace OpenVulkano +{ + class ExeAppendedZipResourceLoaderLinux final : public ExeAppendedZipResourceLoader + { + public: + std::string GetCurrentExecutablePath() const override; + }; +} \ No newline at end of file diff --git a/openVulkanoCpp/Host/ResourceLoader.hpp b/openVulkanoCpp/Host/ResourceLoader.hpp index 7b8000a..3cf051e 100644 --- a/openVulkanoCpp/Host/ResourceLoader.hpp +++ b/openVulkanoCpp/Host/ResourceLoader.hpp @@ -4,6 +4,8 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +#pragma once + #include "Data/Containers/Array.hpp" #include #include diff --git a/openVulkanoCpp/Host/Windows/EmbeddedResourceLoaderWindows.cpp b/openVulkanoCpp/Host/Windows/EmbeddedResourceLoaderWindows.cpp index 5ff7694..def9d0e 100644 --- a/openVulkanoCpp/Host/Windows/EmbeddedResourceLoaderWindows.cpp +++ b/openVulkanoCpp/Host/Windows/EmbeddedResourceLoaderWindows.cpp @@ -52,7 +52,7 @@ namespace OpenVulkano std::string EmbeddedResourceLoaderWindows::GetResourceName(const char* resId) { - // check if resId was provided as interger and not CString + // check if resId was provided as interger or CString // https://stackoverflow.com/questions/3610565/why-does-makeintresource-work // first 64KB uintptr_t ptrT = reinterpret_cast(resId); diff --git a/openVulkanoCpp/Host/Windows/ExeAppendedZipResourceLoaderWindows.cpp b/openVulkanoCpp/Host/Windows/ExeAppendedZipResourceLoaderWindows.cpp new file mode 100644 index 0000000..8800276 --- /dev/null +++ b/openVulkanoCpp/Host/Windows/ExeAppendedZipResourceLoaderWindows.cpp @@ -0,0 +1,25 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "ExeAppendedZipResourceLoaderWindows.hpp" +#include + +namespace OpenVulkano +{ + namespace + { + void* HANDLE = ResourceLoader::RegisterResourceLoader(std::make_unique()); + } + + std::string OpenVulkano::ExeAppendedZipResourceLoaderWindows::GetCurrentExecutablePath() const + { + std::string exe(MAX_PATH, '\0'); + DWORD len = GetModuleFileNameA(NULL, exe.data(), MAX_PATH); + exe.resize(len); + return exe; + } + +} \ No newline at end of file diff --git a/openVulkanoCpp/Host/Windows/ExeAppendedZipResourceLoaderWindows.hpp b/openVulkanoCpp/Host/Windows/ExeAppendedZipResourceLoaderWindows.hpp new file mode 100644 index 0000000..c1ff2c0 --- /dev/null +++ b/openVulkanoCpp/Host/Windows/ExeAppendedZipResourceLoaderWindows.hpp @@ -0,0 +1,18 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "Host/ExeAppendedZipResourceLoader.hpp" + +namespace OpenVulkano +{ + class ExeAppendedZipResourceLoaderWindows final : public ExeAppendedZipResourceLoader + { + public: + std::string GetCurrentExecutablePath() const override; + }; +} \ No newline at end of file diff --git a/openVulkanoCpp/IO/Archive/ArchiveReader.cpp b/openVulkanoCpp/IO/Archive/ArchiveReader.cpp index b8efd2e..b392842 100644 --- a/openVulkanoCpp/IO/Archive/ArchiveReader.cpp +++ b/openVulkanoCpp/IO/Archive/ArchiveReader.cpp @@ -20,22 +20,22 @@ namespace OpenVulkano constexpr int BUFFER_SIZE = 16384; } - ArchiveReader::ArchiveReader(const std::shared_ptr& logger) - : ArchiveBase(archive_read_new(), nullptr, logger) + ArchiveReader::ArchiveReader(const std::shared_ptr& logger, std::optional archiveType) + : ArchiveBase(archive_read_new(), nullptr, logger), m_archiveType(archiveType) {} - ArchiveReader::ArchiveReader(const std::string& archiveFile, const std::shared_ptr& logger) - : ArchiveReader(archiveFile.c_str(), logger) + ArchiveReader::ArchiveReader(const std::string& archiveFile, const std::shared_ptr& logger, std::optional archiveType) + : ArchiveReader(archiveFile.c_str(), logger, archiveType) {} - ArchiveReader::ArchiveReader(const char* archiveFile, const std::shared_ptr& logger) - : ArchiveReader(logger) + ArchiveReader::ArchiveReader(const char* archiveFile, const std::shared_ptr& logger, std::optional archiveType) + : ArchiveReader(logger, archiveType) { Open(archiveFile); } - ArchiveReader::ArchiveReader(const void* archiveBuffer, const size_t size, const std::shared_ptr& logger) - : ArchiveReader(logger) + ArchiveReader::ArchiveReader(const void* archiveBuffer, const size_t size, const std::shared_ptr& logger, std::optional archiveType) + : ArchiveReader(logger, archiveType) { OpenMemory(archiveBuffer, size); } @@ -51,7 +51,7 @@ namespace OpenVulkano m_archive = archive_read_new(); } ChkErr(archive_read_support_filter_all(m_archive)); - ChkErr(archive_read_support_format_all(m_archive)); + ChkErr(GetCurrentFormatReadFunc()(m_archive)); m_open = true; } @@ -156,6 +156,33 @@ namespace OpenVulkano } } + std::function ArchiveReader::GetCurrentFormatReadFunc() const + { + if (!m_archiveType) + { + return archive_read_support_format_all; + } + ArchiveType::Type type = *m_archiveType; + switch (type) + { + case ArchiveType::ZIP: + return archive_read_support_format_zip; + case ArchiveType::SEVEN_ZIP: + return archive_read_support_format_7zip; + case ArchiveType::CPIO: + return archive_read_support_format_cpio; + case ArchiveType::ISO: + return archive_read_support_format_iso9660; + case ArchiveType::TAR: + return archive_read_support_format_tar; + case ArchiveType::WARC: + return archive_read_support_format_warc; + case ArchiveType::XAR: + return archive_read_support_format_xar; + } + return archive_read_support_format_all; + } + size_t ArchiveReader::ExtractRemaining(std::string_view targetDir) { size_t count = 0; @@ -263,6 +290,22 @@ namespace OpenVulkano return std::nullopt; } + std::optional>> ArchiveReader::GetFile(const std::string& path) + { + while (HasNext()) + { + if (path == archive_entry_pathname(m_archiveEntry)) + { + return GetNextFile(); + } + if (archive_read_next_header(m_archive, &m_archiveEntry) != ARCHIVE_OK) + { + return {}; + } + } + return {}; + } + bool ArchiveReader::GetNextFileAsStream(const std::function& streamReader) { if (SkipTill(std::filesystem::file_type::regular)) diff --git a/openVulkanoCpp/IO/Archive/ArchiveReader.hpp b/openVulkanoCpp/IO/Archive/ArchiveReader.hpp index 776a57c..b2f0642 100644 --- a/openVulkanoCpp/IO/Archive/ArchiveReader.hpp +++ b/openVulkanoCpp/IO/Archive/ArchiveReader.hpp @@ -8,6 +8,7 @@ #include "ArchiveBase.hpp" #include "Data/Containers/Array.hpp" +#include "ArchiveType.hpp" #include #include #include @@ -20,17 +21,17 @@ namespace OpenVulkano { bool m_open = false; bool m_eof = false; - + std::optional m_archiveType; std::queue m_archivesToRead; public: - explicit ArchiveReader(const std::shared_ptr& logger = nullptr); + explicit ArchiveReader(const std::shared_ptr& logger = nullptr, std::optional archiveType = std::nullopt); - explicit ArchiveReader(const char* archiveFile, const std::shared_ptr& logger = nullptr); + explicit ArchiveReader(const char* archiveFile, const std::shared_ptr& logger = nullptr, std::optional archiveType = std::nullopt); - explicit ArchiveReader(const std::string& archiveFile, const std::shared_ptr& logger = nullptr); + explicit ArchiveReader(const std::string& archiveFile, const std::shared_ptr& logger = nullptr, std::optional archiveType = std::nullopt); - ArchiveReader(const void* archiveBuffer, size_t size, const std::shared_ptr& logger = nullptr); + ArchiveReader(const void* archiveBuffer, size_t size, const std::shared_ptr& logger = nullptr, std::optional archiveType = std::nullopt); ~ArchiveReader() override; @@ -65,13 +66,15 @@ namespace OpenVulkano std::optional>> GetNextFileAsVector(); + std::optional>> GetFile(const std::string& path); + std::optional StreamNextFile(std::ostream& stream); bool GetNextFileAsStream(const std::function& streamReader); private: void ReadNextHeader(); - + std::function GetCurrentFormatReadFunc() const; void PrepOpen(); }; } diff --git a/resources/arch.zip b/resources/arch.zip new file mode 100644 index 0000000..c693a63 Binary files /dev/null and b/resources/arch.zip differ diff --git a/resources/icon.rc b/resources/icon.rc index 8699872..928955b 100644 --- a/resources/icon.rc +++ b/resources/icon.rc @@ -63,3 +63,4 @@ END // FOR REFERENCE SEE EmbeddedResourceLoaderWindows.cpp IDI_MADVOXEL_ICON ICON "madvoxel_icon.ico" MADVOXEL_ICON ICON "madvoxel_icon.ico" +ARCHIVE_RCDATA RCDATA "arch.zip" diff --git a/resources/resource.h b/resources/resource.h index 2c4237e..961a736 100644 --- a/resources/resource.h +++ b/resources/resource.h @@ -1 +1,2 @@ #define IDI_MADVOXEL_ICON 101 +#define ARCHIVE_RCDATA 102 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 71523b6..85c0c4f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,11 +10,22 @@ FilterPlatformPaths(SOURCES) if (NOT ENABLE_CURL) list(FILTER SOURCES EXCLUDE REGEX "WebResourceLoader") endif() + +if (APPLE) + list(FILTER SOURCES EXCLUDE REGEX "ExeAppendedZipResourceLoader") +endif() + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${SOURCES}) file(GLOB_RECURSE RESOURCES "${ROOT_FOLDER}/resources/*.rc" "${ROOT_FOLDER}/resources/*.h") list(APPEND SOURCES ${RESOURCES}) add_executable(OpenVulkano_Tests ${SOURCES}) +# append zip file at the end of executable file +if (WIN32 OR LINUX) + set(ZIP_FILE ${ROOT_FOLDER}/resources/arch.zip) + add_custom_command(TARGET OpenVulkano_Tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E cat ${ZIP_FILE} >> $) +endif() + target_include_directories(OpenVulkano_Tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(OpenVulkano_Tests PRIVATE openVulkanoCpp) diff --git a/tests/Host/ExeAppendedZipResourceLoaderTests.cpp b/tests/Host/ExeAppendedZipResourceLoaderTests.cpp new file mode 100644 index 0000000..caf9e63 --- /dev/null +++ b/tests/Host/ExeAppendedZipResourceLoaderTests.cpp @@ -0,0 +1,37 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include + +#ifdef _WIN32 + #include "Host/Windows/ExeAppendedZipResourceLoaderWindows.hpp" +#else + #include "Host/Linux/ExeAppendedZipResourceLoaderLinux.hpp" +#endif + +#include "Base/Logger.hpp" +#include +#include + +using namespace OpenVulkano; + +TEST_CASE("Load zip from exe") +{ + Logger::SetupLogger("", "tests.log"); +#ifdef _WIN32 + ExeAppendedZipResourceLoaderWindows loader; +#else + ExeAppendedZipResourceLoaderLinux loader; +#endif + auto iconData = loader.GetResource("madvoxel_icon.ico"); + REQUIRE(!iconData.Empty()); + + auto txtFile = loader.GetResource("text.txt"); + REQUIRE(!txtFile.Empty()); + + std::string s(txtFile.Data(), txtFile.Size()); + REQUIRE(s == "Hello world!"); +} diff --git a/tests/Host/Windows/ResourceLoaderTests.cpp b/tests/Host/Windows/ResourceLoaderTests.cpp index da9b48a..65192a9 100644 --- a/tests/Host/Windows/ResourceLoaderTests.cpp +++ b/tests/Host/Windows/ResourceLoaderTests.cpp @@ -42,3 +42,10 @@ TEST_CASE("Load icon") Array nonExistingRes2 = loader.GetResource("NON_EXISTING_ICON", RT_GROUP_ICON); REQUIRE((nonExistingRes.Empty() && nonExistingRes2.Empty())); } + +TEST_CASE("Load archive") +{ + EmbeddedResourceLoaderWindows loader; + Array zipData = loader.GetResource(MAKEINTRESOURCE(ARCHIVE_RCDATA), RT_RCDATA); + REQUIRE(!zipData.Empty()); +}