Skip to content

Commit

Permalink
Add zStandard support
Browse files Browse the repository at this point in the history
Enable zstd support By -DWANT_ZSTD=On (Off by default).
Uses the exported targets produced by the cmake build script of zstd.
Use ZSTD_PREFER_STATIC=Off to link with the shared lib version of zstd.
  • Loading branch information
baylej committed Jan 16, 2021
1 parent 6c2d555 commit 3881e6b
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 10 deletions.
14 changes: 14 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ set(API_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
set(BUILD_VERSION "${PROJECT_VERSION}")

option(WANT_ZLIB "use zlib (ability to decompress layers data) ?" On)
option(WANT_ZSTD "use zstd (ability to decompress layers data) ?" Off)
option(BUILD_SHARED_LIBS "Build shared libraries (dll / so)" Off)
option(ZSTD_PREFER_STATIC "use the static build of zstd ?" On)

set(EMSCRIPTEN False)
if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
Expand Down Expand Up @@ -48,6 +50,18 @@ else()
message("Zlib not wanted")
endif()

if(WANT_ZSTD)
target_compile_definitions(tmx PRIVATE WANT_ZSTD)
find_package(zstd REQUIRED)
if (ZSTD_PREFER_STATIC)
target_link_libraries(tmx zstd::libzstd_static)
else()
target_link_libraries(tmx zstd::libzstd_shared)
endif()
else()
message("zstd not wanted")
endif()

include(FindLibXml2)
find_package(LibXml2 REQUIRED)
target_link_libraries(tmx LibXml2::LibXml2)
Expand Down
75 changes: 75 additions & 0 deletions examples/data/b64zstd.tmx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.2" tiledversion="1.3.5" orientation="orthogonal" renderorder="right-down" width="28" height="18" tilewidth="32" tileheight="32" infinite="0" backgroundcolor="#ffff7f" nextlayerid="5" nextobjectid="5">
<properties>
<property name="alt" type="file" value="csv.tmx"/>
<property name="bool_false" type="bool" value="false"/>
<property name="bool_true" type="bool" value="true"/>
<property name="colour" type="color" value="#cc1a1a1a"/>
<property name="multilines">foo
bar
baz</property>
<property name="pi" type="float" value="3.14"/>
<property name="xml" value="libxml2"/>
</properties>
<tileset firstgid="1" name="base" tilewidth="32" tileheight="32" spacing="1" tilecount="9" columns="3">
<image source="numbers.png" width="100" height="100"/>
<tile id="0">
<properties>
<property name="number" type="int" value="1"/>
</properties>
<objectgroup draworder="index">
<object id="0" x="11.75" y="4.75" width="10.25" height="25.25"/>
</objectgroup>
</tile>
<tile id="1">
<properties>
<property name="number" type="int" value="2"/>
</properties>
</tile>
<tile id="2">
<properties>
<property name="number" type="int" value="3"/>
</properties>
</tile>
<tile id="4" type="five"/>
<tile id="6">
<animation>
<frame tileid="0" duration="200"/>
<frame tileid="1" duration="300"/>
<frame tileid="2" duration="400"/>
<frame tileid="3" duration="500"/>
<frame tileid="4" duration="600"/>
<frame tileid="5" duration="700"/>
<frame tileid="6" duration="2000"/>
</animation>
</tile>
</tileset>
<group id="1" name="Group">
<imagelayer id="2" name="Image">
<image source="bg.jpg" width="896" height="576"/>
<properties>
<property name="alt" value="rainbow"/>
</properties>
</imagelayer>
<layer id="3" name="Layer" width="28" height="18">
<data encoding="base64" compression="zstd">
KLUv/WDgBt0FAHQCBQAAAAMFBgMBAwQDCAYDAQQGCQMCCQcDCQUGCQcIAgUDAwQFAAAAYKBgPQEAgwpmDgI4Afyc+zIh+QH/mAjS3eL3YCA++G4W88cZ7WXdp3Q+buteXb5C+/Bb19DMQ3iBGb1CHFyS3Bn4APOSLyLFnjOyM2LFHxYYPBiciXaDO4yw8f6IIfC8nRWgF/LEHAzOuBr6uIszJIFNgvSemyl6SmpbfgLe7qwNjH+USwh7g6Vl+vJaq0vg9Pw=
</data>
</layer>
</group>
<objectgroup color="#aa0000" id="4" name="Objects">
<object id="1" name="square" type="spawn" x="128" y="128" width="192" height="192" rotation="22.5"/>
<object id="2" name="polygon" x="492" y="325">
<polygon points="20,-5 -44,-197 180,-229"/>
</object>
<object id="3" name="polyline" x="174" y="477">
<polyline points="-14,3 50,-61 114,3 178,-61 242,3 306,-61 370,3"/>
</object>
<object id="4" name="ellipse" x="672" y="352" width="160" height="160">
<ellipse/>
</object>
<object id="5" name="text" x="4" y="0" width="110" height="20" rotation="10">
<text wrap="1" color="#ff0000" bold="1" italic="1">Hello World</text>
</object>
</objectgroup>
</map>
1 change: 1 addition & 0 deletions src/tmx.h
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ typedef enum _tmx_error_codes {
E_BDATA = 20, /* B64 bad data */
E_ZDATA = 21, /* Zlib corrupted data */
E_XDATA = 22, /* XML corrupted data */
E_ZSDATA = 23, /* Zstd corrupted data */
E_CDATA = 24, /* CSV corrupted data */
E_MISSEL = 30 /* Missing element, incomplete source */
} tmx_error_codes;
Expand Down
64 changes: 57 additions & 7 deletions src/tmx_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,51 @@ char* zlib_decompress(const char *source, unsigned int slength, unsigned int rle

#endif /* WANT_ZLIB */

#ifdef WANT_ZSTD

#include <zstd.h>

char* zstd_decompress(const char *source, unsigned int slength, unsigned int rlength) {
char *res;
size_t ret;

if (!source) {
tmx_err(E_INVAL, "zstd_decompress: invalid argument: source is NULL");
return NULL;
}

res = (char*) tmx_alloc_func(NULL, rlength);
if (!res) {
tmx_errno = E_ALLOC;
return NULL;
}

ret = ZSTD_decompress(res, rlength, source, slength);

if (ZSTD_isError(ret)) {
tmx_err(E_ZSDATA, "zstd_decompress: %s\n", ZSTD_getErrorName(ret));
goto cleanup;
}
if (ret < rlength) {
tmx_err(E_ZSDATA, "layer contains not enough tiles");
goto cleanup;
}

return res;
cleanup:
tmx_free_func(res);
return NULL;
}

#else

char* zstd_decompress(const char *source, unsigned int slength, unsigned int rlength) {
tmx_err(E_FONCT, "This library was not built with zstd support");
return NULL;
}

#endif /* WANT_ZSTD */

/*
Layer data decoders
*/
Expand All @@ -237,14 +282,19 @@ int data_decode(const char *source, enum enccmp_t type, size_t gids_count, uint3
source++;
}
}
else if (type==B64Z) {
else if (type==B64 || type==B64Z || type==B64ZSTD) {
if (!(b64dec = b64_decode(source, &b64_len))) return 0;
*gids = (uint32_t*)zlib_decompress(b64dec, b64_len, (unsigned int)(gids_count*sizeof(int32_t)));
tmx_free_func(b64dec);
if (!(*gids)) return 0;
}
else if (type==B64) {
*gids = (uint32_t*)b64_decode(source, &b64_len);
if (type==B64ZSTD) {
*gids = (uint32_t*)zstd_decompress(b64dec, b64_len, (unsigned int)(gids_count*sizeof(int32_t)));
tmx_free_func(b64dec);
}
else if (type==B64Z) {
*gids = (uint32_t*)zlib_decompress(b64dec, b64_len, (unsigned int)(gids_count*sizeof(int32_t)));
tmx_free_func(b64dec);
}
else {
*gids = (uint32_t*)b64dec;
}
if (!(*gids)) return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion src/tmx_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ void property_deallocator(void *val, const char *key);
*/
#define MAX(a,b) (a<b) ? b: a;

enum enccmp_t {CSV, B64Z, B64};
enum enccmp_t { CSV, B64Z, B64, B64ZSTD };
int data_decode(const char *source, enum enccmp_t type, size_t gids_count, uint32_t **gids);

void map_post_parsing(tmx_map **map);
Expand Down
10 changes: 8 additions & 2 deletions src/tmx_xml.c
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ static int parse_object(xmlTextReaderPtr reader, tmx_object *obj, int is_on_map,

static int parse_data(xmlTextReaderPtr reader, uint32_t **gidsadr, size_t gidscount) {
char *value, *inner_xml;
enum enccmp_t data_type;

if (!(value = (char*)xmlTextReaderGetAttribute(reader, (xmlChar*)"encoding"))) { /* encoding */
tmx_err(E_MISSEL, "xml parser: missing 'encoding' attribute in the 'data' element");
Expand All @@ -391,11 +392,16 @@ static int parse_data(xmlTextReaderPtr reader, uint32_t **gidsadr, size_t gidsco
tmx_free_func(value);
value = (char*)xmlTextReaderGetAttribute(reader, (xmlChar*)"compression"); /* compression */

if (value && strcmp(value, "zlib") && strcmp(value, "gzip")) {
data_type = B64;
if (value && !strcmp(value, "zstd")) {
data_type = B64ZSTD;
} else if (value && !(strcmp(value, "zlib") && strcmp(value, "gzip"))) {
data_type = B64Z;
} else {
tmx_err(E_ENCCMP, "xml parser: unsupported data compression: '%s'", value); /* unsupported compression */
goto cleanup;
}
if (!data_decode(str_trim(inner_xml), value ? B64Z : B64, gidscount, gidsadr)) goto cleanup;
if (!data_decode(str_trim(inner_xml), data_type, gidscount, gidsadr)) goto cleanup;

} else if (!strcmp(value, "xml")) {
tmx_err(E_ENCCMP, "xml parser: unimplemented data encoding: XML");
Expand Down
5 changes: 5 additions & 0 deletions tmxConfig.cmake.in
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
include(CMakeFindDependencyMacro)
find_dependency(LibXml2)

if(@WANT_ZLIB@ AND NOT @EMSCRIPTEN@)
find_dependency(ZLIB)
endif()

if(@WANT_ZSTD@)
find_dependency(ZSTD)
endif()

include("${CMAKE_CURRENT_LIST_DIR}/tmxExports.cmake")

0 comments on commit 3881e6b

Please sign in to comment.