diff --git a/CMakeLists.txt b/CMakeLists.txt index 0eeff0ca..20286804 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ project(Jazz2 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) get_filename_component(PARENT_SOURCE_DIR ${CMAKE_SOURCE_DIR} DIRECTORY) +set_property(GLOBAL PROPERTY USE_FOLDERS ON) if(NOT CMAKE_GENERATOR_PLATFORM OR "${CMAKE_GENERATOR_PLATFORM}" STREQUAL "${CMAKE_SYSTEM_PROCESSOR}") if(EMSCRIPTEN) @@ -62,17 +63,16 @@ else() endif() include(ncine_options) +include(ncine_get_version) +include(ncine_imported_targets) +include(ncine_imgui) +include(ncine_tracy) if(NOT IS_DIRECTORY ${NCINE_DATA_DIR}) message(WARNING "Content directory not found at: ${NCINE_DATA_DIR}") else() message(STATUS "Content directory: ${NCINE_DATA_DIR}") endif() - -include(ncine_get_version) -include(ncine_imported_targets) -include(ncine_imgui) -include(ncine_tracy) if(NCINE_BUILD_ANDROID) include(ncine_generated_sources) @@ -81,6 +81,7 @@ if(NCINE_BUILD_ANDROID) endif() add_executable(${NCINE_APP}) + if(WINDOWS_PHONE OR WINDOWS_STORE) message(STATUS "Compiling for Windows RT") else() @@ -109,30 +110,7 @@ include(ncine_extra_sources) include(ncine_generated_sources) # Organize main project files into folders -set_property(GLOBAL PROPERTY USE_FOLDERS ON) -set(PATH_PREFIX ${NCINE_SOURCE_DIR}) -foreach(FILE ${HEADERS} ${SOURCES}) - get_filename_component(PARENT_DIR "${FILE}" DIRECTORY) - string(LENGTH "${PATH_PREFIX}" PATH_PREFIX_LENGTH) - string(SUBSTRING "${PARENT_DIR}" 0 ${PATH_PREFIX_LENGTH} PATH_START) - if(PATH_START STREQUAL "${PATH_PREFIX}") - string(SUBSTRING "${PARENT_DIR}" ${PATH_PREFIX_LENGTH} -1 GROUP) - string(REPLACE "/" "\\" GROUP "${GROUP}") - - # Group into "Source Files" and "Header Files" - if("${FILE}" MATCHES ".*\\.cpp") - set(GROUP "Source Files${GROUP}") - elseif("${FILE}" MATCHES ".*\\.h") - set(GROUP "Header Files${GROUP}") - endif() - else() - # Path doesn't have common prefix, put it into "Dependencies" - set(GROUP "Dependencies") - endif() - - source_group("${GROUP}" FILES "${FILE}") -endforeach() - +ncine_assign_source_group(PATH_PREFIX ${NCINE_SOURCE_DIR} FILES ${HEADERS} ${SOURCES}) foreach(SOURCE_FILE IN LISTS SHADER_FILES) source_group("Shaders" FILES ${SOURCE_FILE}) endforeach() diff --git a/Sources/Jazz2.vcxproj b/Sources/Jazz2.vcxproj index 5db60800..6df960ce 100644 --- a/Sources/Jazz2.vcxproj +++ b/Sources/Jazz2.vcxproj @@ -314,7 +314,9 @@ - + + + @@ -702,7 +704,6 @@ - @@ -729,7 +730,9 @@ - + + + @@ -1039,7 +1042,6 @@ - diff --git a/Sources/Jazz2.vcxproj.filters b/Sources/Jazz2.vcxproj.filters index 57ebb285..a83cdb74 100644 --- a/Sources/Jazz2.vcxproj.filters +++ b/Sources/Jazz2.vcxproj.filters @@ -232,6 +232,12 @@ {7de2dc9b-0c39-47b3-991e-9a71db70648a} + + {e87f0049-c3b9-47e3-98f4-c5a7f7817396} + + + {fbd151b2-7efb-45b9-a98b-582cf95d2a3b} + @@ -1413,9 +1419,6 @@ Header Files\Jazz2\UI - - Header Files\Shared\IO - Header Files\Shared\Containers @@ -1533,8 +1536,14 @@ Header Files\Shared\IO - - Header Files\Shared\IO + + Header Files\Shared\IO\Compression + + + Header Files\Shared\IO\Compression + + + Header Files\Shared\IO\Compression @@ -2456,9 +2465,6 @@ Source Files\Jazz2\UI - - Source Files\Shared\IO - Source Files\Shared\Containers @@ -2522,8 +2528,14 @@ Source Files\Shared\IO - - Source Files\Shared\IO + + Source Files\Shared\IO\Compression + + + Source Files\Shared\IO\Compression + + + Source Files\Shared\IO\Compression diff --git a/Sources/Jazz2/Compatibility/JJ2Block.cpp b/Sources/Jazz2/Compatibility/JJ2Block.cpp index f3fe3bcf..fd5df1c6 100644 --- a/Sources/Jazz2/Compatibility/JJ2Block.cpp +++ b/Sources/Jazz2/Compatibility/JJ2Block.cpp @@ -2,7 +2,9 @@ #include -#include +#include + +using namespace Death::IO::Compression; namespace Jazz2::Compatibility { diff --git a/Sources/Jazz2/Compatibility/JJ2Data.cpp b/Sources/Jazz2/Compatibility/JJ2Data.cpp index b991c892..16329a21 100644 --- a/Sources/Jazz2/Compatibility/JJ2Data.cpp +++ b/Sources/Jazz2/Compatibility/JJ2Data.cpp @@ -8,11 +8,12 @@ #include #include -#include #include +#include using namespace Death; using namespace Death::Containers::Literals; +using namespace Death::IO::Compression; using namespace nCine; namespace Jazz2::Compatibility diff --git a/Sources/Jazz2/Compatibility/JJ2Level.cpp b/Sources/Jazz2/Compatibility/JJ2Level.cpp index 884f52e8..825c2dfe 100644 --- a/Sources/Jazz2/Compatibility/JJ2Level.cpp +++ b/Sources/Jazz2/Compatibility/JJ2Level.cpp @@ -9,10 +9,11 @@ #include #include -#include #include +#include using namespace Death::IO; +using namespace Death::IO::Compression; namespace Jazz2::Compatibility { diff --git a/Sources/Jazz2/Compatibility/JJ2Tileset.cpp b/Sources/Jazz2/Compatibility/JJ2Tileset.cpp index 0e8354de..0f1809db 100644 --- a/Sources/Jazz2/Compatibility/JJ2Tileset.cpp +++ b/Sources/Jazz2/Compatibility/JJ2Tileset.cpp @@ -2,11 +2,12 @@ #include "JJ2Anims.h" #include "JJ2Block.h" -#include #include #include +#include using namespace Death::IO; +using namespace Death::IO::Compression; namespace Jazz2::Compatibility { diff --git a/Sources/Jazz2/ContentResolver.cpp b/Sources/Jazz2/ContentResolver.cpp index 737582db..e185a28a 100644 --- a/Sources/Jazz2/ContentResolver.cpp +++ b/Sources/Jazz2/ContentResolver.cpp @@ -25,12 +25,13 @@ #include #include -#include #include +#include #define SIMDJSON_EXCEPTIONS 0 #include "../simdjson/simdjson.h" +using namespace Death::IO::Compression; using namespace simdjson; template diff --git a/Sources/Jazz2/Multiplayer/MultiLevelHandler.cpp b/Sources/Jazz2/Multiplayer/MultiLevelHandler.cpp index 53612249..90db81c3 100644 --- a/Sources/Jazz2/Multiplayer/MultiLevelHandler.cpp +++ b/Sources/Jazz2/Multiplayer/MultiLevelHandler.cpp @@ -46,8 +46,9 @@ #include #include #include -#include +#include +using namespace Death::IO::Compression; using namespace nCine; namespace Jazz2::Multiplayer diff --git a/Sources/Jazz2/PreferencesCache.cpp b/Sources/Jazz2/PreferencesCache.cpp index 577bf63d..5404f4de 100644 --- a/Sources/Jazz2/PreferencesCache.cpp +++ b/Sources/Jazz2/PreferencesCache.cpp @@ -6,12 +6,13 @@ #include #include -#include #include #include +#include using namespace Death::Containers::Literals; using namespace Death::IO; +using namespace Death::IO::Compression; using namespace nCine; namespace Jazz2 diff --git a/Sources/Jazz2/UI/Cinematics.cpp b/Sources/Jazz2/UI/Cinematics.cpp index ed86af55..b6270d98 100644 --- a/Sources/Jazz2/UI/Cinematics.cpp +++ b/Sources/Jazz2/UI/Cinematics.cpp @@ -12,7 +12,7 @@ #include "../../nCine/Base/FrameTimer.h" #include -#include +#include namespace Jazz2::UI { diff --git a/Sources/Jazz2/UI/Cinematics.h b/Sources/Jazz2/UI/Cinematics.h index 9aa6833d..d77d4e02 100644 --- a/Sources/Jazz2/UI/Cinematics.h +++ b/Sources/Jazz2/UI/Cinematics.h @@ -12,10 +12,11 @@ #include "../../nCine/Audio/AudioStreamPlayer.h" #include "../../nCine/Input/InputEvents.h" -#include #include +#include using namespace Death::IO; +using namespace Death::IO::Compression; namespace Jazz2::UI { diff --git a/Sources/Jazz2/UI/Menu/CustomLevelSelectSection.cpp b/Sources/Jazz2/UI/Menu/CustomLevelSelectSection.cpp index 598cab0b..ebce7931 100644 --- a/Sources/Jazz2/UI/Menu/CustomLevelSelectSection.cpp +++ b/Sources/Jazz2/UI/Menu/CustomLevelSelectSection.cpp @@ -10,10 +10,11 @@ # include "CreateServerOptionsSection.h" #endif -#include #include +#include using namespace Death::IO; +using namespace Death::IO::Compression; using namespace Jazz2::UI::Menu::Resources; namespace Jazz2::UI::Menu diff --git a/Sources/Jazz2/UI/Menu/HighscoresSection.cpp b/Sources/Jazz2/UI/Menu/HighscoresSection.cpp index a55e8a79..2777f5b3 100644 --- a/Sources/Jazz2/UI/Menu/HighscoresSection.cpp +++ b/Sources/Jazz2/UI/Menu/HighscoresSection.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #if defined(DEATH_TARGET_ANDROID) # include "../../../nCine/Backends/Android/AndroidApplication.h" @@ -16,6 +16,7 @@ # include #endif +using namespace Death::IO::Compression; using namespace Jazz2::UI::Menu::Resources; namespace Jazz2::UI::Menu diff --git a/Sources/Main.cpp b/Sources/Main.cpp index 9467a446..889d8f98 100644 --- a/Sources/Main.cpp +++ b/Sources/Main.cpp @@ -60,9 +60,9 @@ using namespace Jazz2::Multiplayer; #include #include #include -#include #include #include +#include #if !defined(DEATH_DEBUG) # include diff --git a/Sources/Shared/IO/DeflateStream.cpp b/Sources/Shared/IO/Compression/DeflateStream.cpp similarity index 99% rename from Sources/Shared/IO/DeflateStream.cpp rename to Sources/Shared/IO/Compression/DeflateStream.cpp index f643499c..560da4dd 100644 --- a/Sources/Shared/IO/DeflateStream.cpp +++ b/Sources/Shared/IO/Compression/DeflateStream.cpp @@ -1,5 +1,5 @@ #include "DeflateStream.h" -#include "../Asserts.h" +#include "../../Asserts.h" #if !defined(WITH_ZLIB) && !defined(WITH_MINIZ) # pragma message("Death::IO::DeflateStream requires `zlib` or `miniz` library") @@ -18,7 +18,7 @@ #include #include -namespace Death { namespace IO { +namespace Death { namespace IO { namespace Compression { //###==##====#=====--==~--~=~- --- -- - - - - DeflateStream::DeflateStream() @@ -397,6 +397,6 @@ namespace Death { namespace IO { return uncompressedSize + (5 * maxBlocks) + 1 + 8; } -}} +}}} #endif \ No newline at end of file diff --git a/Sources/Shared/IO/DeflateStream.h b/Sources/Shared/IO/Compression/DeflateStream.h similarity index 96% rename from Sources/Shared/IO/DeflateStream.h rename to Sources/Shared/IO/Compression/DeflateStream.h index 28d9deda..419ed5d0 100644 --- a/Sources/Shared/IO/DeflateStream.h +++ b/Sources/Shared/IO/Compression/DeflateStream.h @@ -1,7 +1,7 @@ #pragma once -#include "../Common.h" -#include "Stream.h" +#include "../../Common.h" +#include "../Stream.h" #if defined(WITH_ZLIB) || defined(WITH_MINIZ) @@ -29,7 +29,7 @@ # endif #endif -namespace Death { namespace IO { +namespace Death { namespace IO { namespace Compression { //###==##====#=====--==~--~=~- --- -- - - - - /** @@ -131,6 +131,7 @@ namespace Death { namespace IO { std::int32_t WriteInternal(const void* buffer, std::int32_t bytesToWrite, bool finish); }; -}} + +}}} #endif \ No newline at end of file diff --git a/Sources/Shared/IO/Lz4Stream.cpp b/Sources/Shared/IO/Compression/Lz4Stream.cpp similarity index 92% rename from Sources/Shared/IO/Lz4Stream.cpp rename to Sources/Shared/IO/Compression/Lz4Stream.cpp index d5bae70f..442474e5 100644 --- a/Sources/Shared/IO/Lz4Stream.cpp +++ b/Sources/Shared/IO/Compression/Lz4Stream.cpp @@ -1,5 +1,5 @@ #include "Lz4Stream.h" -#include "../Asserts.h" +#include "../../Asserts.h" #if !defined(WITH_LZ4) # pragma message("Death::IO::Lz4Stream requires `lz4` library") @@ -18,7 +18,7 @@ #include #include -namespace Death { namespace IO { +namespace Death { namespace IO { namespace Compression { //###==##====#=====--==~--~=~- --- -- - - - - static std::int32_t GetLz4BlockSize(const LZ4F_frameInfo_t* info) @@ -198,9 +198,8 @@ namespace Death { namespace IO { auto result = LZ4F_createDecompressionContext(&_ctx, LZ4F_VERSION); if (LZ4F_isError(result)) { - //printf("LZ4F_getFrameInfo error: %s\n", LZ4F_getErrorName(fires)); + LOGE("LZ4F_createDecompressionContext() failed with error %0x%zx (%s)", result, LZ4F_getErrorName(result)); _state = State::Failed; - return; } } @@ -238,7 +237,7 @@ namespace Death { namespace IO { std::size_t consumedSize = bytesRead; std::size_t result = LZ4F_getFrameInfo(_ctx, &info, _inBuffer, &consumedSize); if (LZ4F_isError(result)) { - LOGE("LZ4F_getFrameInfo() failed with error %zu (%s)", result, LZ4F_getErrorName(result)); + LOGE("LZ4F_getFrameInfo() failed with error %0x%zx (%s)", result, LZ4F_getErrorName(result)); _state = State::Failed; return; } @@ -293,7 +292,7 @@ namespace Death { namespace IO { std::size_t srcSize = _inLength - _inPos; auto result = LZ4F_decompress(_ctx, &_outBuffer[0], &dstSize, &_inBuffer[_inPos], &srcSize, nullptr); if (LZ4F_isError(result)) { - LOGE("LZ4F_decompress() failed with error %zu (%s)", result, LZ4F_getErrorName(result)); + LOGE("LZ4F_decompress() failed with error %0x%zx (%s)", result, LZ4F_getErrorName(result)); _state = State::Failed; return Stream::Invalid; } @@ -337,7 +336,7 @@ namespace Death { namespace IO { { auto result = LZ4F_createCompressionContext(&_ctx, LZ4F_VERSION); if (LZ4F_isError(result)) { - LOGE("LZ4F_createCompressionContext() failed with error %zu (%s)", result, LZ4F_getErrorName(result)); + LOGE("LZ4F_createCompressionContext() failed with error %0x%zx (%s)", result, LZ4F_getErrorName(result)); _state = State::Failed; } @@ -354,7 +353,7 @@ namespace Death { namespace IO { std::size_t headerSize = LZ4F_compressBegin(_ctx, &_outBuffer[0], _outCapacity, &kPrefs); if (LZ4F_isError(result)) { - LOGE("LZ4F_compressBegin() failed with error %zu (%s)", headerSize, LZ4F_getErrorName(headerSize)); + LOGE("LZ4F_compressBegin() failed with error %0x%zx (%s)", headerSize, LZ4F_getErrorName(headerSize)); _state = State::Failed; } @@ -432,7 +431,7 @@ namespace Death { namespace IO { { std::size_t compressedSize = LZ4F_flush(_ctx, &_outBuffer[0], _outCapacity, nullptr); if (LZ4F_isError(compressedSize)) { - LOGE("LZ4F_flush() failed with error %zu (%s)", compressedSize, LZ4F_getErrorName(compressedSize)); + LOGE("LZ4F_flush() failed with error %0x%zx (%s)", compressedSize, LZ4F_getErrorName(compressedSize)); _state = State::Failed; return false; } @@ -472,7 +471,7 @@ namespace Death { namespace IO { std::size_t compressedSize = LZ4F_compressUpdate(_ctx, &_outBuffer[0], _outCapacity, buffer, bytesToWrite, nullptr); if (LZ4F_isError(compressedSize)) { - LOGE("LZ4F_compressUpdate() failed with error %zu (%s)", compressedSize, LZ4F_getErrorName(compressedSize)); + LOGE("LZ4F_compressUpdate() failed with error %0x%zx (%s)", compressedSize, LZ4F_getErrorName(compressedSize)); _state = State::Failed; return Stream::Invalid; } @@ -485,7 +484,7 @@ namespace Death { namespace IO { if (finish) { std::size_t compressedSize = LZ4F_compressEnd(_ctx, &_outBuffer[0], _outCapacity, nullptr); if (LZ4F_isError(compressedSize)) { - LOGE("LZ4F_compressEnd() failed with error %zu (%s)", compressedSize, LZ4F_getErrorName(compressedSize)); + LOGE("LZ4F_compressEnd() failed with error %0x%zx (%s)", compressedSize, LZ4F_getErrorName(compressedSize)); _state = State::Failed; return Stream::Invalid; } @@ -496,6 +495,6 @@ namespace Death { namespace IO { return bytesToWrite; } -}} +}}} #endif \ No newline at end of file diff --git a/Sources/Shared/IO/Lz4Stream.h b/Sources/Shared/IO/Compression/Lz4Stream.h similarity index 95% rename from Sources/Shared/IO/Lz4Stream.h rename to Sources/Shared/IO/Compression/Lz4Stream.h index 808db002..0eb3bd79 100644 --- a/Sources/Shared/IO/Lz4Stream.h +++ b/Sources/Shared/IO/Compression/Lz4Stream.h @@ -1,7 +1,7 @@ #pragma once -#include "../Common.h" -#include "Stream.h" +#include "../../Common.h" +#include "../Stream.h" #if defined(WITH_LZ4) @@ -16,7 +16,7 @@ # include #endif -namespace Death { namespace IO { +namespace Death { namespace IO { namespace Compression { //###==##====#=====--==~--~=~- --- -- - - - - /** @@ -79,7 +79,7 @@ namespace Death { namespace IO { }; /** - @brief Write-only streaming to compress written data by using the Deflate algorithm + @brief Write-only streaming to compress written data by using the LZ4 compression algorithm */ class Lz4Writer : public Stream { @@ -121,6 +121,7 @@ namespace Death { namespace IO { std::int32_t WriteInternal(const void* buffer, std::int32_t bytesToWrite, bool finish); }; -}} + +}}} #endif \ No newline at end of file diff --git a/Sources/Shared/IO/Compression/ZstdStream.cpp b/Sources/Shared/IO/Compression/ZstdStream.cpp new file mode 100644 index 00000000..b7758a8a --- /dev/null +++ b/Sources/Shared/IO/Compression/ZstdStream.cpp @@ -0,0 +1,432 @@ +#include "ZstdStream.h" +#include "../../Asserts.h" + +#if !defined(WITH_ZSTD) +# pragma message("Death::IO::ZstdStream requires `zstd` library") +#else + +#if defined(DEATH_TARGET_WINDOWS) && !defined(CMAKE_BUILD) +# if defined(_M_X64) +# pragma comment(lib, "../Libs/Windows/x64/zstd.lib") +# elif defined(_M_IX86) +# pragma comment(lib, "../Libs/Windows/x86/zstd.lib") +# else +# error Unsupported architecture +# endif +#endif + +#include +#include + +#ifdef __HAS_LOCAL_ZSTD +# include "zstd/zstd_errors.h" +#else +# include +#endif + +namespace Death { namespace IO { namespace Compression { +//###==##====#=====--==~--~=~- --- -- - - - - + + ZstdStream::ZstdStream() + : _inputStream(nullptr), _strm(nullptr), _size(Stream::Invalid), _inputSize(-1), _state(State::Unknown) + { + } + + ZstdStream::ZstdStream(Stream& inputStream, std::int32_t inputSize) + : ZstdStream() + { + Open(inputStream, inputSize); + } + + ZstdStream::~ZstdStream() + { + ZstdStream::Dispose(); + } + + ZstdStream::ZstdStream(ZstdStream&& other) noexcept + { + _inputStream = other._inputStream; + _strm = other._strm; + _inputSize = other._inputSize; + _state = other._state; + + _buffer = std::move(other._buffer); + _inBufferPos = other._inBufferPos; + _inBufferLength = other._inBufferLength; + _inBufferCapacity = other._inBufferCapacity; + _outBufferPos = other._outBufferPos; + _outPosTotal = other._outPosTotal; + _outBufferLength = other._outBufferLength; + _outBufferCapacity = other._outBufferCapacity; + + // Original instance will be disabled + if (other._state == State::Created || other._state == State::Initialized) { + other._state = State::Failed; + } + } + + ZstdStream& ZstdStream::operator=(ZstdStream&& other) noexcept + { + Dispose(); + + _inputStream = other._inputStream; + _strm = other._strm; + _inputSize = other._inputSize; + _state = other._state; + + _buffer = std::move(other._buffer); + _inBufferPos = other._inBufferPos; + _inBufferLength = other._inBufferLength; + _inBufferCapacity = other._inBufferCapacity; + _outBufferPos = other._outBufferPos; + _outPosTotal = other._outPosTotal; + _outBufferLength = other._outBufferLength; + _outBufferCapacity = other._outBufferCapacity; + + // Original instance will be disabled + if (other._state == State::Created || other._state == State::Initialized) { + other._state = State::Failed; + } + + return *this; + } + + std::int64_t ZstdStream::Seek(std::int64_t offset, SeekOrigin origin) + { + switch (origin) { + case SeekOrigin::Current: { + DEATH_ASSERT(offset >= 0, "Can't seek to negative values", Stream::OutOfRange); + + char buffer[4096]; + while (offset > 0) { + std::int64_t size = (offset > sizeof(buffer) ? sizeof(buffer) : offset); + std::int64_t bytesRead = Read(buffer, size); + if (bytesRead <= 0) { + return bytesRead; + } + offset -= bytesRead; + } + + return static_cast(_outPosTotal); + } + } + + return Stream::OutOfRange; + } + + std::int64_t ZstdStream::GetPosition() const + { + return static_cast(_outPosTotal); + } + + std::int64_t ZstdStream::Read(void* destination, std::int64_t bytesToRead) + { + if (bytesToRead <= 0) { + return 0; + } + + DEATH_ASSERT(destination != nullptr, "destination is null", 0); + std::uint8_t* typedBuffer = static_cast(destination); + std::int64_t bytesReadTotal = 0; + + do { + // ReadInternal() can read only up to ZSTD_DStreamInSize() bytes + std::int32_t partialBytesToRead = (bytesToRead < INT32_MAX ? (std::int32_t)bytesToRead : INT32_MAX); + std::int32_t bytesRead = ReadInternal(&typedBuffer[bytesReadTotal], partialBytesToRead); + if DEATH_UNLIKELY(bytesRead < 0) { + return bytesRead; + } else if DEATH_UNLIKELY(bytesRead == 0) { + break; + } + bytesReadTotal += bytesRead; + bytesToRead -= bytesRead; + } while (bytesToRead > 0); + + _outPosTotal += bytesReadTotal; + return bytesReadTotal; + } + + std::int64_t ZstdStream::Write(const void* source, std::int64_t bytesToWrite) + { + // Not supported + return Stream::Invalid; + } + + bool ZstdStream::Flush() + { + // Not supported + return true; + } + + bool ZstdStream::IsValid() + { + if (_state == State::Created) { + InitializeInternal(); + } + return (_state == State::Initialized || _state == State::Finished); + } + + std::int64_t ZstdStream::GetSize() const + { + return _size; + } + + void ZstdStream::Open(Stream& inputStream, std::int32_t inputSize) + { + Dispose(); + + _inputStream = &inputStream; + _inputSize = inputSize; + _state = State::Created; + _size = Stream::NotSeekable; + + _strm = ZSTD_createDStream(); + DEATH_DEBUG_ASSERT(_strm != nullptr); + + _inBufferPos = 0; + _inBufferLength = 0; + _inBufferCapacity = static_cast(ZSTD_DStreamInSize()); // Recommended input buffer size + _outBufferPos = 0; + _outPosTotal = 0; + _outBufferLength = 0; + _outBufferCapacity = static_cast(ZSTD_DStreamOutSize()); // Recommended output buffer size + } + + void ZstdStream::Dispose() + { + CeaseReading(); + + ZSTD_freeDStream(_strm); + _strm = nullptr; + + _inputStream = nullptr; + _state = State::Unknown; + _size = Stream::Invalid; + } + + void ZstdStream::InitializeInternal() + { + std::size_t result = ZSTD_initDStream(_strm); + if (ZSTD_isError(result)) { + LOGE("ZSTD_initDStream() failed with error 0x%zx (%s)", result, ZSTD_getErrorName(result)); + _state = State::Failed; + } + + if (_buffer == nullptr) { + _buffer = std::make_unique(_inBufferCapacity + _outBufferCapacity); + } + + _state = State::Initialized; + } + + std::int32_t ZstdStream::ReadInternal(void* ptr, std::int32_t size) + { + if (_state == State::Created) { + InitializeInternal(); + } + if (size <= 0 || _state == State::Finished) { + return 0; + } + if (_state == State::Unknown || _state >= State::Failed) { + return Stream::Invalid; + } + + std::int32_t n = (_outBufferLength - _outBufferPos); + while (n == 0) { + if (_inBufferPos >= _inBufferLength) { + std::int32_t bytesRead = _inputSize; + if (bytesRead < 0 || bytesRead > _inBufferCapacity) { + bytesRead = _inBufferCapacity; + } + + bytesRead = (std::int32_t)_inputStream->Read(&_buffer[0], bytesRead); + if (bytesRead <= 0) { + return Stream::Invalid; + } + + if (_inputSize > 0) { + _inputSize -= bytesRead; + } + + _inBufferPos = 0; + } + + ZSTD_inBuffer inBuffer = { &_buffer[0], _inBufferCapacity, _inBufferLength }; + ZSTD_outBuffer outBuffer = { &_buffer[_inBufferCapacity], _outBufferCapacity, _outBufferLength }; + std::size_t result = ZSTD_decompressStream(_strm, &outBuffer, &inBuffer); + if (ZSTD_isError(result)) { + LOGE("ZSTD_decompressStream() failed with error 0x%zx (%s)", result, ZSTD_getErrorName(result)); + _state = State::Failed; + return Stream::Invalid; + } + + _inBufferLength = inBuffer.pos; + _outBufferLength = outBuffer.pos; + + n = (_outBufferLength - _outBufferPos); + } + + if (n > size) { + n = size; + } + std::memcpy(ptr, &_buffer[_inBufferCapacity + _outBufferPos], n); + _outBufferPos += n; + return n; + } + + bool ZstdStream::CeaseReading() + { + if (_state != State::Initialized) { + return true; + } + + std::int64_t seekToEnd = _inBufferLength - _inBufferPos; + if (seekToEnd != 0) { + _inputStream->Seek(seekToEnd, SeekOrigin::Current); + } + + _state = State::Finished; + _size = static_cast(_outPosTotal); + return true; + } + + ZstdWriter::ZstdWriter(Stream& outputStream, std::int32_t compressionLevel) + : _outputStream(&outputStream), _state(State::Created), _outBufferPos(0), _outBufferLength(0) + { + _strm = ZSTD_createCStream(); + DEATH_DEBUG_ASSERT(_strm != nullptr); + + std::size_t result = ZSTD_initCStream(_strm, compressionLevel); + if (ZSTD_isError(result)) { + LOGE("ZSTD_initCStream() failed with error 0x%zx (%s)", result, ZSTD_getErrorName(result)); + _state = State::Failed; + } + + _inBufferCapacity = static_cast(ZSTD_CStreamInSize()); // Recommended input buffer size + _outBufferCapacity = static_cast(ZSTD_CStreamOutSize()); // Recommended output buffer size + + _buffer = std::make_unique(_outBufferCapacity); + } + + ZstdWriter::~ZstdWriter() + { + ZstdWriter::Dispose(); + } + + void ZstdWriter::Dispose() + { + if (_state != State::Created && _state != State::Initialized) { + return; + } + + if (_state == State::Initialized) { + WriteInternal(nullptr, 0, true); + } + + ZSTD_freeCStream(_strm); + _strm = nullptr; + + _state = State::Finished; + } + + std::int64_t ZstdWriter::Seek(std::int64_t offset, SeekOrigin origin) + { + return Stream::NotSeekable; + } + + std::int64_t ZstdWriter::GetPosition() const + { + return Stream::NotSeekable; + } + + std::int64_t ZstdWriter::Read(void* destination, std::int64_t bytesToRead) + { + // Not supported + return Stream::Invalid; + } + + std::int64_t ZstdWriter::Write(const void* source, std::int64_t bytesToWrite) + { + if (bytesToWrite <= 0) { + return 0; + } + if (_state != State::Created && _state != State::Initialized) { + return Stream::Invalid; + } + + DEATH_ASSERT(source != nullptr, "source is null", 0); + const std::uint8_t* typedBuffer = static_cast(source); + std::int64_t bytesWrittenTotal = 0; + _state = State::Initialized; + + do { + std::int32_t partialBytesToWrite = (bytesToWrite < INT32_MAX ? (std::int32_t)bytesToWrite : INT32_MAX); + std::int32_t bytesWritten = WriteInternal(&typedBuffer[bytesWrittenTotal], partialBytesToWrite, false); + if DEATH_UNLIKELY(bytesWritten < 0) { + return bytesWritten; + } else if DEATH_UNLIKELY(bytesWritten == 0) { + break; + } + bytesWrittenTotal += bytesWritten; + bytesToWrite -= bytesWritten; + } while (bytesToWrite > 0); + + return bytesWrittenTotal; + } + + bool ZstdWriter::Flush() + { + return (WriteInternal(nullptr, 0, false) >= 0); + } + + bool ZstdWriter::IsValid() + { + return(_state != State::Failed && _outputStream->IsValid()); + } + + std::int64_t ZstdWriter::GetSize() const + { + return Stream::NotSeekable; + } + + std::int32_t ZstdWriter::WriteInternal(const void* buffer, std::int32_t bytesToWrite, bool finish) + { + if (bytesToWrite > 0) { + if (bytesToWrite > _inBufferCapacity) { + bytesToWrite = _inBufferCapacity; + } + + ZSTD_inBuffer inBuffer = { buffer, bytesToWrite, 0 }; + ZSTD_outBuffer outBuffer = { &_buffer[0], _outBufferCapacity, 0 }; + std::size_t result = ZSTD_compressStream(_strm, &outBuffer, &inBuffer); + if (ZSTD_isError(result)) { + LOGE("ZSTD_compressStream() failed with error %zx (%s)", result, ZSTD_getErrorName(result)); + _state = State::Failed; + return Stream::Invalid; + } + + if (outBuffer.pos > 0) { + _outputStream->Write(&_buffer[0], outBuffer.pos); + } + + bytesToWrite = inBuffer.pos; + } + + if (finish) { + ZSTD_outBuffer outBuffer = { &_buffer[0], _outBufferCapacity, 0 }; + std::size_t result = ZSTD_endStream(_strm, &outBuffer); + if (ZSTD_isError(result)) { + LOGE("ZSTD_endStream() failed with error %zx (%s)", result, ZSTD_getErrorName(result)); + _state = State::Failed; + return Stream::Invalid; + } + + _outputStream->Write(&_buffer[0], outBuffer.pos); + } + + return bytesToWrite; + } + +}}} + +#endif \ No newline at end of file diff --git a/Sources/Shared/IO/Compression/ZstdStream.h b/Sources/Shared/IO/Compression/ZstdStream.h new file mode 100644 index 00000000..058c3418 --- /dev/null +++ b/Sources/Shared/IO/Compression/ZstdStream.h @@ -0,0 +1,122 @@ +#pragma once + +#include "../../Common.h" +#include "../Stream.h" + +#if defined(WITH_ZSTD) + +#if !defined(CMAKE_BUILD) && defined(__has_include) +# if __has_include("zstd/zstd.h") +# define __HAS_LOCAL_ZSTD +# endif +#endif +#ifdef __HAS_LOCAL_ZSTD +# include "zstd/zstd.h" +#else +# include +#endif + +namespace Death { namespace IO { namespace Compression { +//###==##====#=====--==~--~=~- --- -- - - - - + + /** + @brief Read-only streaming of compressed data using the Zstandard compression algorithm + */ + class ZstdStream : public Stream + { + public: + ZstdStream(); + ZstdStream(Stream& inputStream, std::int32_t inputSize = -1); + ~ZstdStream(); + + ZstdStream(const ZstdStream&) = delete; + ZstdStream(ZstdStream&& other) noexcept; + ZstdStream& operator=(const ZstdStream&) = delete; + ZstdStream& operator=(ZstdStream&& other) noexcept; + + void Open(Stream& inputStream, std::int32_t inputSize = -1); + + void Dispose() override; + std::int64_t Seek(std::int64_t offset, SeekOrigin origin) override; + std::int64_t GetPosition() const override; + std::int64_t Read(void* destination, std::int64_t bytesToRead) override; + std::int64_t Write(const void* source, std::int64_t bytesToWrite) override; + bool Flush() override; + bool IsValid() override; + std::int64_t GetSize() const override; + + bool CeaseReading(); + + protected: + enum class State : std::uint8_t { + Unknown, + Created, + Initialized, + Finished, + Failed + }; + + Stream* _inputStream; + ZSTD_DStream* _strm; + std::int64_t _size; + std::int32_t _inputSize; + State _state; + + std::unique_ptr _buffer; + std::int32_t _inBufferPos; + std::int32_t _inBufferLength; + std::int32_t _inBufferCapacity; + std::int32_t _outBufferPos; + std::int32_t _outPosTotal; + std::int32_t _outBufferLength; + std::int32_t _outBufferCapacity; + + void InitializeInternal(); + std::int32_t ReadInternal(void* ptr, std::int32_t size); + }; + + /** + @brief Write-only streaming to compress written data by using the Zstandard compression algorithm + */ + class ZstdWriter : public Stream + { + public: + ZstdWriter(Stream& outputStream, std::int32_t compressionLevel = 0); + ~ZstdWriter(); + + ZstdWriter(const ZstdWriter&) = delete; + ZstdWriter& operator=(const ZstdWriter&) = delete; + + void Dispose() override; + std::int64_t Seek(std::int64_t offset, SeekOrigin origin) override; + std::int64_t GetPosition() const override; + std::int64_t Read(void* destination, std::int64_t bytesToRead) override; + std::int64_t Write(const void* source, std::int64_t bytesToWrite) override; + bool Flush() override; + bool IsValid() override; + std::int64_t GetSize() const override; + + protected: + enum class State : std::uint8_t { + Created, + Initialized, + Finished, + Failed + }; + + Stream* _outputStream; + ZSTD_CStream* _strm; + State _state; + + std::unique_ptr _buffer; + std::int32_t _inBufferCapacity; + std::int32_t _outBufferPos; + std::int32_t _outBufferLength; + std::int32_t _outBufferCapacity; + + std::int32_t WriteInternal(const void* buffer, std::int32_t bytesToWrite, bool finish); + }; + +}}} + +#endif \ No newline at end of file diff --git a/Sources/Shared/IO/PakFile.cpp b/Sources/Shared/IO/PakFile.cpp index c16ed968..23f45e67 100644 --- a/Sources/Shared/IO/PakFile.cpp +++ b/Sources/Shared/IO/PakFile.cpp @@ -1,8 +1,9 @@ #include "PakFile.h" #include "BoundedFileStream.h" -#include "DeflateStream.h" -#include "Lz4Stream.h" #include "FileSystem.h" +#include "Compression/DeflateStream.h" +#include "Compression/Lz4Stream.h" +#include "Compression/ZstdStream.h" #include "../Containers/GrowableArray.h" #include "../Containers/StringConcatenable.h" @@ -13,91 +14,18 @@ using namespace Death::Containers; namespace Death { namespace IO { //###==##====#=====--==~--~=~- --- -- - - - - -#if defined(WITH_ZLIB) || defined(WITH_MINIZ) - - class ZlibCompressedBoundedStream : public Stream - { - public: - ZlibCompressedBoundedStream(const String& path, std::uint64_t offset, std::uint32_t uncompressedSize, std::uint32_t compressedSize); - - ZlibCompressedBoundedStream(const ZlibCompressedBoundedStream&) = delete; - ZlibCompressedBoundedStream& operator=(const ZlibCompressedBoundedStream&) = delete; - - void Dispose() override; - std::int64_t Seek(std::int64_t offset, SeekOrigin origin) override; - std::int64_t GetPosition() const override; - std::int64_t Read(void* destination, std::int64_t bytesToRead) override; - std::int64_t Write(const void* source, std::int64_t bytesToWrite) override; - bool Flush() override; - bool IsValid() override; - std::int64_t GetSize() const override; - - private: - BoundedFileStream _underlyingStream; - DeflateStream _deflateStream; - std::int64_t _uncompressedSize; - }; - - ZlibCompressedBoundedStream::ZlibCompressedBoundedStream(const String& path, std::uint64_t offset, std::uint32_t uncompressedSize, std::uint32_t compressedSize) - : _underlyingStream(path, offset, compressedSize), _uncompressedSize(uncompressedSize) - { - _deflateStream.Open(_underlyingStream, static_cast(compressedSize)); - } - - void ZlibCompressedBoundedStream::Dispose() - { - _deflateStream.Dispose(); - _underlyingStream.Dispose(); - } - - std::int64_t ZlibCompressedBoundedStream::Seek(std::int64_t offset, SeekOrigin origin) - { - return _deflateStream.Seek(offset, origin); - } - - std::int64_t ZlibCompressedBoundedStream::GetPosition() const - { - return _deflateStream.GetPosition(); - } - - std::int64_t ZlibCompressedBoundedStream::Read(void* destination, std::int64_t bytesToRead) - { - return _deflateStream.Read(destination, bytesToRead); - } - - std::int64_t ZlibCompressedBoundedStream::Write(const void* source, std::int64_t bytesToWrite) - { - // Not supported - return Stream::Invalid; - } - - bool ZlibCompressedBoundedStream::Flush() - { - // Not supported - return true; - } +#if defined(WITH_ZLIB) || defined(WITH_MINIZ) || defined(WITH_LZ4) || defined(WITH_ZSTD) - bool ZlibCompressedBoundedStream::IsValid() - { - return _underlyingStream.IsValid() && _deflateStream.IsValid(); - } + using namespace Death::IO::Compression; - std::int64_t ZlibCompressedBoundedStream::GetSize() const - { - return _uncompressedSize; - } - -#endif - -#if defined(WITH_LZ4) - - class Lz4CompressedBoundedStream : public Stream + template + class CompressedBoundedStream : public Stream { public: - Lz4CompressedBoundedStream(const String& path, std::uint64_t offset, std::uint32_t uncompressedSize, std::uint32_t compressedSize); + CompressedBoundedStream(const String& path, std::uint64_t offset, std::uint32_t uncompressedSize, std::uint32_t compressedSize); - Lz4CompressedBoundedStream(const Lz4CompressedBoundedStream&) = delete; - Lz4CompressedBoundedStream& operator=(const Lz4CompressedBoundedStream&) = delete; + CompressedBoundedStream(const CompressedBoundedStream&) = delete; + CompressedBoundedStream& operator=(const CompressedBoundedStream&) = delete; void Dispose() override; std::int64_t Seek(std::int64_t offset, SeekOrigin origin) override; @@ -110,55 +38,64 @@ namespace Death { namespace IO { private: BoundedFileStream _underlyingStream; - Lz4Stream _lz4Stream; + T _compressedStream; std::int64_t _uncompressedSize; }; - Lz4CompressedBoundedStream::Lz4CompressedBoundedStream(const String& path, std::uint64_t offset, std::uint32_t uncompressedSize, std::uint32_t compressedSize) + template + CompressedBoundedStream::CompressedBoundedStream(const String& path, std::uint64_t offset, std::uint32_t uncompressedSize, std::uint32_t compressedSize) : _underlyingStream(path, offset, compressedSize), _uncompressedSize(uncompressedSize) { - _lz4Stream.Open(_underlyingStream, static_cast(compressedSize)); + _compressedStream.Open(_underlyingStream, static_cast(compressedSize)); } - void Lz4CompressedBoundedStream::Dispose() + template + void CompressedBoundedStream::Dispose() { - _lz4Stream.Dispose(); + _compressedStream.Dispose(); _underlyingStream.Dispose(); } - std::int64_t Lz4CompressedBoundedStream::Seek(std::int64_t offset, SeekOrigin origin) + template + std::int64_t CompressedBoundedStream::Seek(std::int64_t offset, SeekOrigin origin) { - return _lz4Stream.Seek(offset, origin); + return _compressedStream.Seek(offset, origin); } - std::int64_t Lz4CompressedBoundedStream::GetPosition() const + template + std::int64_t CompressedBoundedStream::GetPosition() const { - return _lz4Stream.GetPosition(); + return _compressedStream.GetPosition(); } - std::int64_t Lz4CompressedBoundedStream::Read(void* destination, std::int64_t bytesToRead) + template + std::int64_t CompressedBoundedStream::Read(void* destination, std::int64_t bytesToRead) { - return _lz4Stream.Read(destination, bytesToRead); + return _compressedStream.Read(destination, bytesToRead); } - std::int64_t Lz4CompressedBoundedStream::Write(const void* source, std::int64_t bytesToWrite) + template + std::int64_t CompressedBoundedStream::Write(const void* source, std::int64_t bytesToWrite) { // Not supported return Stream::Invalid; } - bool Lz4CompressedBoundedStream::Flush() + template + bool CompressedBoundedStream::Flush() { // Not supported return true; } - bool Lz4CompressedBoundedStream::IsValid() + template + bool CompressedBoundedStream::IsValid() { - return _underlyingStream.IsValid() && _lz4Stream.IsValid(); + return _underlyingStream.IsValid() && _compressedStream.IsValid(); } - std::int64_t Lz4CompressedBoundedStream::GetSize() const + template + std::int64_t CompressedBoundedStream::GetSize() const { return _uncompressedSize; } @@ -244,7 +181,7 @@ namespace Death { namespace IO { if ((item.Flags & ItemFlags::Directory) != ItemFlags::Directory) { item.UncompressedSize = s->ReadVariableUint32(); - if ((item.Flags & (ItemFlags::ZlibCompressed | ItemFlags::Lz4Compressed | ItemFlags::Lzma2Compressed)) != ItemFlags::None) { + if (HasCompressedSize(item.Flags)) { item.Size = s->ReadVariableUint32(); } } @@ -284,7 +221,7 @@ namespace Death { namespace IO { if ((foundItem->Flags & ItemFlags::ZlibCompressed) == ItemFlags::ZlibCompressed) { #if defined(WITH_ZLIB) || defined(WITH_MINIZ) - return std::make_unique(_path, foundItem->Offset, foundItem->UncompressedSize, foundItem->Size); + return std::make_unique>(_path, foundItem->Offset, foundItem->UncompressedSize, foundItem->Size); #else LOGE("File \"%s\" was compressed using an unsupported compression method (Deflate)", String::nullTerminatedView(path).data()); return nullptr; @@ -293,13 +230,22 @@ namespace Death { namespace IO { if ((foundItem->Flags & ItemFlags::Lz4Compressed) == ItemFlags::Lz4Compressed) { #if defined(WITH_LZ4) - return std::make_unique(_path, foundItem->Offset, foundItem->UncompressedSize, foundItem->Size); + return std::make_unique>(_path, foundItem->Offset, foundItem->UncompressedSize, foundItem->Size); #else LOGE("File \"%s\" was compressed using an unsupported compression method (LZ4)", String::nullTerminatedView(path).data()); return nullptr; #endif } + if ((foundItem->Flags & ItemFlags::ZstdCompressed) == ItemFlags::ZstdCompressed) { +#if defined(WITH_ZSTD) + return std::make_unique>(_path, foundItem->Offset, foundItem->UncompressedSize, foundItem->Size); +#else + LOGE("File \"%s\" was compressed using an unsupported compression method (Zstd)", String::nullTerminatedView(path).data()); + return nullptr; +#endif + } + return std::make_unique(_path, foundItem->Offset, foundItem->UncompressedSize); } @@ -337,6 +283,12 @@ namespace Death { namespace IO { } } + bool PakFile::HasCompressedSize(ItemFlags itemFlags) + { + return ((itemFlags & (PakFile::ItemFlags::ZlibCompressed | PakFile::ItemFlags::Lz4Compressed | + PakFile::ItemFlags::Lzma2Compressed | PakFile::ItemFlags::ZstdCompressed)) != PakFile::ItemFlags::None); + } + class PakFile::Directory::Impl { friend class Directory; @@ -570,6 +522,16 @@ namespace Death { namespace IO { DEATH_DEBUG_ASSERT(size > 0); flags |= PakFile::ItemFlags::Lz4Compressed; } else +#endif +#if defined(WITH_ZSTD) + if (preferredCompression == PakPreferredCompression::Zstd) { + ZstdWriter dw(*_outputStream); + uncompressedSize = stream.CopyTo(dw); + dw.Dispose(); + size = _outputStream->GetPosition() - offset; + DEATH_DEBUG_ASSERT(size > 0); + flags |= PakFile::ItemFlags::ZstdCompressed; + } else #endif { uncompressedSize = stream.CopyTo(*_outputStream); @@ -713,7 +675,7 @@ namespace Death { namespace IO { if ((item.Flags & PakFile::ItemFlags::Directory) != PakFile::ItemFlags::Directory) { _outputStream->WriteVariableUint32(item.UncompressedSize); - if ((item.Flags & (PakFile::ItemFlags::ZlibCompressed | PakFile::ItemFlags::Lz4Compressed | PakFile::ItemFlags::Lzma2Compressed)) != PakFile::ItemFlags::None) { + if (PakFile::HasCompressedSize(item.Flags)) { _outputStream->WriteVariableUint32(item.Size); } } diff --git a/Sources/Shared/IO/PakFile.h b/Sources/Shared/IO/PakFile.h index 325a4ec5..80bf9629 100644 --- a/Sources/Shared/IO/PakFile.h +++ b/Sources/Shared/IO/PakFile.h @@ -15,7 +15,8 @@ namespace Death { namespace IO { { None, Deflate, - Lz4 + Lz4, + Zstd }; class PakFile @@ -100,10 +101,9 @@ namespace Death { namespace IO { Directory = 0x01, ZlibCompressed = 0x02, Lz4Compressed = 0x04, + ZstdCompressed = 0x08, - Lzma2Compressed = 0x08, // Not implemented - ZstdCompressed = 0x10, // Not implemented - + Lzma2Compressed = 0x10, // Not implemented Aes256Encrypten = 0x40, // Not implemented Link = 0x80 // Not implemented @@ -131,6 +131,8 @@ namespace Death { namespace IO { void ReadIndex(std::unique_ptr& s, Item* parentItem); Item* FindItem(Containers::StringView path); + + static DEATH_ALWAYS_INLINE bool HasCompressedSize(ItemFlags itemFlags); }; class PakWriter diff --git a/Sources/nCine/Graphics/TextureLoaderPng.cpp b/Sources/nCine/Graphics/TextureLoaderPng.cpp index 68dac563..04c72bc4 100644 --- a/Sources/nCine/Graphics/TextureLoaderPng.cpp +++ b/Sources/nCine/Graphics/TextureLoaderPng.cpp @@ -1,11 +1,12 @@ #include "TextureLoaderPng.h" #include -#include #include +#include using namespace Death::Containers; using namespace Death::IO; +using namespace Death::IO::Compression; namespace nCine { diff --git a/cmake/FindAngelscript.cmake b/cmake/FindAngelscript.cmake index 7bc1ef12..89a1279d 100644 --- a/cmake/FindAngelscript.cmake +++ b/cmake/FindAngelscript.cmake @@ -17,8 +17,8 @@ if(NOT TARGET Angelscript) ncine_add_dependency(Angelscript STATIC) - set(ANGELSCRIPT_DIR "${angelscriptgit_SOURCE_DIR}/sdk/angelscript/") - set(ANGELSCRIPT_INCLUDE_DIR "${ANGELSCRIPT_DIR}/include/") + set(ANGELSCRIPT_DIR "${angelscriptgit_SOURCE_DIR}/sdk/angelscript") + set(ANGELSCRIPT_INCLUDE_DIR "${ANGELSCRIPT_DIR}/include") set_target_properties(Angelscript PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${ANGELSCRIPT_INCLUDE_DIR}) target_link_libraries(Angelscript Threads::Threads) @@ -139,12 +139,9 @@ if(NOT TARGET Angelscript) endif() endif() + ncine_assign_source_group(PATH_PREFIX "${ANGELSCRIPT_DIR}/source" FILES ${ANGELSCRIPT_SOURCES} ${ANGELSCRIPT_HEADERS} SKIP_EXTERNAL) target_sources(Angelscript PRIVATE ${ANGELSCRIPT_SOURCES} ${ANGELSCRIPT_HEADERS}) target_include_directories(Angelscript PRIVATE "${ANGELSCRIPT_INCLUDE_DIR}" "${ANGELSCRIPT_DIR}/source") - - if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE OR CMAKE_POSITION_INDEPENDENT_CODE) - set_target_properties(Angelscript PROPERTIES POSITION_INDEPENDENT_CODE ON) - endif() set(ANGELSCRIPT_FOUND TRUE) set(ANGELSCRIPT_STATIC TRUE) diff --git a/cmake/FindLz4.cmake b/cmake/FindLz4.cmake index c96abac4..59f2b4f2 100644 --- a/cmake/FindLz4.cmake +++ b/cmake/FindLz4.cmake @@ -16,7 +16,7 @@ if(NOT TARGET Lz4) ncine_add_dependency(Lz4 STATIC) - set(LZ4_DIR "${lz4git_SOURCE_DIR}/lib/") + set(LZ4_DIR "${lz4git_SOURCE_DIR}/lib") set(LZ4_INCLUDE_DIR "${LZ4_DIR}") set_target_properties(Lz4 PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${LZ4_INCLUDE_DIR}) @@ -37,13 +37,10 @@ if(NOT TARGET Lz4) ${LZ4_DIR}/xxhash.c ) + ncine_assign_source_group(PATH_PREFIX ${LZ4_DIR} FILES ${LZ4_HEADERS} ${LZ4_SOURCES} SKIP_EXTERNAL) target_sources(Lz4 PRIVATE ${LZ4_SOURCES} ${LZ4_HEADERS}) target_include_directories(Lz4 PRIVATE ${LZ4_INCLUDE_DIR}) - if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE OR CMAKE_POSITION_INDEPENDENT_CODE) - set_target_properties(Lz4 PROPERTIES POSITION_INDEPENDENT_CODE ON) - endif() - set(LZ4_FOUND TRUE) set(LZ4_STATIC TRUE) mark_as_advanced(LZ4_STATIC) diff --git a/cmake/FindZstd.cmake b/cmake/FindZstd.cmake new file mode 100644 index 00000000..e28098b5 --- /dev/null +++ b/cmake/FindZstd.cmake @@ -0,0 +1,80 @@ +include(ncine_helpers) + +if(NOT TARGET Zstd) + if(NCINE_DOWNLOAD_DEPENDENCIES) + # Try to build `zstd` from source + set(ZSTD_URL "https://github.com/facebook/zstd/archive/refs/tags/v1.5.6.tar.gz") + message(STATUS "Downloading dependencies from \"${ZSTD_URL}\"...") + + include(FetchContent) + FetchContent_Declare( + ZstdGit + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + URL ${ZSTD_URL} + ) + FetchContent_MakeAvailable(ZstdGit) + + ncine_add_dependency(Zstd STATIC) + + set(ZSTD_DIR "${zstdgit_SOURCE_DIR}/lib") + set(ZSTD_INCLUDE_DIR "${ZSTD_DIR}") + set_target_properties(Zstd PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${ZSTD_INCLUDE_DIR}) + if(MSVC) + target_compile_definitions(Zstd PRIVATE "_CRT_SECURE_NO_WARNINGS" "ZSTD_HEAPMODE=0") + endif() + + file(GLOB CommonSources "${ZSTD_DIR}/common/*.c") + file(GLOB CompressSources "${ZSTD_DIR}/compress/*.c") + file(GLOB DecompressSources "${ZSTD_DIR}/decompress/*.c") + if (MSVC) + target_compile_definitions(Zstd PRIVATE "ZSTD_DISABLE_ASM") + else() + if(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|AMD64.*|x86_64.*|X86_64.*") + set(DecompressSources ${DecompressSources} "${ZSTD_DIR}/decompress/huf_decompress_amd64.S") + else() + target_compile_definitions(Zstd PRIVATE "ZSTD_DISABLE_ASM") + endif() + endif() + file(GLOB DictBuilderSources "${ZSTD_DIR}/dictBuilder/*.c") + file(GLOB DeprecatedSources "${ZSTD_DIR}/deprecated/*.c") + + file(GLOB PublicHeaders "${ZSTD_DIR}/*.h") + file(GLOB CommonHeaders "${ZSTD_DIR}/common/*.h") + file(GLOB CompressHeaders "${ZSTD_DIR}/compress/*.h") + file(GLOB DecompressHeaders "${ZSTD_DIR}/decompress/*.h") + file(GLOB DictBuilderHeaders "${ZSTD_DIR}/dictBuilder/*.h") + file(GLOB DeprecatedHeaders "${ZSTD_DIR}/deprecated/*.h") + + set(ZSTD_SOURCES ${CommonSources} ${CompressSources} ${DecompressSources}) + set(ZSTD_HEADERS ${PublicHeaders} ${CommonHeaders} ${CompressHeaders} ${DecompressHeaders}) + + #if (ZSTD_BUILD_DICTBUILDER) + # set(ZSTD_SOURCES ${ZSTD_SOURCES} ${DictBuilderSources}) + # set(ZSTD_HEADERS ${ZSTD_HEADERS} ${DictBuilderHeaders}) + #endif() + #if (ZSTD_BUILD_DEPRECATED) + # set(ZSTD_SOURCES ${ZSTD_SOURCES} ${DeprecatedSources}) + # set(ZSTD_HEADERS ${ZSTD_HEADERS} ${DeprecatedHeaders}) + #endif() + + if(NOT CMAKE_ASM_COMPILER STREQUAL CMAKE_C_COMPILER) + set_source_files_properties(${ZSTD_SOURCES} PROPERTIES LANGUAGE C) + endif() + + ncine_assign_source_group(PATH_PREFIX ${ZSTD_DIR} FILES ${ZSTD_HEADERS} ${ZSTD_SOURCES} SKIP_EXTERNAL) + target_sources(Zstd PRIVATE ${ZSTD_SOURCES} ${ZSTD_HEADERS}) + target_include_directories(Zstd PRIVATE ${ZSTD_INCLUDE_DIR}) + + set(ZSTD_FOUND TRUE) + set(ZSTD_STATIC TRUE) + mark_as_advanced(ZSTD_STATIC) + else() + find_package(PkgConfig REQUIRED) + pkg_check_modules(Zstd REQUIRED IMPORTED_TARGET libzstd) + if(TARGET PkgConfig::Zstd) + message(STATUS "Using Zstd from PkgConfig") + add_library(Zstd ALIAS PkgConfig::Zstd) + endif() + endif() +endif() \ No newline at end of file diff --git a/cmake/Findlibopenmpt.cmake b/cmake/Findlibopenmpt.cmake index 800e25c8..53509778 100644 --- a/cmake/Findlibopenmpt.cmake +++ b/cmake/Findlibopenmpt.cmake @@ -102,6 +102,7 @@ if(NOT TARGET libopenmpt::libopenmpt AND (NCINE_DOWNLOAD_DEPENDENCIES OR NCINE_C "${libopenmptgit_SOURCE_DIR}/libopenmpt/libopenmpt_impl.cpp" "${libopenmptgit_SOURCE_DIR}/libopenmpt/libopenmpt_ext_impl.cpp") + ncine_assign_source_group(PATH_PREFIX ${libopenmptgit_SOURCE_DIR} FILES ${LIBOPENMPT_SOURCES} SKIP_EXTERNAL) target_sources(Libopenmpt PRIVATE ${LIBOPENMPT_SOURCES}) target_include_directories(Libopenmpt PRIVATE "${libopenmptgit_SOURCE_DIR}" "${libopenmptgit_SOURCE_DIR}/common" "${libopenmptgit_SOURCE_DIR}/src" "${ZLIB_INCLUDE_DIRS}") diff --git a/cmake/ncine_extra_sources.cmake b/cmake/ncine_extra_sources.cmake index a1800a95..58b6e22e 100644 --- a/cmake/ncine_extra_sources.cmake +++ b/cmake/ncine_extra_sources.cmake @@ -484,6 +484,11 @@ if(LZ4_FOUND) target_link_libraries(${NCINE_APP} PRIVATE Lz4) endif() +if(ZSTD_FOUND) + target_compile_definitions(${NCINE_APP} PRIVATE "WITH_ZSTD") + target_link_libraries(${NCINE_APP} PRIVATE Zstd) +endif() + if(NCINE_BUILD_ANDROID) list(APPEND HEADERS ${NCINE_SOURCE_DIR}/nCine/Backends/Android/AndroidApplication.h diff --git a/cmake/ncine_headers.cmake b/cmake/ncine_headers.cmake index 124258d4..fbe02c91 100644 --- a/cmake/ncine_headers.cmake +++ b/cmake/ncine_headers.cmake @@ -36,16 +36,17 @@ set(HEADERS ${NCINE_SOURCE_DIR}/Shared/Core/Logger.h ${NCINE_SOURCE_DIR}/Shared/IO/AndroidAssetStream.h ${NCINE_SOURCE_DIR}/Shared/IO/BoundedFileStream.h - ${NCINE_SOURCE_DIR}/Shared/IO/DeflateStream.h ${NCINE_SOURCE_DIR}/Shared/IO/EmscriptenFileStream.h ${NCINE_SOURCE_DIR}/Shared/IO/FileAccess.h ${NCINE_SOURCE_DIR}/Shared/IO/FileStream.h ${NCINE_SOURCE_DIR}/Shared/IO/FileSystem.h ${NCINE_SOURCE_DIR}/Shared/IO/HttpRequest.h - ${NCINE_SOURCE_DIR}/Shared/IO/Lz4Stream.h ${NCINE_SOURCE_DIR}/Shared/IO/MemoryStream.h ${NCINE_SOURCE_DIR}/Shared/IO/PakFile.h ${NCINE_SOURCE_DIR}/Shared/IO/Stream.h + ${NCINE_SOURCE_DIR}/Shared/IO/Compression/DeflateStream.h + ${NCINE_SOURCE_DIR}/Shared/IO/Compression/Lz4Stream.h + ${NCINE_SOURCE_DIR}/Shared/IO/Compression/ZstdStream.h ${NCINE_SOURCE_DIR}/Shared/Threading/Event.h ${NCINE_SOURCE_DIR}/Shared/Threading/Spinlock.h ${NCINE_SOURCE_DIR}/Shared/Threading/Implementation/WaitOnAddress.h diff --git a/cmake/ncine_helpers.cmake b/cmake/ncine_helpers.cmake index 7b774968..246c3ac5 100644 --- a/cmake/ncine_helpers.cmake +++ b/cmake/ncine_helpers.cmake @@ -262,7 +262,7 @@ function(ncine_normalize_optimizations) endfunction() function(ncine_apply_compiler_options target) - cmake_parse_arguments(PARSE_ARGV 1 ARGS "ALLOW_EXCEPTIONS" "" "") + cmake_parse_arguments(PARSE_ARGV 2 ARGS "ALLOW_EXCEPTIONS" "" "") get_target_property(target_type ${target} TYPE) set(target_is_executable FALSE) @@ -528,7 +528,38 @@ function(ncine_apply_compiler_options target) endfunction() function(ncine_add_dependency target target_type) - set(CMAKE_FOLDER "Dependencies") add_library(${target} ${target_type}) + set_target_properties(${target} PROPERTIES FOLDER "Dependencies") ncine_apply_compiler_options(${ARGV}) +endfunction() + +function(ncine_assign_source_group) + cmake_parse_arguments(PARSE_ARGV 0 ARGS "SKIP_EXTERNAL" "PATH_PREFIX" "FILES") + + foreach(FILE ${ARGS_FILES}) + get_filename_component(PARENT_DIR "${FILE}" DIRECTORY) + string(LENGTH "${ARGS_PATH_PREFIX}" ARGS_PATH_PREFIX_LENGTH) + string(SUBSTRING "${PARENT_DIR}" 0 ${ARGS_PATH_PREFIX_LENGTH} PATH_START) + if(PATH_START STREQUAL "${ARGS_PATH_PREFIX}") + string(SUBSTRING "${PARENT_DIR}" ${ARGS_PATH_PREFIX_LENGTH} -1 GROUP) + #string(REPLACE "/" "\\" GROUP "${GROUP}") + string(REPLACE "\\" "/" GROUP "${GROUP}") + + # Group into "Source Files" and "Header Files" + if("${FILE}" MATCHES ".*\\.(c|cpp|asm|s)$") + set(GROUP "/Source Files${GROUP}/") + elseif("${FILE}" MATCHES ".*\\.h$") + set(GROUP "/Header Files${GROUP}/") + else() + set(GROUP "/") + endif() + elseif(ARGS_SKIP_EXTERNAL) + set(GROUP "/") + else() + # Path doesn't have common prefix, put it into "Dependencies" + set(GROUP "/Dependencies/") + endif() + + source_group("${GROUP}" FILES "${FILE}") + endforeach() endfunction() \ No newline at end of file diff --git a/cmake/ncine_imported_targets.cmake b/cmake/ncine_imported_targets.cmake index 5f0df2b5..17807bec 100644 --- a/cmake/ncine_imported_targets.cmake +++ b/cmake/ncine_imported_targets.cmake @@ -92,6 +92,10 @@ if(NCINE_WITH_LZ4) find_package(Lz4) endif() +if(NCINE_WITH_ZSTD) + find_package(Zstd) +endif() + if(NCINE_WITH_ANGELSCRIPT) find_package(Angelscript) endif() diff --git a/cmake/ncine_options.cmake b/cmake/ncine_options.cmake index 5a39497f..6b7c4f4a 100644 --- a/cmake/ncine_options.cmake +++ b/cmake/ncine_options.cmake @@ -60,7 +60,8 @@ else() endif() endif() -#option(NCINE_WITH_LZ4 "Enable Lz4 compression support" OFF) +#option(NCINE_WITH_LZ4 "Enable LZ4 compression support" OFF) +#option(NCINE_WITH_ZSTD "Enable Zstd compression support" OFF) option(NCINE_WITH_WEBP "Enable WebP image file support" OFF) option(NCINE_WITH_AUDIO "Enable OpenAL support and thus sound" ON) cmake_dependent_option(NCINE_WITH_VORBIS "Enable Ogg Vorbis audio file support" ON "NCINE_WITH_AUDIO" OFF) diff --git a/cmake/ncine_sources.cmake b/cmake/ncine_sources.cmake index 3bb57a4a..97823206 100644 --- a/cmake/ncine_sources.cmake +++ b/cmake/ncine_sources.cmake @@ -10,14 +10,15 @@ set(SOURCES ${NCINE_SOURCE_DIR}/Shared/Core/Logger.cpp ${NCINE_SOURCE_DIR}/Shared/IO/AndroidAssetStream.cpp ${NCINE_SOURCE_DIR}/Shared/IO/BoundedFileStream.cpp - ${NCINE_SOURCE_DIR}/Shared/IO/DeflateStream.cpp ${NCINE_SOURCE_DIR}/Shared/IO/EmscriptenFileStream.cpp ${NCINE_SOURCE_DIR}/Shared/IO/FileStream.cpp ${NCINE_SOURCE_DIR}/Shared/IO/FileSystem.cpp - ${NCINE_SOURCE_DIR}/Shared/IO/Lz4Stream.cpp ${NCINE_SOURCE_DIR}/Shared/IO/MemoryStream.cpp ${NCINE_SOURCE_DIR}/Shared/IO/PakFile.cpp ${NCINE_SOURCE_DIR}/Shared/IO/Stream.cpp + ${NCINE_SOURCE_DIR}/Shared/IO/Compression/DeflateStream.cpp + ${NCINE_SOURCE_DIR}/Shared/IO/Compression/Lz4Stream.cpp + ${NCINE_SOURCE_DIR}/Shared/IO/Compression/ZstdStream.cpp ${NCINE_SOURCE_DIR}/Shared/Threading/Implementation/WaitOnAddress.cpp )