Skip to content

Commit

Permalink
Add compression step for the map format (#8531)
Browse files Browse the repository at this point in the history
  • Loading branch information
ihhub authored Apr 3, 2024
1 parent 55068ef commit b9ee503
Show file tree
Hide file tree
Showing 11 changed files with 167 additions and 119 deletions.
6 changes: 3 additions & 3 deletions src/engine/h2d_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ namespace fheroes2
}

for ( const auto & data : _fileData ) {
fileStream.putRaw( reinterpret_cast<const char *>( data.second.data() ), data.second.size() );
fileStream.putRaw( data.second.data(), data.second.size() );
}

return true;
Expand Down Expand Up @@ -213,8 +213,8 @@ namespace fheroes2
stream.putLE32( static_cast<uint32_t>( image.y() ) );

const size_t imageSize = static_cast<size_t>( image.width() ) * static_cast<size_t>( image.height() );
stream.putRaw( reinterpret_cast<const char *>( image.image() ), imageSize );
stream.putRaw( reinterpret_cast<const char *>( image.transform() ), imageSize );
stream.putRaw( image.image(), imageSize );
stream.putRaw( image.transform(), imageSize );

return writer.add( name, stream.getRaw() );
}
Expand Down
4 changes: 2 additions & 2 deletions src/engine/serialize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ std::vector<uint8_t> StreamBuf::getRaw( size_t sz )
return v;
}

