Skip to content

Commit 6230161

Browse files
authored
Add zlib compression support (PR #4080)
1 parent 1dff560 commit 6230161

File tree

5 files changed

+265
-3
lines changed

5 files changed

+265
-3
lines changed

Shared/mods/deathmatch/logic/Enums.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ ADD_ENUM(StringEncodeFunction::AES128, "aes128")
6969
ADD_ENUM(StringEncodeFunction::RSA, "rsa")
7070
ADD_ENUM(StringEncodeFunction::BASE64, "base64")
7171
ADD_ENUM(StringEncodeFunction::BASE32, "base32")
72+
ADD_ENUM(StringEncodeFunction::ZLIB, "zlib")
7273
IMPLEMENT_ENUM_CLASS_END("string-encode-function")
7374

7475
IMPLEMENT_ENUM_CLASS_BEGIN(KeyPairAlgorithm)
@@ -84,6 +85,20 @@ ADD_ENUM(HmacAlgorithm::SHA384, "sha384")
8485
ADD_ENUM(HmacAlgorithm::SHA512, "sha512")
8586
IMPLEMENT_ENUM_CLASS_END("hmac-algorithm")
8687

88+
IMPLEMENT_ENUM_CLASS_BEGIN(ZLibFormat)
89+
ADD_ENUM(ZLibFormat::ZRAW, "raw")
90+
ADD_ENUM(ZLibFormat::ZLIB, "zlib")
91+
ADD_ENUM(ZLibFormat::GZIP, "gzip")
92+
IMPLEMENT_ENUM_CLASS_END("zlib-format")
93+
94+
IMPLEMENT_ENUM_CLASS_BEGIN(ZLibStrategy)
95+
ADD_ENUM(ZLibStrategy::DEFAULT, "default")
96+
ADD_ENUM(ZLibStrategy::FILTERED, "filtered")
97+
ADD_ENUM(ZLibStrategy::HUFFMAN_ONLY, "huffman")
98+
ADD_ENUM(ZLibStrategy::RLE, "rle")
99+
ADD_ENUM(ZLibStrategy::FIXED, "fixed")
100+
IMPLEMENT_ENUM_CLASS_END("zlib-strategy")
101+
87102
IMPLEMENT_ENUM_CLASS_BEGIN(WorldSpecialProperty)
88103
ADD_ENUM(WorldSpecialProperty::HOVERCARS, "hovercars")
89104
ADD_ENUM(WorldSpecialProperty::AIRCARS, "aircars")

Shared/mods/deathmatch/logic/Enums.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ DECLARE_ENUM_CLASS(PasswordHashFunction);
7272
DECLARE_ENUM_CLASS(StringEncodeFunction);
7373
DECLARE_ENUM_CLASS(KeyPairAlgorithm);
7474
DECLARE_ENUM_CLASS(HmacAlgorithm);
75+
DECLARE_ENUM_CLASS(ZLibFormat);
76+
DECLARE_ENUM_CLASS(ZLibStrategy);
7577

7678
enum class WorldSpecialProperty
7779
{

Shared/mods/deathmatch/logic/luadefs/CLuaCryptDefs.cpp

Lines changed: 150 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,8 @@ int CLuaCryptDefs::EncodeString(lua_State* luaVM)
403403
argStream.ReadEnumString(algorithm);
404404
argStream.ReadString(data);
405405

406-
if ((algorithm != StringEncodeFunction::BASE64 && algorithm != StringEncodeFunction::BASE32) || argStream.NextIsTable())
406+
if ((algorithm != StringEncodeFunction::BASE64 && algorithm != StringEncodeFunction::BASE32 && algorithm != StringEncodeFunction::ZLIB) ||
407+
argStream.NextIsTable())
407408
{
408409
argStream.ReadStringMap(options);
409410
}
@@ -727,6 +728,88 @@ int CLuaCryptDefs::EncodeString(lua_State* luaVM)
727728
}
728729
return 1;
729730
}
731+
case StringEncodeFunction::ZLIB:
732+
{
733+
int compression = 9;
734+
int format = (int)ZLibFormat::GZIP;
735+
ZLibStrategy strategy = ZLibStrategy::DEFAULT;
736+
if (!options["format"].empty() && !StringToEnum(options["format"], (ZLibFormat&)format) && !StringToZLibFormat(options["format"], format))
737+
{
738+
m_pScriptDebugging->LogCustom(luaVM, "Invalid value for field 'format'");
739+
lua::Push(luaVM, false);
740+
return 1;
741+
}
742+
if (!options["strategy"].empty() && !StringToEnum(options["strategy"], strategy))
743+
{
744+
m_pScriptDebugging->LogCustom(luaVM, "Invalid value for field 'strategy'");
745+
lua::Push(luaVM, false);
746+
return 1;
747+
}
748+
if (!options["compression"].empty())
749+
{
750+
compression = atoi(options["compression"].c_str());
751+
if (compression < 0 || compression > 9)
752+
{
753+
m_pScriptDebugging->LogCustom(luaVM, "Value for field 'compression' is out of range (0-9)");
754+
lua::Push(luaVM, false);
755+
return 1;
756+
}
757+
}
758+
759+
// Async
760+
if (VERIFY_FUNCTION(luaFunctionRef))
761+
{
762+
CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM);
763+
if (pLuaMain)
764+
{
765+
CLuaShared::GetAsyncTaskScheduler()->PushTask(
766+
[data, format, compression, strategy]
767+
{
768+
// Execute time-consuming task
769+
SString output;
770+
int result = SharedUtil::ZLibCompress(data, output, format, compression, strategy);
771+
if (result == Z_STREAM_END)
772+
return std::make_pair(output, true);
773+
else
774+
return std::make_pair(SString("zlib error: %i", result), false);
775+
},
776+
[luaFunctionRef](const std::pair<SString, bool>& result)
777+
{
778+
CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaFunctionRef.GetLuaVM());
779+
if (pLuaMain)
780+
{
781+
CLuaArguments arguments;
782+
if (result.second)
783+
{
784+
arguments.PushString(result.first);
785+
arguments.Call(pLuaMain, luaFunctionRef);
786+
}
787+
else
788+
{
789+
m_pScriptDebugging->LogWarning(luaFunctionRef.GetLuaVM(), result.first.c_str());
790+
arguments.PushBoolean(false);
791+
arguments.Call(pLuaMain, luaFunctionRef);
792+
}
793+
}
794+
});
795+
796+
lua_pushboolean(luaVM, true);
797+
}
798+
}
799+
else // Sync
800+
{
801+
SString output;
802+
int result = SharedUtil::ZLibCompress(data, output, format, compression, strategy);
803+
if (result == Z_STREAM_END)
804+
lua::Push(luaVM, output);
805+
else
806+
{
807+
m_pScriptDebugging->LogWarning(luaVM, "zlib error: %i", result);
808+
lua::Push(luaVM, false);
809+
}
810+
}
811+
return 1;
812+
}
730813
default:
731814
{
732815
m_pScriptDebugging->LogCustom(luaVM, "Unknown encryption algorithm");
@@ -753,7 +836,8 @@ int CLuaCryptDefs::DecodeString(lua_State* luaVM)
753836
argStream.ReadEnumString(algorithm);
754837
argStream.ReadString(data);
755838

756-
if ((algorithm != StringEncodeFunction::BASE64 && algorithm != StringEncodeFunction::BASE32) || argStream.NextIsTable())
839+
if ((algorithm != StringEncodeFunction::BASE64 && algorithm != StringEncodeFunction::BASE32 && algorithm != StringEncodeFunction::ZLIB) ||
840+
argStream.NextIsTable())
757841
{
758842
argStream.ReadStringMap(options);
759843
}
@@ -1084,6 +1168,70 @@ int CLuaCryptDefs::DecodeString(lua_State* luaVM)
10841168
}
10851169
return 1;
10861170
}
1171+
case StringEncodeFunction::ZLIB:
1172+
{
1173+
int format = 0;
1174+
if (!options["format"].empty() && !StringToEnum(options["format"], (ZLibFormat&)format) && !StringToZLibFormat(options["format"], format))
1175+
{
1176+
m_pScriptDebugging->LogCustom(luaVM, "Not supported value for field 'format'");
1177+
lua::Push(luaVM, false);
1178+
return 1;
1179+
}
1180+
1181+
// Async
1182+
if (VERIFY_FUNCTION(luaFunctionRef))
1183+
{
1184+
CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM);
1185+
if (pLuaMain)
1186+
{
1187+
CLuaShared::GetAsyncTaskScheduler()->PushTask(
1188+
[data, format]
1189+
{
1190+
// Execute time-consuming task
1191+
SString output;
1192+
int result = SharedUtil::ZLibUncompress(data, output, format);
1193+
if (result == Z_STREAM_END)
1194+
return std::make_pair(output, true);
1195+
else
1196+
return std::make_pair(SString("zlib error: %i", result), false);
1197+
},
1198+
[luaFunctionRef](const std::pair<SString, bool>& result)
1199+
{
1200+
CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaFunctionRef.GetLuaVM());
1201+
if (pLuaMain)
1202+
{
1203+
CLuaArguments arguments;
1204+
if (result.second)
1205+
{
1206+
arguments.PushString(result.first);
1207+
arguments.Call(pLuaMain, luaFunctionRef);
1208+
}
1209+
else
1210+
{
1211+
m_pScriptDebugging->LogWarning(luaFunctionRef.GetLuaVM(), result.first.c_str());
1212+
arguments.PushBoolean(false);
1213+
arguments.Call(pLuaMain, luaFunctionRef);
1214+
}
1215+
}
1216+
});
1217+
1218+
lua_pushboolean(luaVM, true);
1219+
}
1220+
}
1221+
else // Sync
1222+
{
1223+
SString output;
1224+
int result = SharedUtil::ZLibUncompress(data, output, format);
1225+
if (result == Z_STREAM_END)
1226+
lua::Push(luaVM, output);
1227+
else
1228+
{
1229+
m_pScriptDebugging->LogWarning(luaVM, "zlib error: %i", result);
1230+
lua::Push(luaVM, false);
1231+
}
1232+
}
1233+
return 1;
1234+
}
10871235
default:
10881236
{
10891237
m_pScriptDebugging->LogCustom(luaVM, "Unknown encryption algorithm");

Shared/sdk/SharedUtil.Crypto.h

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <cryptopp/hmac.h>
1818
#include <cryptopp/hex.h>
1919
#include <cryptopp/md5.h>
20+
#include <zlib/zlib.h>
2021
#include "SString.h"
2122

2223
namespace SharedUtil
@@ -202,4 +203,83 @@ namespace SharedUtil
202203

203204
return result;
204205
}
206+
207+
inline bool StringToZLibFormat(const std::string& format, int& outResult)
208+
{
209+
int value = atoi(format.c_str());
210+
if ((value >= 9 && value <= 31) || (value >= -15 && value <= -9)) // allowed values: 9..31, -9..-15
211+
{
212+
outResult = value;
213+
return true;
214+
}
215+
return false;
216+
}
217+
218+
inline int ZLibCompress(const std::string& input, std::string& output, const int windowBits = (int)ZLibFormat::GZIP, const int compression = 9,
219+
const ZLibStrategy strategy = ZLibStrategy::DEFAULT)
220+
{
221+
z_stream stream{};
222+
223+
int result = deflateInit2(&stream, compression, Z_DEFLATED, windowBits, MAX_MEM_LEVEL, (int)strategy);
224+
if (result != Z_OK)
225+
return result;
226+
227+
output.resize(deflateBound(&stream, input.size())); // resize to the upper bound of what the compressed size might be
228+
229+
stream.next_out = (Bytef*)output.data();
230+
stream.avail_out = output.size();
231+
232+
stream.next_in = (z_const Bytef*)input.data();
233+
stream.avail_in = input.size();
234+
235+
result = deflate(&stream, Z_FINISH);
236+
result |= deflateEnd(&stream);
237+
238+
if (result == Z_STREAM_END)
239+
output.resize(stream.total_out); // resize to the actual size
240+
241+
return result;
242+
}
243+
244+
inline int ZLibUncompress(const std::string& input, std::string& output, int windowBits = 0)
245+
{
246+
if (windowBits == 0 && input.size() >= 2) // try to determine format automatically
247+
{
248+
if (input[0] == '\x1F' && input[1] == '\x8B')
249+
windowBits = (int)ZLibFormat::GZIP;
250+
else if (input[0] == '\x78')
251+
windowBits = (int)ZLibFormat::ZLIB;
252+
else
253+
windowBits = (int)ZLibFormat::ZRAW;
254+
}
255+
z_stream stream{};
256+
257+
int result = inflateInit2(&stream, windowBits);
258+
if (result != Z_OK)
259+
return result;
260+
261+
stream.next_in = (z_const Bytef*)input.data();
262+
stream.avail_in = input.size();
263+
264+
// Uncompress in chunks
265+
std::string buffer;
266+
buffer.resize(std::min(stream.avail_in, 128000U)); // use input length for chunk size (capped to 128k bytes which should be efficient enough)
267+
while (true)
268+
{
269+
stream.next_out = (Bytef*)buffer.data();
270+
stream.avail_out = buffer.size();
271+
272+
result = inflate(&stream, Z_NO_FLUSH);
273+
if (result != Z_OK && result != Z_STREAM_END)
274+
break;
275+
276+
output.append(buffer, 0, stream.total_out - output.size()); // append only what was written to buffer
277+
278+
if (result == Z_STREAM_END)
279+
break;
280+
}
281+
result |= inflateEnd(&stream);
282+
return result;
283+
}
284+
205285
} // namespace SharedUtil

Shared/sdk/SharedUtil.Hash.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ enum class StringEncodeFunction
4949
AES128,
5050
RSA,
5151
BASE64,
52-
BASE32
52+
BASE32,
53+
ZLIB,
5354
};
5455

5556
enum class KeyPairAlgorithm
@@ -67,6 +68,22 @@ enum class HmacAlgorithm
6768
SHA512,
6869
};
6970

71+
enum class ZLibFormat
72+
{
73+
ZRAW = -15,
74+
ZLIB = 15,
75+
GZIP = 31,
76+
};
77+
78+
enum class ZLibStrategy
79+
{
80+
DEFAULT,
81+
FILTERED,
82+
HUFFMAN_ONLY,
83+
RLE,
84+
FIXED,
85+
};
86+
7087
namespace SharedUtil
7188
{
7289
struct MD5

0 commit comments

Comments
 (0)