Merge pull request 'Appended zip executable loader' (#177) from misc into master

Reviewed-on: https://git.madvoxel.net/OpenVulkano/OpenVulkano/pulls/177
Reviewed-by: Georg Hagen <georg.hagen@madvoxel.com>
This commit is contained in:
Oleksii_Hyzha
2024-12-23 11:10:31 +01:00
17 changed files with 310 additions and 16 deletions

52
.vscode/tasks.json vendored Normal file
View File

@@ -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
}
}
]
}

View File

@@ -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 <filesystem>
#include <fstream>
namespace OpenVulkano
{
Array<char> ExeAppendedZipResourceLoader::GetResource(const std::string& resourceName)
{
ArchiveReader reader(GetCurrentExecutablePath(), nullptr, ArchiveType::ZIP);
if (auto data = reader.GetFile(resourceName))
{
return data->second;
}
return {};
}
}

View File

@@ -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 <variant>
#include <vector>
namespace OpenVulkano
{
class ExeAppendedZipResourceLoader : public ResourceLoader
{
public:
std::string GetResourcePath(const std::string& resourceName) final { return ""; }
Array<char> GetResource(const std::string& resourceName) override;
protected:
virtual std::string GetCurrentExecutablePath() const = 0;
};
}

View File

@@ -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 <unistd.h>
#include <climits>
namespace OpenVulkano
{
namespace
{
void* HANDLE = ResourceLoader::RegisterResourceLoader(std::make_unique<ExeAppendedZipResourceLoaderLinux>());
}
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<ssize_t>(0, sz));
return path;
}
}

View File

@@ -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;
};
}

View File

@@ -4,6 +4,8 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
#pragma once
#include "Data/Containers/Array.hpp" #include "Data/Containers/Array.hpp"
#include <memory> #include <memory>
#include <string> #include <string>

View File

@@ -52,7 +52,7 @@ namespace OpenVulkano
std::string EmbeddedResourceLoaderWindows::GetResourceName(const char* resId) 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 // https://stackoverflow.com/questions/3610565/why-does-makeintresource-work
// first 64KB // first 64KB
uintptr_t ptrT = reinterpret_cast<uintptr_t>(resId); uintptr_t ptrT = reinterpret_cast<uintptr_t>(resId);

View File

@@ -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 <Windows.h>
namespace OpenVulkano
{
namespace
{
void* HANDLE = ResourceLoader::RegisterResourceLoader(std::make_unique<ExeAppendedZipResourceLoaderWindows>());
}
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;
}
}

View File

@@ -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;
};
}

View File

