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
)