1 /*****************************************************************************
2  * Copyright (c) 2014-2020 OpenRCT2 developers
3  *
4  * For a complete list of all authors, please refer to contributors.md
5  * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6  *
7  * OpenRCT2 is licensed under the GNU General Public License version 3.
8  *****************************************************************************/
9 
10 #include "Util.h"
11 
12 #include "../common.h"
13 #include "../core/Guard.hpp"
14 #include "../interface/Window.h"
15 #include "../localisation/Localisation.h"
16 #include "../platform/platform.h"
17 #include "../title/TitleScreen.h"
18 #include "zlib.h"
19 
20 #include <algorithm>
21 #include <cctype>
22 #include <cmath>
23 #include <ctime>
24 #include <random>
25 
squaredmetres_to_squaredfeet(int32_t squaredMetres)26 int32_t squaredmetres_to_squaredfeet(int32_t squaredMetres)
27 {
28     // 1 metre squared = 10.7639104 feet squared
29     // RCT2 approximates as 11
30     return squaredMetres * 11;
31 }
32 
metres_to_feet(int32_t metres)33 int32_t metres_to_feet(int32_t metres)
34 {
35     // 1 metre = 3.2808399 feet
36     // RCT2 approximates as 3.28125
37     return (metres * 840) / 256;
38 }
39 
mph_to_kmph(int32_t mph)40 int32_t mph_to_kmph(int32_t mph)
41 {
42     // 1 mph = 1.60934 kmph
43     // RCT2 approximates as 1.609375
44     return (mph * 1648) >> 10;
45 }
46 
mph_to_dmps(int32_t mph)47 int32_t mph_to_dmps(int32_t mph)
48 {
49     // 1 mph = 4.4704 decimeters/s
50     return (mph * 73243) >> 14;
51 }
52 
filename_valid_characters(const utf8 * filename)53 bool filename_valid_characters(const utf8* filename)
54 {
55     for (int32_t i = 0; filename[i] != '\0'; i++)
56     {
57         if (filename[i] == '\\' || filename[i] == '/' || filename[i] == ':' || filename[i] == '?' || filename[i] == '*'
58             || filename[i] == '<' || filename[i] == '>' || filename[i] == '|')
59             return false;
60     }
61     return true;
62 }
63 
path_get_directory(const utf8 * path)64 utf8* path_get_directory(const utf8* path)
65 {
66     // Find the last slash or backslash in the path
67     char* filename = const_cast<char*>(strrchr(path, *PATH_SEPARATOR));
68     char* filename_posix = const_cast<char*>(strrchr(path, '/'));
69     filename = filename < filename_posix ? filename_posix : filename;
70 
71     // If the path is invalid (e.g. just a file name), return NULL
72     if (filename == nullptr)
73     {
74         return nullptr;
75     }
76 
77     char* directory = _strdup(path);
78     safe_strtrunc(directory, strlen(path) - strlen(filename) + 2);
79 
80     return directory;
81 }
82 
path_get_filename(const utf8 * path)83 const char* path_get_filename(const utf8* path)
84 {
85     // Find last slash or backslash in the path
86     char* filename = const_cast<char*>(strrchr(path, *PATH_SEPARATOR));
87     char* filename_posix = const_cast<char*>(strchr(path, '/'));
88     filename = filename < filename_posix ? filename_posix : filename;
89 
90     // Checks if the path is valid (e.g. not just a file name)
91     if (filename == nullptr)
92     {
93         // Return the input string to keep things working
94         return path;
95     }
96 
97     // Increase pointer by one, to get rid of the slashes
98     filename++;
99 
100     return filename;
101 }
102 
103 // Returns the extension (dot inclusive) from the given path, or the end of the
104 // string when no extension was found.
path_get_extension(const utf8 * path)105 const char* path_get_extension(const utf8* path)
106 {
107     // Get the filename from the path
108     const char* filename = path_get_filename(path);
109 
110     // Try to find the most-right dot in the filename
111     char* extension = const_cast<char*>(strrchr(filename, '.'));
112 
113     // When no dot was found, return a pointer to the null-terminator
114     if (extension == nullptr)
115         extension = const_cast<char*>(strrchr(filename, '\0'));
116 
117     return extension;
118 }
119 
path_set_extension(utf8 * path,const utf8 * newExtension,size_t size)120 void path_set_extension(utf8* path, const utf8* newExtension, size_t size)
121 {
122     // Remove existing extension (check first if there is one)
123     if (path_get_extension(path) < strrchr(path, '\0'))
124         path_remove_extension(path);
125     // Append new extension
126     path_append_extension(path, newExtension, size);
127 }
128 
path_append_extension(utf8 * path,const utf8 * newExtension,size_t size)129 void path_append_extension(utf8* path, const utf8* newExtension, size_t size)
130 {
131     // Skip to the dot if the extension starts with a pattern (starts with "*.")
132     if (newExtension[0] == '*')
133         newExtension++;
134 
135     // Append a dot to the filename if the new extension doesn't start with it
136     if (newExtension[0] != '.')
137         safe_strcat(path, ".", size);
138 
139     // Append the extension to the path
140     safe_strcat(path, newExtension, size);
141 }
142 
path_remove_extension(utf8 * path)143 void path_remove_extension(utf8* path)
144 {
145     // Find last dot in filename, and replace it with a null-terminator
146     char* lastDot = const_cast<char*>(strrchr(path_get_filename(path), '.'));
147     if (lastDot != nullptr)
148         *lastDot = '\0';
149     else
150         log_warning("No extension found. (path = %s)", path);
151 }
152 
path_end_with_separator(utf8 * path,size_t size)153 void path_end_with_separator(utf8* path, size_t size)
154 {
155     size_t length = strnlen(path, size);
156     if (length >= size - 1)
157         return;
158 
159     if ((length == 0) || ((path[length - 1] != *PATH_SEPARATOR) && path[length - 1] != '/'))
160     {
161         safe_strcat(path, PATH_SEPARATOR, size);
162     }
163 }
164 
bitscanforward(int32_t source)165 int32_t bitscanforward(int32_t source)
166 {
167 #if defined(_MSC_VER) && (_MSC_VER >= 1400) // Visual Studio 2005
168     DWORD i;
169     uint8_t success = _BitScanForward(&i, static_cast<uint32_t>(source));
170     return success != 0 ? i : -1;
171 #elif defined(__GNUC__)
172     int32_t success = __builtin_ffs(source);
173     return success - 1;
174 #else
175 #    pragma message("Falling back to iterative bitscan forward, consider using intrinsics")
176     // This is a low-hanging optimisation boost, check if your compiler offers
177     // any intrinsic.
178     // cf. https://github.com/OpenRCT2/OpenRCT2/pull/2093
179     for (int32_t i = 0; i < 32; i++)
180         if (source & (1u << i))
181             return i;
182 
183     return -1;
184 #endif
185 }
186 
bitscanforward(int64_t source)187 int32_t bitscanforward(int64_t source)
188 {
189 #if defined(_MSC_VER) && (_MSC_VER >= 1400) && defined(_M_X64) // Visual Studio 2005
190     DWORD i;
191     uint8_t success = _BitScanForward64(&i, static_cast<uint64_t>(source));
192     return success != 0 ? i : -1;
193 #elif defined(__GNUC__)
194     int32_t success = __builtin_ffsll(source);
195     return success - 1;
196 #else
197 #    pragma message("Falling back to iterative bitscan forward, consider using intrinsics")
198     // This is a low-hanging optimisation boost, check if your compiler offers
199     // any intrinsic.
200     // cf. https://github.com/OpenRCT2/OpenRCT2/pull/2093
201     for (int32_t i = 0; i < 64; i++)
202         if (source & (1ull << i))
203             return i;
204 
205     return -1;
206 #endif
207 }
208 
209 #if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
210 #    include <cpuid.h>
211 #    define OpenRCT2_CPUID_GNUC_X86
212 #elif defined(_MSC_VER) && (_MSC_VER >= 1500) && (defined(_M_X64) || defined(_M_IX86)) // VS2008
213 #    include <intrin.h>
214 #    include <nmmintrin.h>
215 #    define OpenRCT2_CPUID_MSVC_X86
216 #endif
217 
218 #ifdef OPENRCT2_X86
cpuid_x86(uint32_t * cpuid_outdata,int32_t eax)219 static bool cpuid_x86(uint32_t* cpuid_outdata, int32_t eax)
220 {
221 #    if defined(OpenRCT2_CPUID_GNUC_X86)
222     int ret = __get_cpuid(eax, &cpuid_outdata[0], &cpuid_outdata[1], &cpuid_outdata[2], &cpuid_outdata[3]);
223     return ret == 1;
224 #    elif defined(OpenRCT2_CPUID_MSVC_X86)
225     __cpuid(reinterpret_cast<int*>(cpuid_outdata), static_cast<int>(eax));
226     return true;
227 #    else
228     return false;
229 #    endif
230 }
231 #endif // OPENRCT2_X86
232 
sse41_available()233 bool sse41_available()
234 {
235 #ifdef OPENRCT2_X86
236     // SSE4.1 support is declared as the 19th bit of ECX with CPUID(EAX = 1).
237     uint32_t regs[4] = { 0 };
238     if (cpuid_x86(regs, 1))
239     {
240         return (regs[2] & (1 << 19));
241     }
242 #endif
243     return false;
244 }
245 
avx2_available()246 bool avx2_available()
247 {
248 #ifdef OPENRCT2_X86
249     // For GCC and similar use the builtin function, as cpuid changed its semantics in
250     // https://github.com/gcc-mirror/gcc/commit/132fa33ce998df69a9f793d63785785f4b93e6f1
251     // which causes it to ignore subleafs, but the new function is unavailable on
252     // Ubuntu 18.04's toolchains.
253 #    if defined(OpenRCT2_CPUID_GNUC_X86) && (!defined(__FreeBSD__) || (__FreeBSD__ > 10))
254     return __builtin_cpu_supports("avx2");
255 #    else
256     // AVX2 support is declared as the 5th bit of EBX with CPUID(EAX = 7, ECX = 0).
257     uint32_t regs[4] = { 0 };
258     if (cpuid_x86(regs, 7))
259     {
260         bool avxCPUSupport = (regs[1] & (1 << 5)) != 0;
261         if (avxCPUSupport)
262         {
263             // Need to check if OS also supports the register of xmm/ymm
264             // This check has to be conditional, otherwise INVALID_INSTRUCTION exception.
265             uint64_t xcrFeatureMask = _xgetbv(_XCR_XFEATURE_ENABLED_MASK);
266             avxCPUSupport = (xcrFeatureMask & 0x6) || false;
267         }
268         return avxCPUSupport;
269     }
270 #    endif
271 #endif
272     return false;
273 }
274 
bitcount_popcnt_available()275 static bool bitcount_popcnt_available()
276 {
277 #ifdef OPENRCT2_X86
278     // POPCNT support is declared as the 23rd bit of ECX with CPUID(EAX = 1).
279     uint32_t regs[4] = { 0 };
280     if (cpuid_x86(regs, 1))
281     {
282         return (regs[2] & (1 << 23));
283     }
284 #endif
285     return false;
286 }
287 
bitcount_popcnt(uint32_t source)288 static int32_t bitcount_popcnt(uint32_t source)
289 {
290 // Use CPUID defines to figure out calling style
291 #if defined(OpenRCT2_CPUID_GNUC_X86)
292     // use asm directly in order to actually emit the instruction : using
293     // __builtin_popcount results in an extra call to a library function.
294     int32_t rv;
295     asm volatile("popcnt %1,%0" : "=r"(rv) : "rm"(source) : "cc");
296     return rv;
297 #elif defined(OpenRCT2_CPUID_MSVC_X86)
298     return _mm_popcnt_u32(source);
299 #else
300     openrct2_assert(false, "bitcount_popcnt() called, without support compiled in");
301     return INT_MAX;
302 #endif
303 }
304 
bitcount_lut(uint32_t source)305 static int32_t bitcount_lut(uint32_t source)
306 {
307     // https://graphics.stanford.edu/~seander/bithacks.html
308     static constexpr const uint8_t BitsSetTable256[256] = {
309 #define B2(n) n, (n) + 1, (n) + 1, (n) + 2
310 #define B4(n) B2(n), B2((n) + 1), B2((n) + 1), B2((n) + 2)
311 #define B6(n) B4(n), B4((n) + 1), B4((n) + 1), B4((n) + 2)
312         B6(0), B6(1), B6(1), B6(2)
313     };
314     return BitsSetTable256[source & 0xff] + BitsSetTable256[(source >> 8) & 0xff] + BitsSetTable256[(source >> 16) & 0xff]
315         + BitsSetTable256[source >> 24];
316 }
317 
318 static int32_t (*bitcount_fn)(uint32_t);
319 
bitcount_init()320 void bitcount_init()
321 {
322     bitcount_fn = bitcount_popcnt_available() ? bitcount_popcnt : bitcount_lut;
323 }
324 
bitcount(uint32_t source)325 int32_t bitcount(uint32_t source)
326 {
327     return bitcount_fn(source);
328 }
329 
330 /* case insensitive compare */
strcicmp(char const * a,char const * b)331 int32_t strcicmp(char const* a, char const* b)
332 {
333     for (;; a++, b++)
334     {
335         int32_t d = tolower(static_cast<unsigned char>(*a)) - tolower(static_cast<unsigned char>(*b));
336         if (d != 0 || !*a)
337             return d;
338     }
339 }
340 
341 /* Case insensitive logical compare */
342 // Example:
343 // - Guest 10
344 // - Guest 99
345 // - Guest 100
346 // - John v2.0
347 // - John v2.1
strlogicalcmp(const char * s1,const char * s2)348 int32_t strlogicalcmp(const char* s1, const char* s2)
349 {
350     for (;;)
351     {
352         if (*s2 == '\0')
353             return *s1 != '\0';
354         if (*s1 == '\0')
355             return -1;
356         if (!(isdigit(static_cast<unsigned char>(*s1)) && isdigit(static_cast<unsigned char>(*s2))))
357         {
358             if (toupper(*s1) != toupper(*s2))
359                 return toupper(*s1) - toupper(*s2);
360 
361             ++s1;
362             ++s2;
363         }
364         else
365         {
366             char *lim1, *lim2;
367             unsigned long n1 = strtoul(s1, &lim1, 10);
368             unsigned long n2 = strtoul(s2, &lim2, 10);
369             if (n1 > n2)
370                 return 1;
371             if (n1 < n2)
372                 return -1;
373 
374             s1 = lim1;
375             s2 = lim2;
376         }
377     }
378 }
379 
safe_strtrunc(utf8 * text,size_t size)380 utf8* safe_strtrunc(utf8* text, size_t size)
381 {
382     assert(text != nullptr);
383 
384     if (size == 0)
385         return text;
386 
387     const char* sourceLimit = text + size - 1;
388     char* ch = text;
389     char* last = text;
390     while (utf8_get_next(ch, const_cast<const utf8**>(&ch)) != 0)
391     {
392         if (ch <= sourceLimit)
393         {
394             last = ch;
395         }
396         else
397         {
398             break;
399         }
400     }
401     *last = 0;
402 
403     return text;
404 }
405 
safe_strcpy(char * destination,const char * source,size_t size)406 char* safe_strcpy(char* destination, const char* source, size_t size)
407 {
408     assert(destination != nullptr);
409     assert(source != nullptr);
410 
411     if (size == 0)
412         return destination;
413 
414     char* result = destination;
415 
416     bool truncated = false;
417     const char* sourceLimit = source + size - 1;
418     const char* ch = source;
419     uint32_t codepoint;
420     while ((codepoint = utf8_get_next(ch, &ch)) != 0)
421     {
422         if (ch <= sourceLimit)
423         {
424             destination = utf8_write_codepoint(destination, codepoint);
425         }
426         else
427         {
428             truncated = true;
429         }
430     }
431     *destination = 0;
432 
433     if (truncated)
434     {
435         log_warning("Truncating string \"%s\" to %d bytes.", result, size);
436     }
437     return result;
438 }
439 
safe_strcat(char * destination,const char * source,size_t size)440 char* safe_strcat(char* destination, const char* source, size_t size)
441 {
442     assert(destination != nullptr);
443     assert(source != nullptr);
444 
445     if (size == 0)
446     {
447         return destination;
448     }
449 
450     char* result = destination;
451 
452     size_t i;
453     for (i = 0; i < size; i++)
454     {
455         if (*destination == '\0')
456         {
457             break;
458         }
459 
460         destination++;
461     }
462 
463     bool terminated = false;
464     for (; i < size; i++)
465     {
466         if (*source != '\0')
467         {
468             *destination++ = *source++;
469         }
470         else
471         {
472             *destination = *source;
473             terminated = true;
474             break;
475         }
476     }
477 
478     if (!terminated)
479     {
480         result[size - 1] = '\0';
481         log_warning("Truncating string \"%s\" to %d bytes.", result, size);
482     }
483 
484     return result;
485 }
486 
safe_strcat_path(char * destination,const char * source,size_t size)487 char* safe_strcat_path(char* destination, const char* source, size_t size)
488 {
489     path_end_with_separator(destination, size);
490     if (source[0] == *PATH_SEPARATOR)
491     {
492         source = source + 1;
493     }
494     return safe_strcat(destination, source, size);
495 }
496 
497 #if defined(_WIN32)
strcasestr(const char * haystack,const char * needle)498 char* strcasestr(const char* haystack, const char* needle)
499 {
500     const char* p1 = haystack;
501     const char* p2 = needle;
502     const char* r = *p2 == 0 ? haystack : nullptr;
503 
504     while (*p1 != 0 && *p2 != 0)
505     {
506         if (tolower(static_cast<unsigned char>(*p1)) == tolower(static_cast<unsigned char>(*p2)))
507         {
508             if (r == nullptr)
509                 r = p1;
510             p2++;
511         }
512         else
513         {
514             p2 = needle;
515             if (r != nullptr)
516                 p1 = r + 1;
517 
518             if (tolower(static_cast<unsigned char>(*p1)) == tolower(static_cast<unsigned char>(*p2)))
519             {
520                 r = p1;
521                 p2++;
522             }
523             else
524             {
525                 r = nullptr;
526             }
527         }
528 
529         p1++;
530     }
531 
532     return *p2 == 0 ? const_cast<char*>(r) : nullptr;
533 }
534 #endif
535 
utf8_is_bom(const char * str)536 bool utf8_is_bom(const char* str)
537 {
538     return str[0] == static_cast<char>(static_cast<uint8_t>(0xEF)) && str[1] == static_cast<char>(static_cast<uint8_t>(0xBB))
539         && str[2] == static_cast<char>(static_cast<uint8_t>(0xBF));
540 }
541 
str_is_null_or_empty(const char * str)542 bool str_is_null_or_empty(const char* str)
543 {
544     return str == nullptr || str[0] == 0;
545 }
546 
util_rand()547 uint32_t util_rand()
548 {
549     thread_local std::mt19937 _prng(std::random_device{}());
550     return _prng();
551 }
552 
553 constexpr size_t CHUNK = 128 * 1024;
554 constexpr int32_t MAX_ZLIB_REALLOC = 4 * 1024 * 1024;
555 
556 /**
557  * @brief Inflates zlib-compressed data
558  * @param data Data to be decompressed
559  * @param data_in_size Size of data to be decompressed
560  * @param data_out_size Pointer to a variable where output size will be written. If not 0, it will be used to set initial output
561  * buffer size.
562  * @return Returns a pointer to memory holding decompressed data or NULL on failure.
563  * @note It is caller's responsibility to free() the returned pointer once done with it.
564  */
util_zlib_inflate(const uint8_t * data,size_t data_in_size,size_t * data_out_size)565 uint8_t* util_zlib_inflate(const uint8_t* data, size_t data_in_size, size_t* data_out_size)
566 {
567     int32_t ret = Z_OK;
568     uLongf out_size = static_cast<uLong>(*data_out_size);
569     if (out_size == 0)
570     {
571         // Try to guesstimate the size needed for output data by applying the
572         // same ratio it would take to compress data_in_size.
573         out_size = static_cast<uLong>(data_in_size) * static_cast<uLong>(data_in_size)
574             / compressBound(static_cast<uLong>(data_in_size));
575         out_size = std::min(static_cast<uLongf>(MAX_ZLIB_REALLOC), out_size);
576     }
577     uLongf buffer_size = out_size;
578     uint8_t* buffer = static_cast<uint8_t*>(malloc(buffer_size));
579     do
580     {
581         if (ret == Z_BUF_ERROR)
582         {
583             buffer_size *= 2;
584             out_size = buffer_size;
585             buffer = static_cast<uint8_t*>(realloc(buffer, buffer_size));
586         }
587         else if (ret == Z_STREAM_ERROR)
588         {
589             log_error("Your build is shipped with broken zlib. Please use the official build.");
590             free(buffer);
591             return nullptr;
592         }
593         else if (ret < 0)
594         {
595             log_error("Error uncompressing data.");
596             free(buffer);
597             return nullptr;
598         }
599         ret = uncompress(buffer, &out_size, data, static_cast<uLong>(data_in_size));
600     } while (ret != Z_OK);
601     buffer = static_cast<uint8_t*>(realloc(buffer, out_size));
602     *data_out_size = out_size;
603     return buffer;
604 }
605 
606 /**
607  * @brief Deflates input using zlib
608  * @param data Data to be compressed
609  * @param data_in_size Size of data to be compressed
610  * @return Returns an optional std::vector of bytes, which is equal to std::nullopt when deflate has failed
611  */
util_zlib_deflate(const uint8_t * data,size_t data_in_size)612 std::optional<std::vector<uint8_t>> util_zlib_deflate(const uint8_t* data, size_t data_in_size)
613 {
614     int32_t ret = Z_OK;
615     uLongf out_size = 0;
616     uLong buffer_size = compressBound(static_cast<uLong>(data_in_size));
617     std::vector<uint8_t> buffer(buffer_size);
618     do
619     {
620         if (ret == Z_BUF_ERROR)
621         {
622             buffer_size *= 2;
623             out_size = buffer_size;
624             buffer.resize(buffer_size);
625         }
626         else if (ret == Z_STREAM_ERROR)
627         {
628             log_error("Your build is shipped with broken zlib. Please use the official build.");
629             return std::nullopt;
630         }
631         ret = compress(buffer.data(), &out_size, data, static_cast<uLong>(data_in_size));
632     } while (ret != Z_OK);
633     buffer.resize(out_size);
634     return buffer;
635 }
636 
637 // Compress the source to gzip-compatible stream, write to dest.
638 // Mainly used for compressing the crashdumps
util_gzip_compress(FILE * source,FILE * dest)639 bool util_gzip_compress(FILE* source, FILE* dest)
640 {
641     if (source == nullptr || dest == nullptr)
642     {
643         return false;
644     }
645     int ret, flush;
646     size_t have;
647     z_stream strm{};
648     strm.zalloc = Z_NULL;
649     strm.zfree = Z_NULL;
650     strm.opaque = Z_NULL;
651     unsigned char in[CHUNK];
652     unsigned char out[CHUNK];
653     int windowBits = 15;
654     int GZIP_ENCODING = 16;
655     ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, windowBits | GZIP_ENCODING, 8, Z_DEFAULT_STRATEGY);
656     if (ret != Z_OK)
657     {
658         log_error("Failed to initialise stream");
659         return false;
660     }
661     do
662     {
663         strm.avail_in = uInt(fread(in, 1, CHUNK, source));
664         if (ferror(source))
665         {
666             deflateEnd(&strm);
667             log_error("Failed to read data from source");
668             return false;
669         }
670         flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
671         strm.next_in = in;
672         do
673         {
674             strm.avail_out = CHUNK;
675             strm.next_out = out;
676             ret = deflate(&strm, flush);
677             if (ret == Z_STREAM_ERROR)
678             {
679                 log_error("Failed to compress data");
680                 return false;
681             }
682             have = CHUNK - strm.avail_out;
683             if (fwrite(out, 1, have, dest) != have || ferror(dest))
684             {
685                 deflateEnd(&strm);
686                 log_error("Failed to write data to destination");
687                 return false;
688             }
689         } while (strm.avail_out == 0);
690     } while (flush != Z_FINISH);
691     deflateEnd(&strm);
692     return true;
693 }
694 
Gzip(const void * data,const size_t dataLen)695 std::vector<uint8_t> Gzip(const void* data, const size_t dataLen)
696 {
697     assert(data != nullptr);
698 
699     std::vector<uint8_t> output;
700     z_stream strm{};
701     strm.zalloc = Z_NULL;
702     strm.zfree = Z_NULL;
703     strm.opaque = Z_NULL;
704 
705     {
706         const auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY);
707         if (ret != Z_OK)
708         {
709             throw std::runtime_error("deflateInit2 failed with error " + std::to_string(ret));
710         }
711     }
712 
713     int flush = 0;
714     const auto* src = static_cast<const Bytef*>(data);
715     size_t srcRemaining = dataLen;
716     do
717     {
718         const auto nextBlockSize = std::min(srcRemaining, CHUNK);
719         srcRemaining -= nextBlockSize;
720 
721         flush = srcRemaining == 0 ? Z_FINISH : Z_NO_FLUSH;
722         strm.avail_in = static_cast<uInt>(nextBlockSize);
723         strm.next_in = const_cast<Bytef*>(src);
724         do
725         {
726             output.resize(output.size() + nextBlockSize);
727             strm.avail_out = static_cast<uInt>(nextBlockSize);
728             strm.next_out = &output[output.size() - nextBlockSize];
729             const auto ret = deflate(&strm, flush);
730             if (ret == Z_STREAM_ERROR)
731             {
732                 throw std::runtime_error("deflate failed with error " + std::to_string(ret));
733             }
734             output.resize(output.size() - strm.avail_out);
735         } while (strm.avail_out == 0);
736 
737         src += nextBlockSize;
738     } while (flush != Z_FINISH);
739     deflateEnd(&strm);
740     return output;
741 }
742 
Ungzip(const void * data,const size_t dataLen)743 std::vector<uint8_t> Ungzip(const void* data, const size_t dataLen)
744 {
745     assert(data != nullptr);
746 
747     std::vector<uint8_t> output;
748     z_stream strm{};
749     strm.zalloc = Z_NULL;
750     strm.zfree = Z_NULL;
751     strm.opaque = Z_NULL;
752 
753     {
754         const auto ret = inflateInit2(&strm, 15 | 16);
755         if (ret != Z_OK)
756         {
757             throw std::runtime_error("inflateInit2 failed with error " + std::to_string(ret));
758         }
759     }
760 
761     int flush = 0;
762     const auto* src = static_cast<const Bytef*>(data);
763     size_t srcRemaining = dataLen;
764     do
765     {
766         const auto nextBlockSize = std::min(srcRemaining, CHUNK);
767         srcRemaining -= nextBlockSize;
768 
769         flush = srcRemaining == 0 ? Z_FINISH : Z_NO_FLUSH;
770         strm.avail_in = static_cast<uInt>(nextBlockSize);
771         strm.next_in = const_cast<Bytef*>(src);
772         do
773         {
774             output.resize(output.size() + nextBlockSize);
775             strm.avail_out = static_cast<uInt>(nextBlockSize);
776             strm.next_out = &output[output.size() - nextBlockSize];
777             const auto ret = inflate(&strm, flush);
778             if (ret == Z_STREAM_ERROR)
779             {
780                 throw std::runtime_error("deflate failed with error " + std::to_string(ret));
781             }
782             output.resize(output.size() - strm.avail_out);
783         } while (strm.avail_out == 0);
784 
785         src += nextBlockSize;
786     } while (flush != Z_FINISH);
787     inflateEnd(&strm);
788     return output;
789 }
790 
791 // Type-independent code left as macro to reduce duplicate code.
792 #define add_clamp_body(value, value_to_add, min_cap, max_cap)                                                                  \
793     if ((value_to_add > 0) && (value > (max_cap - (value_to_add))))                                                            \
794     {                                                                                                                          \
795         value = max_cap;                                                                                                       \
796     }                                                                                                                          \
797     else if ((value_to_add < 0) && (value < (min_cap - (value_to_add))))                                                       \
798     {                                                                                                                          \
799         value = min_cap;                                                                                                       \
800     }                                                                                                                          \
801     else                                                                                                                       \
802     {                                                                                                                          \
803         value += value_to_add;                                                                                                 \
804     }
805 
add_clamp_int8_t(int8_t value,int8_t value_to_add)806 int8_t add_clamp_int8_t(int8_t value, int8_t value_to_add)
807 {
808     add_clamp_body(value, value_to_add, INT8_MIN, INT8_MAX);
809     return value;
810 }
811 
add_clamp_int16_t(int16_t value,int16_t value_to_add)812 int16_t add_clamp_int16_t(int16_t value, int16_t value_to_add)
813 {
814     add_clamp_body(value, value_to_add, INT16_MIN, INT16_MAX);
815     return value;
816 }
817 
add_clamp_int32_t(int32_t value,int32_t value_to_add)818 int32_t add_clamp_int32_t(int32_t value, int32_t value_to_add)
819 {
820     add_clamp_body(value, value_to_add, INT32_MIN, INT32_MAX);
821     return value;
822 }
823 
add_clamp_int64_t(int64_t value,int64_t value_to_add)824 int64_t add_clamp_int64_t(int64_t value, int64_t value_to_add)
825 {
826     add_clamp_body(value, value_to_add, INT64_MIN, INT64_MAX);
827     return value;
828 }
829 
add_clamp_money32(money32 value,money32 value_to_add)830 money32 add_clamp_money32(money32 value, money32 value_to_add)
831 {
832     // This function is intended only for clarity, as money32
833     // is technically the same as int32_t
834     assert_struct_size(money32, sizeof(int32_t));
835     return add_clamp_int32_t(value, value_to_add);
836 }
837 
add_clamp_money64(money64 value,money64 value_to_add)838 money32 add_clamp_money64(money64 value, money64 value_to_add)
839 {
840     // This function is intended only for clarity, as money64
841     // is technically the same as int64_t
842     assert_struct_size(money64, sizeof(int64_t));
843     return add_clamp_int64_t(value, value_to_add);
844 }
845 
846 #undef add_clamp_body
847 
lerp(uint8_t a,uint8_t b,float t)848 uint8_t lerp(uint8_t a, uint8_t b, float t)
849 {
850     if (t <= 0)
851         return a;
852     if (t >= 1)
853         return b;
854 
855     int32_t range = b - a;
856     int32_t amount = static_cast<int32_t>(range * t);
857     return static_cast<uint8_t>(a + amount);
858 }
859 
flerp(float a,float b,float f)860 float flerp(float a, float b, float f)
861 {
862     return (a * (1.0f - f)) + (b * f);
863 }
864 
soft_light(uint8_t a,uint8_t b)865 uint8_t soft_light(uint8_t a, uint8_t b)
866 {
867     float fa = a / 255.0f;
868     float fb = b / 255.0f;
869     float fr;
870     if (fb < 0.5f)
871     {
872         fr = (2 * fa * fb) + ((fa * fa) * (1 - (2 * fb)));
873     }
874     else
875     {
876         fr = (2 * fa * (1 - fb)) + (std::sqrt(fa) * ((2 * fb) - 1));
877     }
878     return static_cast<uint8_t>(std::clamp(fr, 0.0f, 1.0f) * 255.0f);
879 }
880 
881 /**
882  * strftime wrapper which appends to an existing string.
883  */
strcatftime(char * buffer,size_t bufferSize,const char * format,const struct tm * tp)884 size_t strcatftime(char* buffer, size_t bufferSize, const char* format, const struct tm* tp)
885 {
886     size_t stringLen = strnlen(buffer, bufferSize);
887     if (stringLen < bufferSize)
888     {
889         char* dst = buffer + stringLen;
890         size_t dstMaxSize = bufferSize - stringLen;
891         return strftime(dst, dstMaxSize, format, tp);
892     }
893     return 0;
894 }
895