@@ -20,22 +20,22 @@ namespace OpenVulkano
constexpr int BUFFER_SIZE = 16384; constexpr int BUFFER_SIZE = 16384;
} }
ArchiveReader::ArchiveReader(const std::shared_ptr<spdlog::logger>& logger) ArchiveReader::ArchiveReader(const std::shared_ptr<spdlog::logger>& logger, std::optional<ArchiveType::Type> archiveType)
: ArchiveBase(archive_read_new(), nullptr, logger) : ArchiveBase(archive_read_new(), nullptr, logger), m_archiveType(archiveType)
{} {}
ArchiveReader::ArchiveReader(const std::string& archiveFile, const std::shared_ptr<spdlog::logger>& logger) ArchiveReader::ArchiveReader(const std::string& archiveFile, const std::shared_ptr<spdlog::logger>& logger, std::optional<ArchiveType::Type> archiveType)
: ArchiveReader(archiveFile.c_str(), logger) : ArchiveReader(archiveFile.c_str(), logger, archiveType)
{} {}
ArchiveReader::ArchiveReader(const char* archiveFile, const std::shared_ptr<spdlog::logger>& logger) ArchiveReader::ArchiveReader(const char* archiveFile, const std::shared_ptr<spdlog::logger>& logger, std::optional<ArchiveType::Type> archiveType)
: ArchiveReader(logger) : ArchiveReader(logger, archiveType)
{ {
Open(archiveFile); Open(archiveFile);
} }
ArchiveReader::ArchiveReader(const void* archiveBuffer, const size_t size, const std::shared_ptr<spdlog::logger>& logger) ArchiveReader::ArchiveReader(const void* archiveBuffer, const size_t size, const std::shared_ptr<spdlog::logger>& logger, std::optional<ArchiveType::Type> archiveType)
: ArchiveReader(logger) : ArchiveReader(logger, archiveType)
{ {
OpenMemory(archiveBuffer, size); OpenMemory(archiveBuffer, size);
} }
@@ -51,7 +51,7 @@ namespace OpenVulkano
m_archive = archive_read_new(); m_archive = archive_read_new();
} }
ChkErr(archive_read_support_filter_all(m_archive)); ChkErr(archive_read_support_filter_all(m_archive));
ChkErr(archive_read_support_format_all(m_archive)); ChkErr(GetCurrentFormatReadFunc()(m_archive));
m_open = true; m_open = true;
} }
@@ -156,6 +156,33 @@ namespace OpenVulkano
} }
} }
std::function<int(archive*)> 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 ArchiveReader::ExtractRemaining(std::string_view targetDir)
{ {
size_t count = 0; size_t count = 0;
@@ -263,6 +290,22 @@ namespace OpenVulkano
return std::nullopt; return std::nullopt;
} }
std::optional<std::pair<FileDescription, Array<char>>> 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<void(const FileDescription&, std::istream&)>& streamReader) bool ArchiveReader::GetNextFileAsStream(const std::function<void(const FileDescription&, std::istream&)>& streamReader)
{ {
if (SkipTill(std::filesystem::file_type::regular)) if (SkipTill(std::filesystem::file_type::regular))

View File

@@ -8,6 +8,7 @@
#include "ArchiveBase.hpp" #include "ArchiveBase.hpp"
#include "Data/Containers/Array.hpp" #include "Data/Containers/Array.hpp"
#include "ArchiveType.hpp"
#include <string_view> #include <string_view>
#include <optional> #include <optional>
#include <functional> #include <functional>
@@ -20,17 +21,17 @@ namespace OpenVulkano
{ {
bool m_open = false; bool m_open = false;
bool m_eof = false; bool m_eof = false;
std::optional<ArchiveType::Type> m_archiveType;
std::queue<std::string> m_archivesToRead; std::queue<std::string> m_archivesToRead;
public: public:
explicit ArchiveReader(const std::shared_ptr<spdlog::logger>& logger = nullptr); explicit ArchiveReader(const std::shared_ptr<spdlog::logger>& logger = nullptr, std::optional<ArchiveType::Type> archiveType = std::nullopt);
explicit ArchiveReader(const char* archiveFile, const std::shared_ptr<spdlog::logger>& logger = nullptr); explicit ArchiveReader(const char* archiveFile, const std::shared_ptr<spdlog::logger>& logger = nullptr, std::optional<ArchiveType::Type> archiveType = std::nullopt);
explicit ArchiveReader(const std::string& archiveFile, const std::shared_ptr<spdlog::logger>& logger = nullptr); explicit ArchiveReader(const std::string& archiveFile, const std::shared_ptr<spdlog::logger>& logger = nullptr, std::optional<ArchiveType::Type> archiveType = std::nullopt);
ArchiveReader(const void* archiveBuffer, size_t size, const std::shared_ptr<spdlog::logger>& logger = nullptr); ArchiveReader(const void* archiveBuffer, size_t size, const std::shared_ptr<spdlog::logger>& logger = nullptr, std::optional<ArchiveType::Type> archiveType = std::nullopt);
~ArchiveReader() override; ~ArchiveReader() override;
@@ -65,13 +66,15 @@ namespace OpenVulkano
std::optional<std::pair<FileDescription, std::vector<char>>> GetNextFileAsVector(); std::optional<std::pair<FileDescription, std::vector<char>>> GetNextFileAsVector();
std::optional<std::pair<FileDescription, Array<char>>> GetFile(const std::string& path);
std::optional<FileDescription> StreamNextFile(std::ostream& stream); std::optional<FileDescription> StreamNextFile(std::ostream& stream);
bool GetNextFileAsStream(const std::function<void(const FileDescription&, std::istream&)>& streamReader); bool GetNextFileAsStream(const std::function<void(const FileDescription&, std::istream&)>& streamReader);
private: private:
void ReadNextHeader(); void ReadNextHeader();
std::function<int(archive*)> GetCurrentFormatReadFunc() const;
void PrepOpen(); void PrepOpen();
}; };
} }

