diff --git a/3rdParty/CMakeLists.txt b/3rdParty/CMakeLists.txt index cc7e174..c439368 100644 --- a/3rdParty/CMakeLists.txt +++ b/3rdParty/CMakeLists.txt @@ -20,3 +20,4 @@ add_subdirectory(libstud-uuid) add_subdirectory(rapidyaml) add_subdirectory(libarchive) add_subdirectory(libjpeg-turbo) +add_subdirectory(curl) diff --git a/3rdParty/curl/CMakeLists.txt b/3rdParty/curl/CMakeLists.txt new file mode 100644 index 0000000..48a28ba --- /dev/null +++ b/3rdParty/curl/CMakeLists.txt @@ -0,0 +1,42 @@ +include(../../cmake/Utils.cmake) + +set(CURL_DEPS_INSTALL ${CMAKE_BINARY_DIR}/deps_curl) +find_package(CURL QUIET) +if (NOT ${CURL_FOUND}) + file(MAKE_DIRECTORY ${CURL_DEPS_INSTALL}) + execute_process( + COMMAND ${CMAKE_COMMAND} -G ${CMAKE_GENERATOR} -DTOOLCHAIN_FILE=${TOOLCHAIN_FILE} ${CMAKE_CURRENT_SOURCE_DIR}/ext -DCURL_REPO=${CURL_REPO} -DOPENSSL_REPO=${OPENSSL_REPO} + WORKING_DIRECTORY ${CURL_DEPS_INSTALL} + ) + execute_process( + COMMAND ${CMAKE_COMMAND} --build ${CURL_DEPS_INSTALL} + RESULT_VARIABLE build_result + ) + if (NOT ${build_result} EQUAL "0") + message(FATAL_ERROR "Failed to build curl!") + endif() + + list(APPEND CMAKE_PREFIX_PATH ${CURL_DEPS_INSTALL}/INSTALL) +else () + message("Using system curl") + set(USING_SYSTEM_CURL ON PARENT_SCOPE) +endif () + +function(LinkCurl TARGET BINARY_DIR) + find_package(CURL QUIET) + if (NOT ${CURL_FOUND}) + set(CURL_DEPS_INSTALL ${BINARY_DIR}/deps_curl) + target_include_directories(${TARGET} PRIVATE ${CURL_DEPS_INSTALL}/INSTALL/include) + target_link_directories(${TARGET} PRIVATE ${CURL_DEPS_INSTALL}/INSTALL/lib) + if (MSVC) + target_link_libraries(${TARGET} PRIVATE libcrypto.lib libssl.lib libcurl_imp.lib) + elseif (APPLE) + target_link_libraries(${TARGET} PRIVATE curl) + else () + target_link_libraries(${TARGET} PRIVATE crypto ssl curl) + endif () + else () + target_include_directories(${TARGET} PRIVATE ${CURL_INCLUDE_DIR}) + target_link_libraries(${TARGET} PRIVATE CURL::libcurl) + endif () +endfunction() \ No newline at end of file diff --git a/3rdParty/curl/ext/CMakeLists.txt b/3rdParty/curl/ext/CMakeLists.txt new file mode 100644 index 0000000..b018e0c --- /dev/null +++ b/3rdParty/curl/ext/CMakeLists.txt @@ -0,0 +1,99 @@ +cmake_minimum_required(VERSION 3.16) +project(build_curl) +include(ExternalProject) +include(FetchContent) + +if (NOT DEFINED CURL_REPO OR CURL_REPO STREQUAL "") + set(CURL_REPO https://github.com/curl/curl.git) +endif () +if (NOT DEFINED OPENSSL_REPO OR OPENSSL_REPO STREQUAL "") + set(OPENSSL_REPO https://github.com/openssl/openssl.git) +endif () + +set(OPENSSL_SOURCE_DIR ${CMAKE_BINARY_DIR}/deps_curl/openssl-src) +set(OPENSSL_INSTALL_DIR ${CMAKE_BINARY_DIR}/INSTALL) +set(OPENSSL_INCLUDE_DIR ${OPENSSL_INSTALL_DIR}/include) +set(OPENSSL_TAG OpenSSL_1_1_1n) + +find_program(PERL_EXECUTABLE perl) +if (NOT PERL_EXECUTABLE) + message(FATAL_ERROR "Perl not found. Please install Perl to configure OpenSSL.") +endif() + +if (MSVC) + set(OPENSSL_PERL_CONFIGURE_COMMAND ${OPENSSL_SOURCE_DIR}/src/openssl/Configure VC-WIN64A) + set(OPENSSL_MAKE_COMMAND nmake) +else () + set(OPENSSL_PERL_CONFIGURE_COMMAND ${OPENSSL_SOURCE_DIR}/src/OpenSSL/config --static -static -fPIC) + set(OPENSSL_MAKE_COMMAND make) +endif () + +set(CURL_GIT_TAG curl-8_8_0) + +if (APPLE) + ExternalProject_Add( + curl + GIT_REPOSITORY ${CURL_REPO} + GIT_TAG ${CURL_GIT_TAG} + GIT_SHALLOW TRUE + SOURCE_DIR ${CMAKE_BINARY_DIR}/curl + BINARY_DIR ${CMAKE_BINARY_DIR}/curl-build + CMAKE_GENERATOR ${CMAKE_GENERATOR} + CMAKE_ARGS + -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${TOOLCHAIN_FILE} + -DCMAKE_BUILD_TYPE:STRING=Release + -DCURL_USE_OPENSSL:BOOL=ON + -DCMAKE_PREFIX_PATH=${CMAKE_BINARY_DIR}/INSTALL + -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_BINARY_DIR}/INSTALL + BUILD_COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}/curl-build --config Release + INSTALL_COMMAND ${CMAKE_COMMAND} --install ${CMAKE_BINARY_DIR}/curl-build --config Release + USES_TERMINAL_DOWNLOAD TRUE + USES_TERMINAL_CONFIGURE TRUE + USES_TERMINAL_BUILD TRUE + USES_TERMINAL_INSTALL TRUE + ) +else () + ExternalProject_Add( + OpenSSL + PREFIX ${OPENSSL_SOURCE_DIR} + GIT_REPOSITORY ${OPENSSL_REPO} + GIT_TAG ${OPENSSL_TAG} + GIT_SHALLOW TRUE + CONFIGURE_COMMAND ${PERL_EXECUTABLE} ${OPENSSL_PERL_CONFIGURE_COMMAND} --prefix=${OPENSSL_INSTALL_DIR} --openssldir=${OPENSSL_INSTALL_DIR} + BUILD_COMMAND ${OPENSSL_MAKE_COMMAND} + INSTALL_COMMAND ${OPENSSL_MAKE_COMMAND} install + INSTALL_DIR ${OPENSSL_INSTALL_DIR} + USES_TERMINAL_DOWNLOAD TRUE + USES_TERMINAL_CONFIGURE TRUE + USES_TERMINAL_BUILD TRUE + USES_TERMINAL_INSTALL TRUE + ) + + ExternalProject_Get_Property(OpenSSL INSTALL_DIR) + set(OPENSSL_ROOT_DIR ${INSTALL_DIR}) + + ExternalProject_Add( + curl + DEPENDS OpenSSL + GIT_REPOSITORY ${CURL_REPO} + GIT_TAG ${CURL_GIT_TAG} + GIT_SHALLOW TRUE + SOURCE_DIR ${CMAKE_BINARY_DIR}/curl + BINARY_DIR ${CMAKE_BINARY_DIR}/curl-build + CMAKE_GENERATOR ${CMAKE_GENERATOR} + CMAKE_ARGS + -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${TOOLCHAIN_FILE} + -DCMAKE_BUILD_TYPE:STRING=Release + -DCURL_USE_OPENSSL:BOOL=ON + -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR} + -DOPENSSL_INCLUDE_DIR=${OPENSSL_INCLUDE_DIR} + -DCMAKE_PREFIX_PATH=${CMAKE_BINARY_DIR}/INSTALL + -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_BINARY_DIR}/INSTALL + BUILD_COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}/curl-build --config Release + INSTALL_COMMAND ${CMAKE_COMMAND} --install ${CMAKE_BINARY_DIR}/curl-build --config Release + USES_TERMINAL_DOWNLOAD TRUE + USES_TERMINAL_CONFIGURE TRUE + USES_TERMINAL_BUILD TRUE + USES_TERMINAL_INSTALL TRUE + ) +endif () \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index beee030..e12a28a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,7 @@ endif() list(APPEND CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR}/deps/INSTALL) target_link_libraries(openVulkanoCpp PRIVATE magic_enum yaml-cpp fmt spdlog glm pugixml stb eigen utf8cpp imgui_internal TracyClient stud-uuid ryml) +LinkCurl(openVulkanoCpp ${CMAKE_BINARY_DIR}) add_compile_definitions(LIBARCHIVE_STATIC) add_compile_definitions(NOMINMAX) diff --git a/openVulkanoCpp/Host/WebResourceLoader.cpp b/openVulkanoCpp/Host/WebResourceLoader.cpp new file mode 100644 index 0000000..91f6e94 --- /dev/null +++ b/openVulkanoCpp/Host/WebResourceLoader.cpp @@ -0,0 +1,92 @@ +/* + * 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 "WebResourceLoader.hpp" +#include "IO/AppFolders.hpp" +#include "Base/Utils.hpp" +#include "Base/Logger.hpp" + +#include +#include +#include + +namespace OpenVulkano +{ + namespace + { + size_t CurlDownloader(void* contents, size_t size, size_t memBlockCount, void* userData) + { + size_t totalSize = size * memBlockCount; + std::vector* buffer = static_cast*>(userData); + buffer->insert(buffer->end(), static_cast(contents), static_cast(contents) + totalSize); + return totalSize; + } + } + + bool WebResourceLoader::IsUrl(const std::string& str) + { + return str.find("http://") == 0 || str.find("https://") == 0 || str.find("ftp://") == 0; + } + + WebResourceLoader::WebResourceLoader() + { + m_cacheDirectory = AppFolders::GetAppCacheDir() / "resources"; + std::filesystem::create_directories(m_cacheDirectory); + curl_global_init(CURL_GLOBAL_DEFAULT); + } + + WebResourceLoader::~WebResourceLoader() { curl_global_cleanup(); } + + std::filesystem::path WebResourceLoader::GetCacheFilePath(const std::string& url) + { + size_t hash = std::hash {}(url); + std::string hashedUrl = std::to_string(hash); + + return m_cacheDirectory / hashedUrl; + } + + Array WebResourceLoader::DownloadResource(const std::string& url) + { + std::vector buffer; + + CURL* curl = curl_easy_init(); + if (curl) + { + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &CurlDownloader); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); + +#ifdef __APPLE__ + curl_easy_setopt(curl, CURLOPT_SSLENGINE, "DarwinSSL"); +#endif + + CURLcode result = curl_easy_perform(curl); + if (result != CURLE_OK) + { + curl_easy_cleanup(curl); + Logger::APP->error("Failed to download resource: '" + url + "' - " + curl_easy_strerror(result)); + return Array(); + } + curl_easy_cleanup(curl); + } + + std::filesystem::path cacheFilePath = GetCacheFilePath(url); + std::ofstream file(cacheFilePath, std::ios::binary); + file.write(buffer.data(), buffer.size()); + return Array(buffer); + } + + Array WebResourceLoader::GetResource(const std::string& resourceName) + { + if (IsUrl(resourceName)) + { + std::filesystem::path cacheFilePath = GetCacheFilePath(resourceName); + if (std::filesystem::exists(cacheFilePath)) { return Utils::ReadFile(cacheFilePath.string()); } + else { return DownloadResource(resourceName); } + } + return Array(); + } +} \ No newline at end of file diff --git a/openVulkanoCpp/Host/WebResourceLoader.hpp b/openVulkanoCpp/Host/WebResourceLoader.hpp new file mode 100644 index 0000000..5159e85 --- /dev/null +++ b/openVulkanoCpp/Host/WebResourceLoader.hpp @@ -0,0 +1,29 @@ +/* + * 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 "ResourceLoader.hpp" + +#include +#include + +namespace OpenVulkano +{ + class WebResourceLoader : public ResourceLoader + { + std::filesystem::path m_cacheDirectory; + + std::filesystem::path GetCacheFilePath(const std::string& url); + Array DownloadResource(const std::string& url); + + public: + static bool IsUrl(const std::string& str); + + WebResourceLoader(); + ~WebResourceLoader(); + Array GetResource(const std::string& resourceName) override; + }; +} \ No newline at end of file