1
0
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:
2024-04-07 09:41:19 +10:00
parent a7617f3a3a
commit f8a6b976c9
4 changed files with 183 additions and 4 deletions

View File

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

View 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

View File

@@ -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:

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