mirror of
https://github.com/ScrelliCopter/tmx2gba.git
synced 2025-02-21 03:29:25 +11:00
gzip support for miniz
This commit is contained in:
@@ -3,15 +3,16 @@ project(tmxlite VERSION 1.3.1)
|
||||
# includes the list of source files in the src directory
|
||||
set(PROJECT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
file(GLOB PROJECT_SRC ${PROJECT_DIR}/*.cpp)
|
||||
file(GLOB PROJECT_SRC_DETAIL ${PROJECT_DIR}/detail/*.cpp)
|
||||
file(GLOB PROJECT_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/tmxlite/*.hpp)
|
||||
file(GLOB PROJECT_HEADERS_INL ${CMAKE_CURRENT_SOURCE_DIR}/include/tmxlite/*.inl)
|
||||
file(GLOB PROJECT_HEADERS_DETAIL ${CMAKE_CURRENT_SOURCE_DIR}/include/tmxlite/detail/*.hpp)
|
||||
list(APPEND PROJECT_SRC ${PROJECT_HEADERS} ${PROJECT_HEADERS_INL} ${PROJECT_HEADERS_DETAIL})
|
||||
list(APPEND PROJECT_SRC ${PROJECT_SRC_DETAIL} ${PROJECT_HEADERS} ${PROJECT_HEADERS_INL} ${PROJECT_HEADERS_DETAIL})
|
||||
|
||||
add_library(${PROJECT_NAME} STATIC ${PROJECT_SRC})
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
CXX_STANDARD 14
|
||||
CXX_STANDARD 20
|
||||
CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||
|
||||
39
ext/tmxlite/include/tmxlite/detail/gzip.hpp
Normal file
39
ext/tmxlite/include/tmxlite/detail/gzip.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
// gzip.hpp - portable memory miniz based gzip reader
|
||||
// SPDX-License-Identifier: Zlib
|
||||
// SPDX-FileCopyrightText: (c) 2024 a dinosaur
|
||||
|
||||
#ifndef GZIP_HPP
|
||||
#define GZIP_HPP
|
||||
|
||||
#include "miniz.h"
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
|
||||
|
||||
class GZipReader
|
||||
{
|
||||
static constexpr uint8_t
|
||||
FTEXT = 1, FHCRC = 1<<1, FEXTRA = 1<<2, FNAME = 1<<3, FCOMMENT = 1<<4;
|
||||
|
||||
static constexpr uint8_t XFL_BEST = 2, XFL_FASTEST = 4;
|
||||
|
||||
tinfl_decompressor mState;
|
||||
std::span<const uint8_t>::iterator mIt;
|
||||
|
||||
size_t mSourceLen, mBytesRead;
|
||||
uint32_t mModificationTime, mCrc, mInputSize, mComputedCrc;
|
||||
uint16_t crc16;
|
||||
uint8_t mFlags, mXflags, mOsId;
|
||||
|
||||
public:
|
||||
GZipReader() noexcept;
|
||||
|
||||
constexpr size_t SourceLength() const noexcept { return mSourceLen; }
|
||||
constexpr uint32_t OutputLength() const noexcept { return mInputSize; }
|
||||
|
||||
bool OpenMemory(const std::span<const uint8_t> source) noexcept;
|
||||
bool Read(std::span<uint8_t> out) noexcept;
|
||||
bool Check() const noexcept;
|
||||
};
|
||||
|
||||
#endif//GZIP_HPP
|
||||
@@ -29,10 +29,14 @@ source distribution.
|
||||
#include "tmxlite/FreeFuncs.hpp"
|
||||
#include "tmxlite/TileLayer.hpp"
|
||||
#include "tmxlite/detail/Log.hpp"
|
||||
#ifndef USE_ZLIB
|
||||
# include "tmxlite/detail/gzip.hpp"
|
||||
#endif
|
||||
|
||||
#include <pugixml.hpp>
|
||||
#include <zstd.h>
|
||||
#include <sstream>
|
||||
#include <span>
|
||||
|
||||
using namespace tmx;
|
||||
|
||||
@@ -140,8 +144,18 @@ void TileLayer::parseBase64(const pugi::xml_node& node)
|
||||
break;
|
||||
case CompressionType::GZip:
|
||||
#ifndef USE_ZLIB
|
||||
Logger::log("Library must be built with USE_ZLIB for GZip compression", Logger::Type::Error);
|
||||
return {};
|
||||
{
|
||||
byteData.resize(expectedSize);
|
||||
const auto source = std::span(reinterpret_cast<const uint8_t*>(dataString.data()), dataString.size());
|
||||
|
||||
GZipReader reader;
|
||||
if (!reader.OpenMemory(source) || !reader.Read(byteData) || !reader.Check())
|
||||
{
|
||||
LOG("Failed to decompress layer data, node skipped.", Logger::Type::Error);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
//[[fallthrough]];
|
||||
case CompressionType::Zlib:
|
||||
|
||||
125
ext/tmxlite/src/detail/gzip.cpp
Normal file
125
ext/tmxlite/src/detail/gzip.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
// gzip.cpp - portable memory miniz based gzip reader
|
||||
// SPDX-License-Identifier: Zlib
|
||||
// SPDX-FileCopyrightText: (c) 2024 a dinosaur
|
||||
|
||||
#include "tmxlite/detail/gzip.hpp"
|
||||
#include <string_view>
|
||||
|
||||
|
||||
GZipReader::GZipReader() noexcept :
|
||||
mSourceLen(0), mBytesRead(0),
|
||||
mModificationTime(0), mCrc(0), mInputSize(0), mComputedCrc(0),
|
||||
crc16(0), mFlags(0), mXflags(0), mOsId(0)
|
||||
{
|
||||
tinfl_init(&mState);
|
||||
mComputedCrc = static_cast<uint32_t>(mz_crc32(0, nullptr, 0));
|
||||
}
|
||||
|
||||
bool GZipReader::OpenMemory(const std::span<const uint8_t> source) noexcept
|
||||
{
|
||||
if (source.size() < 20)
|
||||
return false;
|
||||
|
||||
auto it = std::cbegin(source), end = std::cend(source);
|
||||
|
||||
constexpr uint8_t magic[2] = { 0x1F, 0x8B };
|
||||
if (*it++ != magic[0] || *it++ != magic[1])
|
||||
return false;
|
||||
|
||||
constexpr uint8_t CM_DEFLATE = 8;
|
||||
uint8_t compression = *it++;
|
||||
if (compression != CM_DEFLATE)
|
||||
return false;
|
||||
|
||||
mFlags = *it++;
|
||||
mModificationTime = *it++;
|
||||
mModificationTime |= *it++ << 8;
|
||||
mModificationTime |= *it++ << 16;
|
||||
mModificationTime |= *it++ << 24;
|
||||
mXflags = *it++;
|
||||
mOsId = *it++;
|
||||
|
||||
if (mFlags & FEXTRA)
|
||||
{
|
||||
// Skip "extra" field
|
||||
if (it + 2 >= end)
|
||||
return false;
|
||||
uint16_t extraLen = *it++;
|
||||
extraLen = *it++ << 8;
|
||||
if (it + extraLen >= end)
|
||||
return false;
|
||||
it += extraLen;
|
||||
}
|
||||
if (mFlags & FNAME)
|
||||
{
|
||||
// Skip null-terminated name string
|
||||
do
|
||||
{
|
||||
if (++it == end)
|
||||
return false;
|
||||
} while (*it != '\0');
|
||||
if (++it == end)
|
||||
return false;
|
||||
}
|
||||
if (mFlags & FCOMMENT)
|
||||
{
|
||||
// Skip null-terminated comment string
|
||||
do
|
||||
{
|
||||
if (++it == end)
|
||||
return false;
|
||||
} while (*it != '\0');
|
||||
if (++it == end)
|
||||
return false;
|
||||
}
|
||||
if (mFlags & FHCRC)
|
||||
{
|
||||
if (it + 2 >= end)
|
||||
return false;
|
||||
crc16 = *it++;
|
||||
crc16 |= *it++;
|
||||
}
|
||||
|
||||
mIt = it;
|
||||
mSourceLen = end - it - 8;
|
||||
|
||||
it += mSourceLen;
|
||||
mCrc = *it++;
|
||||
mCrc |= *it++ << 8;
|
||||
mCrc |= *it++ << 16;
|
||||
mCrc |= *it++ << 24;
|
||||
mInputSize = *it++;
|
||||
mInputSize |= *it++ << 8;
|
||||
mInputSize |= *it++ << 16;
|
||||
mInputSize |= *it++ << 24;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GZipReader::Read(std::span<uint8_t> out) noexcept
|
||||
{
|
||||
size_t outLen = out.size();
|
||||
auto res = tinfl_decompress(&mState,
|
||||
static_cast<const mz_uint8*>(&*mIt), &mSourceLen,
|
||||
static_cast<mz_uint8*>(out.data()), static_cast<mz_uint8*>(out.data()), &outLen,
|
||||
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
|
||||
if (res != TINFL_STATUS_DONE)
|
||||
return false;
|
||||
|
||||
mIt += outLen;
|
||||
mBytesRead += outLen;
|
||||
mComputedCrc = static_cast<uint32_t>(mz_crc32(static_cast<mz_ulong>(mComputedCrc), out.data(), outLen));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GZipReader::Check() const noexcept
|
||||
{
|
||||
if (mComputedCrc != mCrc)
|
||||
return false;
|
||||
|
||||
if (static_cast<uint32_t>(mBytesRead & UINT32_MAX) != mInputSize)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user