void StreamBuf::putRaw( const char * ptr, size_t sz )
void StreamBuf::putRaw( const void * ptr, size_t sz )
{
if ( sz == 0 ) {
return;
Expand Down Expand Up @@ -671,7 +671,7 @@ std::vector<uint8_t> StreamFile::getRaw( const size_t size )
return v;
}

void StreamFile::putRaw( const char * ptr, size_t sz )
void StreamFile::putRaw( const void * ptr, size_t sz )
{
if ( !_file ) {
return;
Expand Down
6 changes: 3 additions & 3 deletions src/engine/serialize.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class StreamBase
virtual void putLE16( uint16_t ) = 0;

virtual std::vector<uint8_t> getRaw( size_t = 0 /* all data */ ) = 0;
virtual void putRaw( const char *, size_t ) = 0;
virtual void putRaw( const void *, size_t ) = 0;

uint16_t get16();
uint32_t get32();
Expand Down Expand Up @@ -322,7 +322,7 @@ class StreamBuf : public StreamBase
void putLE16( uint16_t v ) override;

std::vector<uint8_t> getRaw( size_t sz = 0 /* all data */ ) override;
void putRaw( const char * ptr, size_t sz ) override;
void putRaw( const void * ptr, size_t sz ) override;

std::string toString( const size_t size = 0 );

Expand Down Expand Up @@ -383,7 +383,7 @@ class StreamFile : public StreamBase
// 0 stands for full data.
std::vector<uint8_t> getRaw( const size_t size = 0 ) override;

void putRaw( const char *, size_t ) override;
void putRaw( const void * ptr, size_t sz ) override;

std::string toString( const size_t size = 0 );

Expand Down
147 changes: 75 additions & 72 deletions src/engine/zzlib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,21 @@
#include <algorithm>
#include <cstring>
#include <ostream>
#include <vector>

#include <zconf.h>
#include <zlib.h>

#include "logging.h"
#include "serialize.h"

namespace
{
constexpr uint16_t FORMAT_VERSION_0 = 0;
}

std::vector<uint8_t> zlibDecompress( const uint8_t * src, const size_t srcSize, size_t realSize = 0 )
namespace Compression
{
std::vector<uint8_t> decompressData( const uint8_t * src, const size_t srcSize, size_t realSize /* = 0 */ )
{
if ( src == nullptr || srcSize == 0 ) {
return {};
Expand Down Expand Up @@ -100,7 +103,7 @@ namespace
return res;
}

std::vector<uint8_t> zlibCompress( const uint8_t * src, const size_t srcSize )
std::vector<uint8_t> compressData( const uint8_t * src, const size_t srcSize )
{
if ( src == nullptr || srcSize == 0 ) {
return {};
Expand Down Expand Up @@ -131,94 +134,94 @@ namespace

return res;
}
}

bool ZStreamBuf::read( const std::string & fn, const size_t offset /* = 0 */ )
{
StreamFile sf;
sf.setbigendian( true );

if ( !sf.open( fn, "rb" ) ) {
return false;
}

if ( offset ) {
sf.seek( offset );
}
bool readFile( StreamBuf & output, const std::string & fn, const size_t offset /* = 0 */ )
{
StreamFile sf;
sf.setbigendian( true );

const uint32_t rawSize = sf.get32();
const uint32_t zipSize = sf.get32();
if ( zipSize == 0 ) {
return false;
}
if ( !sf.open( fn, "rb" ) ) {
return false;
}

const uint16_t version = sf.get16();
if ( version != FORMAT_VERSION_0 ) {
return false;
}
if ( offset ) {
sf.seek( offset );
}

sf.skip( 2 ); // Unused bytes
const uint32_t rawSize = sf.get32();
const uint32_t zipSize = sf.get32();
if ( zipSize == 0 ) {
return false;
}

const std::vector<uint8_t> zip = sf.getRaw( zipSize );
const std::vector<uint8_t> raw = zlibDecompress( zip.data(), zip.size(), rawSize );
if ( raw.size() != rawSize ) {
return false;
}
const uint16_t version = sf.get16();
if ( version != FORMAT_VERSION_0 ) {
return false;
}

putRaw( reinterpret_cast<const char *>( raw.data() ), raw.size() );
sf.skip( 2 ); // Unused bytes

return !fail();
}
const std::vector<uint8_t> zip = sf.getRaw( zipSize );
const std::vector<uint8_t> raw = decompressData( zip.data(), zip.size(), rawSize );
if ( raw.size() != rawSize ) {
return false;
}

bool ZStreamBuf::write( const std::string & fn, const bool append /* = false */ )
{
StreamFile sf;
sf.setbigendian( true );
output.putRaw( raw.data(), raw.size() );

if ( !sf.open( fn, append ? "ab" : "wb" ) ) {
return false;
return !output.fail();
}

const std::vector<uint8_t> zip = zlibCompress( data(), size() );
if ( zip.empty() ) {
return false;
}
bool writeFile( StreamBuf & input, const std::string & fn, const bool append /* = false */ )
{
StreamFile sf;
sf.setbigendian( true );

sf.put32( static_cast<uint32_t>( size() ) );
sf.put32( static_cast<uint32_t>( zip.size() ) );
sf.put16( FORMAT_VERSION_0 );
sf.put16( 0 ); // Unused bytes
sf.putRaw( reinterpret_cast<const char *>( zip.data() ), zip.size() );
if ( !sf.open( fn, append ? "ab" : "wb" ) ) {
return false;
}

return !sf.fail();
}
const std::vector<uint8_t> zip = compressData( input.data(), input.size() );
if ( zip.empty() ) {
return false;
}

fheroes2::Image CreateImageFromZlib( int32_t width, int32_t height, const uint8_t * imageData, size_t imageSize, bool doubleLayer )
{
if ( imageData == nullptr || imageSize == 0 || width <= 0 || height <= 0 ) {
return {};
}
sf.put32( static_cast<uint32_t>( input.size() ) );
sf.put32( static_cast<uint32_t>( zip.size() ) );
sf.put16( FORMAT_VERSION_0 );
sf.put16( 0 ); // Unused bytes
sf.putRaw( zip.data(), zip.size() );

const std::vector<uint8_t> & uncompressedData = zlibDecompress( imageData, imageSize );
if ( doubleLayer && ( uncompressedData.size() & 1 ) == 1 ) {
return {};
return !sf.fail();
}

const size_t uncompressedSize = doubleLayer ? uncompressedData.size() / 2 : uncompressedData.size();
fheroes2::Image CreateImageFromZlib( int32_t width, int32_t height, const uint8_t * imageData, size_t imageSize, bool doubleLayer )
{
if ( imageData == nullptr || imageSize == 0 || width <= 0 || height <= 0 ) {
return {};
}

if ( static_cast<size_t>( width * height ) != uncompressedSize ) {
return {};
}
const std::vector<uint8_t> & uncompressedData = decompressData( imageData, imageSize );
if ( doubleLayer && ( uncompressedData.size() & 1 ) == 1 ) {
return {};
}

fheroes2::Image out;
if ( !doubleLayer ) {
out._disableTransformLayer();
}
out.resize( width, height );
const size_t uncompressedSize = doubleLayer ? uncompressedData.size() / 2 : uncompressedData.size();

std::memcpy( out.image(), uncompressedData.data(), uncompressedSize );
if ( doubleLayer ) {
std::memcpy( out.transform(), uncompressedData.data() + uncompressedSize, uncompressedSize );
if ( static_cast<size_t>( width ) * height != uncompressedSize ) {
return {};
}

fheroes2::Image out;
if ( !doubleLayer ) {
out._disableTransformLayer();
}
out.resize( width, height );

std::memcpy( out.image(), uncompressedData.data(), uncompressedSize );
if ( doubleLayer ) {
std::memcpy( out.transform(), uncompressedData.data() + uncompressedSize, uncompressedSize );
}
return out;
}
return out;
}
28 changes: 15 additions & 13 deletions src/engine/zzlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,34 @@
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>

#include "image.h"
#include "serialize.h"

class ZStreamBuf : public StreamBuf
{
public:
ZStreamBuf() = default;

ZStreamBuf( const ZStreamBuf & ) = delete;
class StreamBuf;

~ZStreamBuf() override = default;
namespace Compression
{
// Zips the input data and returns the compressed data or an empty vector in case of an error.
std::vector<uint8_t> compressData( const uint8_t * src, const size_t srcSize );

ZStreamBuf & operator=( const ZStreamBuf & ) = delete;
// Unzips the input data and returns the uncompressed data or an empty vector in case of an error.
// The 'realSize' parameter represents the planned size of the decompressed data and is optional
// (it is only used to speed up the decompression process). If this parameter is omitted or set to
// zero, the size of the decompressed data will be determined automatically.
std::vector<uint8_t> decompressData( const uint8_t * src, const size_t srcSize, size_t realSize = 0 );

// Reads & unzips the zipped chunk from the specified file at the specified offset and appends
// it to the end of the buffer. The current read position of the buffer does not change. Returns
// true on success or false on error.
bool read( const std::string & fn, const size_t offset = 0 );
bool readFile( StreamBuf & output, const std::string & fn, const size_t offset = 0 );

// Zips the contents of the buffer from the current read position to the end of the buffer and
// writes (or appends) it to the specified file. The current read position of the buffer does
// not change. Returns true on success and false on error.
bool write( const std::string & fn, const bool append = false );
};
bool writeFile( StreamBuf & input, const std::string & fn, const bool append = false );

fheroes2::Image CreateImageFromZlib( int32_t width, int32_t height, const uint8_t * imageData, size_t imageSize, bool doubleLayer );
fheroes2::Image CreateImageFromZlib( int32_t width, int32_t height, const uint8_t * imageData, size_t imageSize, bool doubleLayer );
}

#endif
2 changes: 1 addition & 1 deletion src/fheroes2/dialog/dialog_resolution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ namespace Dialog
display.setResolution( selectedResolution );

#if !defined( MACOS_APP_BUNDLE )
const fheroes2::Image & appIcon = CreateImageFromZlib( 32, 32, iconImage, sizeof( iconImage ), true );
const fheroes2::Image & appIcon = Compression::CreateImageFromZlib( 32, 32, iconImage, sizeof( iconImage ), true );
fheroes2::engine().setIcon( appIcon );
#endif

Expand Down
4 changes: 2 additions & 2 deletions src/fheroes2/game/fheroes2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ namespace
void displayMissingResourceWindow()
{
fheroes2::Display & display = fheroes2::Display::instance();
const fheroes2::Image & image = CreateImageFromZlib( 290, 190, errorMessage, sizeof( errorMessage ), false );
const fheroes2::Image & image = Compression::CreateImageFromZlib( 290, 190, errorMessage, sizeof( errorMessage ), false );

display.fill( 0 );
fheroes2::Resize( image, display );
Expand Down Expand Up @@ -190,7 +190,7 @@ namespace
fheroes2::cursor().registerUpdater( Cursor::Refresh );

#if !defined( MACOS_APP_BUNDLE )
const fheroes2::Image & appIcon = CreateImageFromZlib( 32, 32, iconImage, sizeof( iconImage ), true );
const fheroes2::Image & appIcon = Compression::CreateImageFromZlib( 32, 32, iconImage, sizeof( iconImage ), true );
fheroes2::engine().setIcon( appIcon );
#endif
}
Expand Down
Loading

0 comments on commit b9ee503

Please sign in to comment.