diff --git a/.cirrus.yml b/.cirrus.yml index b51459ad168..a5ae9e01efb 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -13,8 +13,10 @@ build_windows_task: AGS_LIBVORBIS_LIB: C:\Lib\Xiph\x86 AGS_SDL_INCLUDE: C:\Lib\SDL2\include AGS_SDL_SOUND_INCLUDE: C:\Lib\SDL_sound\src + AGS_ZLIB_INCLUDE: C:\Lib\zlib\include AGS_SDL_LIB: C:\Lib\SDL2\lib\x86 AGS_SDL_SOUND_LIB: C:\Lib\SDL_sound\lib\x86 + AGS_ZLIB_LIB: C:\Lib\zlib\lib\x86 build_debug_script: > "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 && cd Solutions && @@ -278,6 +280,8 @@ build_editor_task: env: AGS_SDL_INCLUDE: C:\Lib\SDL2\include AGS_SDL_LIB: C:\Lib\SDL2\lib\x86 + AGS_ZLIB_INCLUDE: C:\Lib\zlib\include + AGS_ZLIB_LIB: C:\Lib\zlib\lib\x86 nuget_packages_cache: folder: Solutions\packages fingerprint_script: type Editor\AGS.Editor\packages.config diff --git a/CMake/FetchZLIB.cmake b/CMake/FetchZLIB.cmake new file mode 100644 index 00000000000..8ecdbfeb278 --- /dev/null +++ b/CMake/FetchZLIB.cmake @@ -0,0 +1,22 @@ +FetchContent_Declare( + zlibng_content + URL https://github.com/zlib-ng/zlib-ng/archive/refs/tags/2.1.3.tar.gz + URL_HASH MD5=f2ddef7b10e24cdcc4f17285575a6895 +) + +FetchContent_GetProperties(zlibng_content) +if(NOT zlibng_content_POPULATED) + FetchContent_Populate(zlibng_content) + + set(ZLIB_ENABLE_TESTS OFF CACHE BOOL "") + set(ZLIBNG_ENABLE_TESTS OFF CACHE BOOL "") + set(WITH_GTEST OFF CACHE BOOL "") + set(ZLIB_COMPAT ON CACHE BOOL "") + add_subdirectory(${zlibng_content_SOURCE_DIR} ${zlibng_content_BINARY_DIR} EXCLUDE_FROM_ALL) + + add_library(zlib-interface INTERFACE) + target_link_libraries(zlib-interface INTERFACE zlibstatic) + add_library(ZLIB::ZLIB ALIAS zlib-interface) + +endif() +set(ZLIB_LIBRARIES ZLIB::ZLIB) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3480a3d4d89..3c9b147e46e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ option(AGS_USE_LOCAL_SDL2_SOUND "Use a locally installed SDL2 Sound" ${AGS_USE_L option(AGS_USE_LOCAL_OGG "Use a locally installed OGG" ${AGS_USE_LOCAL_ALL_LIBRARIES}) option(AGS_USE_LOCAL_THEORA "Use a locally installed Theora" ${AGS_USE_LOCAL_ALL_LIBRARIES}) option(AGS_USE_LOCAL_VORBIS "Use a locally installed Vorbis" ${AGS_USE_LOCAL_ALL_LIBRARIES}) +option(AGS_USE_LOCAL_ZLIB "Use a locally installed ZLib" ${AGS_USE_LOCAL_ALL_LIBRARIES}) option(AGS_TESTS "Build tests" OFF) option(AGS_BUILD_ENGINE "Build Engine" ON) @@ -35,6 +36,7 @@ message(" AGS_USE_LOCAL_SDL2_SOUND: ${AGS_USE_LOCAL_SDL2_SOUND}") message(" AGS_USE_LOCAL_OGG: ${AGS_USE_LOCAL_OGG}") message(" AGS_USE_LOCAL_THEORA: ${AGS_USE_LOCAL_THEORA}") message(" AGS_USE_LOCAL_VORBIS: ${AGS_USE_LOCAL_VORBIS}") +message(" AGS_USE_LOCAL_ZLIB: ${AGS_USE_LOCAL_ZLIB}") message("------ AGS selected CMake options ------") message(" AGS_TESTS: ${AGS_TESTS}") message(" AGS_BUILD_ENGINE: ${AGS_BUILD_ENGINE}") @@ -146,7 +148,7 @@ if(EMSCRIPTEN) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/CMake/Emscripten") set(AGS_OPENGLES2 TRUE) - set(USE_FLAGS " -sUSE_SDL=2 -sUSE_VORBIS=1 -sUSE_OGG=1 ") + set(USE_FLAGS " -sUSE_SDL=2 -sUSE_VORBIS=1 -sUSE_OGG=1 -sUSE_ZLIB=1") set(AGS_DISABLE_THREADS TRUE) if(AGS_DISABLE_THREADS) add_compile_definitions("AGS_DISABLE_THREADS=1") @@ -303,6 +305,14 @@ else() find_package(Theora REQUIRED) endif() +if(EMSCRIPTEN) + # do nothing +elseif (NOT AGS_USE_LOCAL_ZLIB) + include(FetchZLIB) +else() + find_package(ZLIB REQUIRED) +endif() + ############################################################################### # subdirectories of this project diff --git a/Common/CMakeLists.txt b/Common/CMakeLists.txt index f5a4d32c16b..b2360f44eb4 100644 --- a/Common/CMakeLists.txt +++ b/Common/CMakeLists.txt @@ -190,7 +190,8 @@ target_link_libraries(common PUBLIC Allegro::Allegro AlFont::AlFont AAStr::AAStr - glm::glm) + glm::glm + ${ZLIB_LIBRARIES}) if (WIN32) target_link_libraries(common PUBLIC shlwapi) diff --git a/Common/ac/spritefile.cpp b/Common/ac/spritefile.cpp index bdb12f28338..333824d5ce0 100644 --- a/Common/ac/spritefile.cpp +++ b/Common/ac/spritefile.cpp @@ -445,6 +445,8 @@ HError SpriteFile::LoadSprite(sprkey_t index, Common::Bitmap *&sprite) break; case kSprCompress_LZW: lzw_decompress(im_data.Buf, im_data.Size, im_data.BPP, _stream.get(), in_data_size); break; + case kSprCompress_Deflate: inflate_decompress(im_data.Buf, im_data.Size, im_data.BPP, _stream.get(), in_data_size); + break; default: assert(!"Unsupported compression type!"); break; } // TODO: test that not more than data_size was read! @@ -699,6 +701,8 @@ void SpriteFileWriter::WriteBitmap(Bitmap *image) break; case kSprCompress_LZW: lzw_compress(im_data.Buf, im_data.Size, im_data.BPP, &mems); break; + case kSprCompress_Deflate: deflate_compress(im_data.Buf, im_data.Size, im_data.BPP, &mems); + break; default: assert(!"Unsupported compression type!"); break; } // mark to write as a plain byte array diff --git a/Common/ac/spritefile.h b/Common/ac/spritefile.h index f1728744970..c6771b9b6df 100644 --- a/Common/ac/spritefile.h +++ b/Common/ac/spritefile.h @@ -83,7 +83,8 @@ enum SpriteCompression { kSprCompress_None = 0, kSprCompress_RLE, - kSprCompress_LZW + kSprCompress_LZW, + kSprCompress_Deflate }; typedef int32_t sprkey_t; diff --git a/Common/util/compress.cpp b/Common/util/compress.cpp index 512ecd6f03f..03c11f58c6f 100644 --- a/Common/util/compress.cpp +++ b/Common/util/compress.cpp @@ -11,14 +11,11 @@ // http://www.opensource.org/licenses/artistic-license-2.0.php // //============================================================================= -#ifdef _MANAGED -// ensure this doesn't get compiled to .NET IL -#pragma unmanaged -#endif #include "util/compress.h" #include #include +#include #include #include "ac/common.h" // quit, update_polled_stuff #include "gfx/bitmap.h" @@ -27,6 +24,7 @@ #if AGS_PLATFORM_ENDIAN_BIG #include "util/bbop.h" #endif +#include using namespace AGS::Common; @@ -449,3 +447,97 @@ std::unique_ptr load_lzw(Stream *in, int dst_bpp, RGB (*pal)[256]) return bmm; } + +//----------------------------------------------------------------------------- +// Deflate +//----------------------------------------------------------------------------- + +bool z_deflate(Stream* input, Stream* output) { + z_stream stream; + memset(&stream, 0, sizeof(stream)); + + int ret = deflateInit(&stream, Z_DEFAULT_COMPRESSION); + if (ret != Z_OK) { + std::cerr << "Error initializing compression" << std::endl; + return false; + } + + stream.data_type = 0; + + char inbuf[1024]; + char outbuf[1024]; + + stream.avail_in = input->Read(inbuf, sizeof(inbuf)); + while (stream.avail_in > 0) { + stream.next_in = (Bytef*)inbuf; + int flush = input->EOS() ? Z_FINISH : Z_NO_FLUSH; + + do { + stream.next_out = (Bytef*)outbuf; + stream.avail_out = sizeof(outbuf); + ret = deflate(&stream, flush); + assert(ret != Z_STREAM_ERROR); + int have = sizeof(outbuf) - stream.avail_out; + output->Write(outbuf, have); + } while (stream.avail_out == 0); + + stream.avail_in = input->Read(inbuf, sizeof(inbuf)); + } + + (void)deflateEnd(&stream); + + return true; +} + +bool z_inflate(const uint8_t* src, size_t src_sz, uint8_t* dst, size_t dst_sz) { + z_stream stream; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + stream.avail_in = 0; + stream.next_in = Z_NULL; + + int ret = inflateInit(&stream); + if (ret != Z_OK) { + std::cerr << "Error initializing decompression" << std::endl; + return false; + } + + stream.next_in = (Bytef*)src; + stream.avail_in = src_sz; + stream.next_out = dst; + stream.avail_out = dst_sz; + + do { + ret = inflate(&stream, Z_FINISH); + switch (ret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + case Z_BUF_ERROR: + std::cerr << "Error decompressing data" << std::endl; + (void)inflateEnd(&stream); + return false; + default: + stream.next_out = dst + (dst_sz - stream.avail_out); + break; + } + } while (stream.avail_out > 0); + + (void)inflateEnd(&stream); + + return true; +} + +void deflate_compress(const uint8_t* data, size_t data_sz, int /*image_bpp*/, Stream* out) +{ + MemoryStream mem_in(data, data_sz); + z_deflate(&mem_in, out); +} + +void inflate_decompress(uint8_t* data, size_t data_sz, int /*image_bpp*/, Stream* in, size_t in_sz) +{ + std::vector in_buf(in_sz); + in->Read(in_buf.data(), in_sz); + z_inflate(in_buf.data(), in_sz, data, data_sz); +} diff --git a/Common/util/compress.h b/Common/util/compress.h index f3c0a6736db..e5c9551e783 100644 --- a/Common/util/compress.h +++ b/Common/util/compress.h @@ -41,4 +41,8 @@ void save_lzw(Common::Stream *out, const Common::Bitmap *bmpp, const RGB (*pal)[ // Loads bitmap decompressing std::unique_ptr load_lzw(Common::Stream *in, int dst_bpp, RGB (*pal)[256] = nullptr); +// Deflate compression +void deflate_compress(const uint8_t* data, size_t data_sz, int image_bpp, Common::Stream* out); +void inflate_decompress(uint8_t* data, size_t data_sz, int image_bpp, Common::Stream* in, size_t in_sz); + #endif // __AC_COMPRESS_H diff --git a/Common/util/lzw.cpp b/Common/util/lzw.cpp index 24e16f6163a..1ef332c7da0 100644 --- a/Common/util/lzw.cpp +++ b/Common/util/lzw.cpp @@ -22,11 +22,6 @@ using namespace AGS::Common; -#ifdef _MANAGED -// ensure this doesn't get compiled to .NET IL -#pragma unmanaged -#endif - int insert(int, int); void _delete(int); diff --git a/Common/util/lzw.h b/Common/util/lzw.h index a1d705e6ac4..fbd8ae6c6e9 100644 --- a/Common/util/lzw.h +++ b/Common/util/lzw.h @@ -19,11 +19,9 @@ #define __AGS_CN_UTIL__LZW_H #include "core/types.h" +#include "util/stream.h" -namespace AGS { namespace Common { class Stream; } } -using namespace AGS; // FIXME later - -bool lzwcompress(Common::Stream *lzw_in, Common::Stream *out); +bool lzwcompress(AGS::Common::Stream *lzw_in, AGS::Common::Stream *out); // Expands lzw-compressed data from src to dst. // the dst buffer should be large enough, or the uncompression will not be complete. bool lzwexpand(const uint8_t *src, size_t src_sz, uint8_t *dst, size_t dst_sz); diff --git a/Editor/AGS.Types/Enums/SpriteCompression.cs b/Editor/AGS.Types/Enums/SpriteCompression.cs index 80305c104ee..1861e2c787f 100644 --- a/Editor/AGS.Types/Enums/SpriteCompression.cs +++ b/Editor/AGS.Types/Enums/SpriteCompression.cs @@ -6,6 +6,7 @@ public enum SpriteCompression { None, RLE, - LZW + LZW, + Deflate } } diff --git a/Engine/Makefile-defs.linux b/Engine/Makefile-defs.linux index 6e0af65d5fc..bd5b44972e8 100644 --- a/Engine/Makefile-defs.linux +++ b/Engine/Makefile-defs.linux @@ -22,6 +22,7 @@ CXXFLAGS := -std=c++11 -Werror=delete-non-virtual-dtor $(CXXFLAGS) LIBS += $(FT_LDFLAGS) LIBS += $(shell pkg-config --libs ogg) LIBS += $(shell pkg-config --libs theora) +LIBS += $(shell pkg-config --libs zlib) ifeq ($(USE_TREMOR), 1) LIBS += -lvorbisidec diff --git a/Engine/Makefile-defs.osx b/Engine/Makefile-defs.osx index ef9269caf3e..738a8797ae4 100644 --- a/Engine/Makefile-defs.osx +++ b/Engine/Makefile-defs.osx @@ -36,6 +36,7 @@ LIBS := -framework Cocoa \ LIBS += $(FT_LDFLAGS) LIBS += $(shell pkg-config --libs ogg) LIBS += $(shell pkg-config --libs theora) +LIBS += $(shell pkg-config --libs zlib) ifeq ($(USE_TREMOR), 1) LIBS += -lvorbisidec diff --git a/Solutions/Common.Lib/Common.Lib.vcxproj b/Solutions/Common.Lib/Common.Lib.vcxproj index d99beffbf45..86cf6276506 100644 --- a/Solutions/Common.Lib/Common.Lib.vcxproj +++ b/Solutions/Common.Lib/Common.Lib.vcxproj @@ -128,39 +128,51 @@ + + + + + + + + + + + + diff --git a/Solutions/zlib.props b/Solutions/zlib.props new file mode 100644 index 00000000000..fbb1dd1b6a2 --- /dev/null +++ b/Solutions/zlib.props @@ -0,0 +1,33 @@ + + + + + + + + $(AGS_ZLIB_INCLUDE);%(AdditionalIncludeDirectories) + + + $(AGS_ZLIB_LIB);%(AdditionalLibraryDirectories) + zlib.lib;%(AdditionalDependencies) + + + $(AGS_ZLIB_LIB);%(AdditionalLibraryDirectories) + zlib.lib;%(AdditionalDependencies) + + + + + $(AGS_ZLIB_INCLUDE);%(AdditionalIncludeDirectories) + + + $(AGS_ZLIB_LIB_X64);$(AGS_ZLIB_LIB)\..\x64;%(AdditionalLibraryDirectories) + zlib.lib;%(AdditionalDependencies) + + + $(AGS_ZLIB_LIB_X64);$(AGS_ZLIB_LIB)\..\x64;%(AdditionalLibraryDirectories) + zlib.lib;%(AdditionalDependencies) + + + + \ No newline at end of file diff --git a/ci/emscripten/Dockerfile b/ci/emscripten/Dockerfile index 226f6acafb8..266d77d17e0 100644 --- a/ci/emscripten/Dockerfile +++ b/ci/emscripten/Dockerfile @@ -108,8 +108,8 @@ RUN echo "## Create emscripten user (1000:1000)" \ # ------------------------------------------------------------------------------ -RUN embuilder.py build --lto libstubs libc libc++ libc++abi sdl2 ogg vorbis -RUN embuilder.py build libstubs libc libc++ libc++abi sdl2 ogg vorbis +RUN embuilder.py build --lto libstubs libc libc++ libc++abi sdl2 ogg vorbis zlib +RUN embuilder.py build libstubs libc libc++ libc++abi sdl2 ogg vorbis zlib # ------------------------------------------------------------------------------ # Use commonly used /src as working directory diff --git a/ci/linux/Dockerfile b/ci/linux/Dockerfile index 0a3ac17f31c..4cfc028de9e 100644 --- a/ci/linux/Dockerfile +++ b/ci/linux/Dockerfile @@ -69,6 +69,19 @@ RUN curl -fLsS "https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSI ./configure --system-curl && make -j$(getconf _NPROCESSORS_ONLN) && make install && \ rm -r /tmp/cmake-$CMAKE_VERSION +# Build and install zlib-ng +ARG ZLIB_VERSION=2.1.3 +RUN cd /tmp && \ + curl -fLsS "https://github.com/zlib-ng/zlib-ng/archive/refs/tags/$ZLIB_VERSION.tar.gz" --output zlib.tar.gz && \ + tar -xvzf zlib.tar.gz && \ + mv zlib-ng-$ZLIB_VERSION zlib && \ + cd /tmp/zlib && \ + mkdir /tmp/zlib/build && \ + cd /tmp/zlib/build && \ + cmake -DZLIB_ENABLE_TESTS=0 -DZLIBNG_ENABLE_TESTS=0 -DWITH_GTEST=0 -DZLIB_COMPAT=1 .. && make -j$(getconf _NPROCESSORS_ONLN) && make install && \ + rm /usr/local/lib/libz.*so* && \ + rm -r /tmp/zlib + # Build newer libogg ARG LIBOGG_VERSION=1.3.5 RUN curl -fLsS "https://github.com/xiph/ogg/releases/download/v${LIBOGG_VERSION}/libogg-${LIBOGG_VERSION}.tar.xz" --output /tmp/libogg-${LIBOGG_VERSION}.tar.xz && \ diff --git a/ci/windows/Dockerfile b/ci/windows/Dockerfile index 9b110649684..8dc408453bf 100644 --- a/ci/windows/Dockerfile +++ b/ci/windows/Dockerfile @@ -13,6 +13,27 @@ RUN pushd %TEMP% && \ robocopy empty %TEMP% /MIR > nul & \ rd /s /q empty +ARG ZLIB_VERSION=2.1.3 +RUN mkdir Lib\zlib && \ + echo "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 ^&^& pushd Lib\zlib\build_x86 ^&^& msbuild zlib.sln /p:PlatformToolset=v140 /p:Configuration=Release /p:Platform=Win32 /maxcpucount /nologo ^&^& popd > zlibvcbuild_x86.bat && \ + echo "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 ^&^& pushd Lib\zlib\build_x64 ^&^& msbuild zlib.sln /p:PlatformToolset=v140 /p:Configuration=Release /p:Platform=x64 /maxcpucount /nologo ^&^& popd > zlibvcbuild_x64.bat && \ + mkdir Lib\zlib\build_x86 && \ + mkdir Lib\zlib\build_x64 && \ + curl -fLSs "https://github.com/zlib-ng/zlib-ng/archive/refs/tags/%ZLIB_VERSION%.tar.gz" | tar -f - -xvzC Lib\zlib --strip-components 1 && \ + cmake -DCMAKE_SYSTEM_VERSION=8.1 -DZLIB_ENABLE_TESTS=0 -DZLIBNG_ENABLE_TESTS=0 -DWITH_GTEST=0 -DZLIB_COMPAT=1 -S Lib\zlib -B Lib\zlib\build_x86 -G "Visual Studio 14 2015" -T"v140" -A"Win32" && \ + zlibvcbuild_x86.bat && \ + cmake -DCMAKE_SYSTEM_VERSION=8.1 -DZLIB_ENABLE_TESTS=0 -DZLIBNG_ENABLE_TESTS=0 -DWITH_GTEST=0 -DZLIB_COMPAT=1 -S Lib\zlib -B Lib\zlib\build_x64 -G "Visual Studio 14 2015" -T"v140" -A"x64" && \ + zlibvcbuild_x64.bat && \ + mkdir Lib\zlib\include && \ + copy Lib\zlib\build_x86\zlib.h Lib\zlib\include\zlib.h && \ + copy Lib\zlib\build_x86\zconf.h Lib\zlib\include\zconf.h && \ + copy Lib\zlib\build_x86\zlib_name_mangling.h Lib\zlib\include\zlib_name_mangling.h && \ + mkdir Lib\zlib\lib && \ + mkdir Lib\zlib\lib\x86 && \ + mkdir Lib\zlib\lib\x64 && \ + copy Lib\zlib\build_x86\Release\zlibstatic.lib Lib\zlib\lib\x86\zlib.lib && \ + copy Lib\zlib\build_x64\Release\zlibstatic.lib Lib\zlib\lib\x64\zlib.lib + ARG NUGET_VERSION=5.7.0 ARG INNO_SETUP_VERSION=6.0.5 RUN cinst nuget.commandline --version %NUGET_VERSION% -y && \