BIN
resources/arch.zip Normal file

Binary file not shown.

View File

@@ -63,3 +63,4 @@ END
// FOR REFERENCE SEE EmbeddedResourceLoaderWindows.cpp // FOR REFERENCE SEE EmbeddedResourceLoaderWindows.cpp
IDI_MADVOXEL_ICON ICON "madvoxel_icon.ico" IDI_MADVOXEL_ICON ICON "madvoxel_icon.ico"
MADVOXEL_ICON ICON "madvoxel_icon.ico" MADVOXEL_ICON ICON "madvoxel_icon.ico"
ARCHIVE_RCDATA RCDATA "arch.zip"

View File

@@ -1 +1,2 @@
#define IDI_MADVOXEL_ICON 101 #define IDI_MADVOXEL_ICON 101
#define ARCHIVE_RCDATA 102

View File

@@ -10,11 +10,22 @@ FilterPlatformPaths(SOURCES)
if (NOT ENABLE_CURL) if (NOT ENABLE_CURL)
list(FILTER SOURCES EXCLUDE REGEX "WebResourceLoader") list(FILTER SOURCES EXCLUDE REGEX "WebResourceLoader")
endif() endif()
if (APPLE)
list(FILTER SOURCES EXCLUDE REGEX "ExeAppendedZipResourceLoader")
endif()
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${SOURCES}) source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${SOURCES})
file(GLOB_RECURSE RESOURCES "${ROOT_FOLDER}/resources/*.rc" "${ROOT_FOLDER}/resources/*.h") file(GLOB_RECURSE RESOURCES "${ROOT_FOLDER}/resources/*.rc" "${ROOT_FOLDER}/resources/*.h")
list(APPEND SOURCES ${RESOURCES}) list(APPEND SOURCES ${RESOURCES})
add_executable(OpenVulkano_Tests ${SOURCES}) 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} >> $<TARGET_FILE:OpenVulkano_Tests>)
endif()
target_include_directories(OpenVulkano_Tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(OpenVulkano_Tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_include_directories(OpenVulkano_Tests PRIVATE openVulkanoCpp) target_include_directories(OpenVulkano_Tests PRIVATE openVulkanoCpp)

View File

@@ -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 <catch2/catch_all.hpp>
#ifdef _WIN32
#include "Host/Windows/ExeAppendedZipResourceLoaderWindows.hpp"
#else
#include "Host/Linux/ExeAppendedZipResourceLoaderLinux.hpp"
#endif
#include "Base/Logger.hpp"
#include <filesystem>
#include <fstream>
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!");
}

View File

@@ -42,3 +42,10 @@ TEST_CASE("Load icon")
Array<char> nonExistingRes2 = loader.GetResource("NON_EXISTING_ICON", RT_GROUP_ICON); Array<char> nonExistingRes2 = loader.GetResource("NON_EXISTING_ICON", RT_GROUP_ICON);
REQUIRE((nonExistingRes.Empty() && nonExistingRes2.Empty())); REQUIRE((nonExistingRes.Empty() && nonExistingRes2.Empty()));
} }
TEST_CASE("Load archive")
{
EmbeddedResourceLoaderWindows loader;
Array<char> zipData = loader.GetResource(MAKEINTRESOURCE(ARCHIVE_RCDATA), RT_RCDATA);
REQUIRE(!zipData.Empty());
}