diff --git a/Makefile b/Makefile
index fbac8db..3158861 100755
--- a/Makefile
+++ b/Makefile
@@ -79,8 +79,6 @@ else
endif
CPPFLAGS += -DHAVE_CEIL -DCP_USE_CGTYPES=0 -DCP_USE_DOUBLES=0 -DHAVE_FLOOR -DHAVE_FMOD -DHAVE_LRINT -DHAVE_LRINTF $(includeflag) -MD $(WARNING_FLAGS) -I. -DVER=\"$(SEM)\" -DCOM=\"$(COM)\" -DINFO=\"$(INFO)\" #-DENABLE_SINC_MEDIUM_CONVERTER -DENABLE_SINC_FAST_CONVERTER -DCP_COLLISION_TYPE_TYPE=uintptr_t -DCP_BITMASK_TYPE=uintptr_t
-
-CPPFLAGS += -D_FILE_OFFSET_BITS=64 # for tinycdb
CPPFLAGS += -DCONFIG_VERSION=\"2024-02-14\" -DCONFIG_BIGNUM #for quickjs
# ENABLE_SINC_[BEST|FAST|MEDIUM]_CONVERTER
@@ -149,14 +147,17 @@ endif
OBJS != find source -type f -name '*.c' | grep -vE 'test|tool|example|fuzz|main' | grep -vE 'quickjs'
CPPOBJS != find source -type f -name '*.cpp' | grep -vE 'test|tool|example|fuzz|main'
OBJS += $(CPPOBJS)
+OBJS += source/engine/yugine.c
OBJS += $(shell find source/engine -type f -name '*.m')
+QUICKJS := source/engine/thirdparty/quickjs
+OBJS += $(addprefix $(QUICKJS)/, libregexp.c quickjs.c libunicode.c cutils.c libbf.c)
OBJS := $(patsubst %.cpp, %$(INFO).o, $(OBJS))
OBJS := $(patsubst %.c, %$(INFO).o,$(OBJS))
OBJS := $(patsubst %.m, %$(INFO).o, $(OBJS))
engineincs != find source/engine -maxdepth 1 -type d
includeflag != find source -type d -name include
-includeflag += $(engineincs) source/engine/thirdparty/tinycdb source/shaders source/engine/thirdparty/sokol source/engine/thirdparty/stb source/engine/thirdparty/cgltf source/engine/thirdparty/TinySoundFont source/engine/thirdparty/dr_libs
+includeflag += $(engineincs) source/shaders source/engine/thirdparty/sokol source/engine/thirdparty/stb source/engine/thirdparty/cgltf source/engine/thirdparty/TinySoundFont source/engine/thirdparty/dr_libs
includeflag += $(STEAM)/public
includeflag += source
includeflag := $(addprefix -I, $(includeflag))
@@ -188,20 +189,12 @@ install: $(NAME)
@echo Copying to destination
cp -f $(NAME) $(DESTDIR)/$(APP)
-$(NAME): libengine$(INFO).a libquickjs$(INFO).a $(DEPS)
+$(NAME): $(OBJS) $(DEPS)
@echo Linking $(NAME)
$(CROSSWIN)$(LD) $^ $(CPPFLAGS) $(LDFLAGS) -L. $(LDPATHS) $(LDLIBS) -o $@
@echo Finished build
-libengine$(INFO).a: $(OBJS)
- @echo Archiving $@
- $(CROSSWIN)$(AR) rcs $@ $(OBJS)
-
-QUICKJS := source/engine/thirdparty/quickjs
-libquickjs$(INFO).a: $(QUICKJS)/libregexp$(INFO).o $(QUICKJS)/quickjs$(INFO).o $(QUICKJS)/libunicode$(INFO).o $(QUICKJS)/cutils$(INFO).o $(QUICKJS)/libbf$(INFO).o
- $(CROSSWIN)$(AR) rcs $@ $^
-
-%$(INFO).o: %.c $(SHADERS)
+%$(INFO).o: %.c source/engine/core.cdb.h $(SHADERS)
@echo Making C object $@
$(CROSSWIN)$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
@@ -224,38 +217,18 @@ SCRIPTS := $(shell ls scripts/*.js*)
CORE != (ls icons/* fonts/*)
CORE := $(CORE) $(SCRIPTS)
-CDB = source/engine/thirdparty/tinycdb
-CDB_C != find $(CDB) -name *.c
-CDB_O := $(patsubst %.c, %.o, $(CDB_C))
-CDB_O := $(notdir $(CDB_O))
-tools/libcdb.a: $(CDB_C)
- cc -c $^
- $(CROSSWIN)$(AR) rcs $@ $(CDB_O)
-
-cdb: tools/cdb.c tools/libcdb.a
- @echo Making cdb
- cc $^ -I$(CDB) -o cdb
-
-packer: tools/packer.c tools/libcdb.a
+packer$(EXT): tools/packer.c source/engine/miniz.c
@echo Making packer
- cc $^ -I$(CDB) -o packer
+ $(CC) -O2 $^ -Isource/engine -o packer
-core.cdb: packer $(CORE)
+core.cdb: packer$(EXT) $(CORE)
@echo Packing core.cdb
- ./packer $(CORE)
- chmod 644 out.cdb
- mv out.cdb core.cdb
+ ./packer$(EXT) $@ $(CORE)
source/engine/core.cdb.h: core.cdb
@echo Making $@
xxd -i $< > $@
-jsc: tools/jso.c tools/libquickjs.a
- $(CC) $^ -lm -Iquickjs -o $@
-
-tools/libquickjs.a: $(BIN)/libquickjs.a
- cp -f $(BIN)/libquickjs.a tools
-
ICNSIZE = 16 32 128 256 512 1024
ICNNAME := $(addsuffix .png, $(ICNSIZE))
ICON = icons/moon.gif
diff --git a/scripts/engine.js b/scripts/engine.js
index c7ee874..256a1f5 100644
--- a/scripts/engine.js
+++ b/scripts/engine.js
@@ -115,8 +115,6 @@ console.trace = console.stack;
var say = console.say;
var print = console.print;
-console.warn("GOT HERE");
-
console.doc = {
level: "Set level to output logging to console.",
info: "Output info level message.",
@@ -143,7 +141,7 @@ function use(file, env, script)
console.info(`CACHE eval ${file} in ${profile.best_t(profile.now()-st)}`);
return;
}
-
+ console.info(`slurping ${file}`);
script ??= io.slurp(file);
script = `(function() { ${script}; })`;
var fn = os.eval(file,script);
@@ -208,6 +206,7 @@ var timescale = 1;
var gggstart = game.engine_start;
game.engine_start = function(s) {
+ game.startengine = 1;
gggstart(function() {
world_start();
go_init();
@@ -216,6 +215,8 @@ game.engine_start = function(s) {
}, process);
}
+game.startengine = 0;
+
function process()
{
var dt = profile.secs(profile.now()) - frame_t;
diff --git a/scripts/std.js b/scripts/std.js
index 6e05414..f25ac55 100644
--- a/scripts/std.js
+++ b/scripts/std.js
@@ -278,7 +278,6 @@ Cmdline.register_order("play", function(argv) {
console.info(`Starting game with window size ${window.size} and render ${window.rendersize}.`);
game.engine_start(function() {
- console.info(`eng start`);
global.mixin("scripts/sound.js");
global.app = actor.spawn("game.js");
if (project.icon) window.set_icon(project.icon);
@@ -297,7 +296,7 @@ Cmdline.register_order("pack", function(str) {
say(`Packing into ${packname}`);
- io.pack_engine(packname);
+// io.pack_engine(packname);
io.chmod(packname, 666);
}, "Pack the game into the given name.", "NAME");
@@ -477,8 +476,10 @@ function cmd_args(cmdargs)
console.warn(`Command ${cmds[0]} not found.`);
return;
}
-
+
Cmdline.orders[cmds[0]](cmds.slice(1));
+ if (!game.startengine)
+ os.exit(0);
}
Cmdline.register_order("clean", function(argv) {
diff --git a/source/engine/config.c b/source/engine/config.c
index c420ab9..665da42 100644
--- a/source/engine/config.c
+++ b/source/engine/config.c
@@ -2,7 +2,6 @@
#define SOKOL_TRACE_HOOKS
#define SOKOL_IMPL
-#define SOKOL_NO_ENTRY
#include "sokol/sokol_audio.h"
#include "sokol/sokol_time.h"
#include "sokol/sokol_args.h"
diff --git a/source/engine/jsffi.c b/source/engine/jsffi.c
index d3e7adb..f4559b1 100644
--- a/source/engine/jsffi.c
+++ b/source/engine/jsffi.c
@@ -644,6 +644,7 @@ JSValue js_os_sys(JSContext *js, JSValue this, int argc, JSValue *argv)
}
JSC_CCALL(os_quit, quit();)
+JSC_CCALL(os_exit, exit(js2number(argv[0]));)
JSC_CCALL(os_reindex_static, cpSpaceReindexStatic(space));
JSC_CCALL(os_gc, script_gc());
JSC_SSCALL(os_eval, ret = script_eval(str, str2))
@@ -712,6 +713,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, sys, 0),
MIST_FUNC_DEF(os, system, 1),
MIST_FUNC_DEF(os, quit, 0),
+ MIST_FUNC_DEF(os, exit, 1),
MIST_FUNC_DEF(os, reindex_static, 0),
MIST_FUNC_DEF(os, gc, 0),
MIST_FUNC_DEF(os, capture, 5),
@@ -1007,8 +1009,6 @@ JSValue js_io_chmod(JSContext *js, JSValue this, int argc, JSValue *argv)
JSC_SCALL(io_save_qoa, save_qoa(str))
-JSC_SCALL(io_pack_engine, pack_engine(str))
-
static const JSCFunctionListEntry js_io_funcs[] = {
MIST_FUNC_DEF(io, exists,1),
MIST_FUNC_DEF(io, ls, 0),
@@ -1022,7 +1022,6 @@ static const JSCFunctionListEntry js_io_funcs[] = {
MIST_FUNC_DEF(io, slurpbytes, 1),
MIST_FUNC_DEF(io, slurpwrite, 2),
MIST_FUNC_DEF(io, save_qoa,1),
- MIST_FUNC_DEF(io, pack_engine, 1),
};
JSC_CCALL(debug_draw_gameobject, gameobject_draw_debug(js2gameobject(argv[0]));)
diff --git a/source/engine/miniz.c b/source/engine/miniz.c
new file mode 100644
index 0000000..8d0032f
--- /dev/null
+++ b/source/engine/miniz.c
@@ -0,0 +1,7833 @@
+#include "miniz.h"
+/**************************************************************************
+ *
+ * Copyright 2013-2014 RAD Game Tools and Valve Software
+ * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+
+typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1];
+typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1];
+typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1];
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ------------------- zlib-style API's */
+
+mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len)
+{
+ mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16);
+ size_t block_len = buf_len % 5552;
+ if (!ptr)
+ return MZ_ADLER32_INIT;
+ while (buf_len)
+ {
+ for (i = 0; i + 7 < block_len; i += 8, ptr += 8)
+ {
+ s1 += ptr[0], s2 += s1;
+ s1 += ptr[1], s2 += s1;
+ s1 += ptr[2], s2 += s1;
+ s1 += ptr[3], s2 += s1;
+ s1 += ptr[4], s2 += s1;
+ s1 += ptr[5], s2 += s1;
+ s1 += ptr[6], s2 += s1;
+ s1 += ptr[7], s2 += s1;
+ }
+ for (; i < block_len; ++i)
+ s1 += *ptr++, s2 += s1;
+ s1 %= 65521U, s2 %= 65521U;
+ buf_len -= block_len;
+ block_len = 5552;
+ }
+ return (s2 << 16) + s1;
+}
+
+/* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */
+#if 0
+ mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len)
+ {
+ static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+ 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c };
+ mz_uint32 crcu32 = (mz_uint32)crc;
+ if (!ptr)
+ return MZ_CRC32_INIT;
+ crcu32 = ~crcu32;
+ while (buf_len--)
+ {
+ mz_uint8 b = *ptr++;
+ crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)];
+ crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)];
+ }
+ return ~crcu32;
+ }
+#elif defined(USE_EXTERNAL_MZCRC)
+/* If USE_EXTERNAL_CRC is defined, an external module will export the
+ * mz_crc32() symbol for us to use, e.g. an SSE-accelerated version.
+ * Depending on the impl, it may be necessary to ~ the input/output crc values.
+ */
+mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len);
+#else
+/* Faster, but larger CPU cache footprint.
+ */
+mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len)
+{
+ static const mz_uint32 s_crc_table[256] =
+ {
+ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535,
+ 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD,
+ 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D,
+ 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
+ 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
+ 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
+ 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC,
+ 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+ 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB,
+ 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F,
+ 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB,
+ 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+ 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA,
+ 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE,
+ 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A,
+ 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
+ 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409,
+ 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
+ 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739,
+ 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
+ 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268,
+ 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0,
+ 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8,
+ 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF,
+ 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703,
+ 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7,
+ 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
+ 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE,
+ 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
+ 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6,
+ 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
+ 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D,
+ 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5,
+ 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605,
+ 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+ 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+ };
+
+ mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF;
+ const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr;
+
+ while (buf_len >= 4)
+ {
+ crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF];
+ crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF];
+ crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF];
+ crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF];
+ pByte_buf += 4;
+ buf_len -= 4;
+ }
+
+ while (buf_len)
+ {
+ crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF];
+ ++pByte_buf;
+ --buf_len;
+ }
+
+ return ~crc32;
+}
+#endif
+
+void mz_free(void *p)
+{
+ MZ_FREE(p);
+}
+
+MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size)
+{
+ (void)opaque, (void)items, (void)size;
+ return MZ_MALLOC(items * size);
+}
+MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address)
+{
+ (void)opaque, (void)address;
+ MZ_FREE(address);
+}
+MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size)
+{
+ (void)opaque, (void)address, (void)items, (void)size;
+ return MZ_REALLOC(address, items * size);
+}
+
+const char *mz_version(void)
+{
+ return MZ_VERSION;
+}
+
+#ifndef MINIZ_NO_ZLIB_APIS
+
+#ifndef MINIZ_NO_DEFLATE_APIS
+
+int mz_deflateInit(mz_streamp pStream, int level)
+{
+ return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY);
+}
+
+int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy)
+{
+ tdefl_compressor *pComp;
+ mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy);
+
+ if (!pStream)
+ return MZ_STREAM_ERROR;
+ if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)))
+ return MZ_PARAM_ERROR;
+
+ pStream->data_type = 0;
+ pStream->adler = MZ_ADLER32_INIT;
+ pStream->msg = NULL;
+ pStream->reserved = 0;
+ pStream->total_in = 0;
+ pStream->total_out = 0;
+ if (!pStream->zalloc)
+ pStream->zalloc = miniz_def_alloc_func;
+ if (!pStream->zfree)
+ pStream->zfree = miniz_def_free_func;
+
+ pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor));
+ if (!pComp)
+ return MZ_MEM_ERROR;
+
+ pStream->state = (struct mz_internal_state *)pComp;
+
+ if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY)
+ {
+ mz_deflateEnd(pStream);
+ return MZ_PARAM_ERROR;
+ }
+
+ return MZ_OK;
+}
+
+int mz_deflateReset(mz_streamp pStream)
+{
+ if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree))
+ return MZ_STREAM_ERROR;
+ pStream->total_in = pStream->total_out = 0;
+ tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags);
+ return MZ_OK;
+}
+
+int mz_deflate(mz_streamp pStream, int flush)
+{
+ size_t in_bytes, out_bytes;
+ mz_ulong orig_total_in, orig_total_out;
+ int mz_status = MZ_OK;
+
+ if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out))
+ return MZ_STREAM_ERROR;
+ if (!pStream->avail_out)
+ return MZ_BUF_ERROR;
+
+ if (flush == MZ_PARTIAL_FLUSH)
+ flush = MZ_SYNC_FLUSH;
+
+ if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE)
+ return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR;
+
+ orig_total_in = pStream->total_in;
+ orig_total_out = pStream->total_out;
+ for (;;)
+ {
+ tdefl_status defl_status;
+ in_bytes = pStream->avail_in;
+ out_bytes = pStream->avail_out;
+
+ defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush);
+ pStream->next_in += (mz_uint)in_bytes;
+ pStream->avail_in -= (mz_uint)in_bytes;
+ pStream->total_in += (mz_uint)in_bytes;
+ pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state);
+
+ pStream->next_out += (mz_uint)out_bytes;
+ pStream->avail_out -= (mz_uint)out_bytes;
+ pStream->total_out += (mz_uint)out_bytes;
+
+ if (defl_status < 0)
+ {
+ mz_status = MZ_STREAM_ERROR;
+ break;
+ }
+ else if (defl_status == TDEFL_STATUS_DONE)
+ {
+ mz_status = MZ_STREAM_END;
+ break;
+ }
+ else if (!pStream->avail_out)
+ break;
+ else if ((!pStream->avail_in) && (flush != MZ_FINISH))
+ {
+ if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out))
+ break;
+ return MZ_BUF_ERROR; /* Can't make forward progress without some input.
+ */
+ }
+ }
+ return mz_status;
+}
+
+int mz_deflateEnd(mz_streamp pStream)
+{
+ if (!pStream)
+ return MZ_STREAM_ERROR;
+ if (pStream->state)
+ {
+ pStream->zfree(pStream->opaque, pStream->state);
+ pStream->state = NULL;
+ }
+ return MZ_OK;
+}
+
+mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len)
+{
+ (void)pStream;
+ /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */
+ return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5);
+}
+
+int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level)
+{
+ int status;
+ mz_stream stream;
+ memset(&stream, 0, sizeof(stream));
+
+ /* In case mz_ulong is 64-bits (argh I hate longs). */
+ if ((mz_uint64)(source_len | *pDest_len) > 0xFFFFFFFFU)
+ return MZ_PARAM_ERROR;
+
+ stream.next_in = pSource;
+ stream.avail_in = (mz_uint32)source_len;
+ stream.next_out = pDest;
+ stream.avail_out = (mz_uint32)*pDest_len;
+
+ status = mz_deflateInit(&stream, level);
+ if (status != MZ_OK)
+ return status;
+
+ status = mz_deflate(&stream, MZ_FINISH);
+ if (status != MZ_STREAM_END)
+ {
+ mz_deflateEnd(&stream);
+ return (status == MZ_OK) ? MZ_BUF_ERROR : status;
+ }
+
+ *pDest_len = stream.total_out;
+ return mz_deflateEnd(&stream);
+}
+
+int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
+{
+ return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION);
+}
+
+mz_ulong mz_compressBound(mz_ulong source_len)
+{
+ return mz_deflateBound(NULL, source_len);
+}
+
+#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/
+
+#ifndef MINIZ_NO_INFLATE_APIS
+
+typedef struct
+{
+ tinfl_decompressor m_decomp;
+ mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed;
+ int m_window_bits;
+ mz_uint8 m_dict[TINFL_LZ_DICT_SIZE];
+ tinfl_status m_last_status;
+} inflate_state;
+
+int mz_inflateInit2(mz_streamp pStream, int window_bits)
+{
+ inflate_state *pDecomp;
+ if (!pStream)
+ return MZ_STREAM_ERROR;
+ if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))
+ return MZ_PARAM_ERROR;
+
+ pStream->data_type = 0;
+ pStream->adler = 0;
+ pStream->msg = NULL;
+ pStream->total_in = 0;
+ pStream->total_out = 0;
+ pStream->reserved = 0;
+ if (!pStream->zalloc)
+ pStream->zalloc = miniz_def_alloc_func;
+ if (!pStream->zfree)
+ pStream->zfree = miniz_def_free_func;
+
+ pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state));
+ if (!pDecomp)
+ return MZ_MEM_ERROR;
+
+ pStream->state = (struct mz_internal_state *)pDecomp;
+
+ tinfl_init(&pDecomp->m_decomp);
+ pDecomp->m_dict_ofs = 0;
+ pDecomp->m_dict_avail = 0;
+ pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT;
+ pDecomp->m_first_call = 1;
+ pDecomp->m_has_flushed = 0;
+ pDecomp->m_window_bits = window_bits;
+
+ return MZ_OK;
+}
+
+int mz_inflateInit(mz_streamp pStream)
+{
+ return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS);
+}
+
+int mz_inflateReset(mz_streamp pStream)
+{
+ inflate_state *pDecomp;
+ if (!pStream)
+ return MZ_STREAM_ERROR;
+
+ pStream->data_type = 0;
+ pStream->adler = 0;
+ pStream->msg = NULL;
+ pStream->total_in = 0;
+ pStream->total_out = 0;
+ pStream->reserved = 0;
+
+ pDecomp = (inflate_state *)pStream->state;
+
+ tinfl_init(&pDecomp->m_decomp);
+ pDecomp->m_dict_ofs = 0;
+ pDecomp->m_dict_avail = 0;
+ pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT;
+ pDecomp->m_first_call = 1;
+ pDecomp->m_has_flushed = 0;
+ /* pDecomp->m_window_bits = window_bits */;
+
+ return MZ_OK;
+}
+
+int mz_inflate(mz_streamp pStream, int flush)
+{
+ inflate_state *pState;
+ mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32;
+ size_t in_bytes, out_bytes, orig_avail_in;
+ tinfl_status status;
+
+ if ((!pStream) || (!pStream->state))
+ return MZ_STREAM_ERROR;
+ if (flush == MZ_PARTIAL_FLUSH)
+ flush = MZ_SYNC_FLUSH;
+ if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH))
+ return MZ_STREAM_ERROR;
+
+ pState = (inflate_state *)pStream->state;
+ if (pState->m_window_bits > 0)
+ decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER;
+ orig_avail_in = pStream->avail_in;
+
+ first_call = pState->m_first_call;
+ pState->m_first_call = 0;
+ if (pState->m_last_status < 0)
+ return MZ_DATA_ERROR;
+
+ if (pState->m_has_flushed && (flush != MZ_FINISH))
+ return MZ_STREAM_ERROR;
+ pState->m_has_flushed |= (flush == MZ_FINISH);
+
+ if ((flush == MZ_FINISH) && (first_call))
+ {
+ /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */
+ decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;
+ in_bytes = pStream->avail_in;
+ out_bytes = pStream->avail_out;
+ status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags);
+ pState->m_last_status = status;
+ pStream->next_in += (mz_uint)in_bytes;
+ pStream->avail_in -= (mz_uint)in_bytes;
+ pStream->total_in += (mz_uint)in_bytes;
+ pStream->adler = tinfl_get_adler32(&pState->m_decomp);
+ pStream->next_out += (mz_uint)out_bytes;
+ pStream->avail_out -= (mz_uint)out_bytes;
+ pStream->total_out += (mz_uint)out_bytes;
+
+ if (status < 0)
+ return MZ_DATA_ERROR;
+ else if (status != TINFL_STATUS_DONE)
+ {
+ pState->m_last_status = TINFL_STATUS_FAILED;
+ return MZ_BUF_ERROR;
+ }
+ return MZ_STREAM_END;
+ }
+ /* flush != MZ_FINISH then we must assume there's more input. */
+ if (flush != MZ_FINISH)
+ decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT;
+
+ if (pState->m_dict_avail)
+ {
+ n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
+ memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
+ pStream->next_out += n;
+ pStream->avail_out -= n;
+ pStream->total_out += n;
+ pState->m_dict_avail -= n;
+ pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
+ return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
+ }
+
+ for (;;)
+ {
+ in_bytes = pStream->avail_in;
+ out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs;
+
+ status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags);
+ pState->m_last_status = status;
+
+ pStream->next_in += (mz_uint)in_bytes;
+ pStream->avail_in -= (mz_uint)in_bytes;
+ pStream->total_in += (mz_uint)in_bytes;
+ pStream->adler = tinfl_get_adler32(&pState->m_decomp);
+
+ pState->m_dict_avail = (mz_uint)out_bytes;
+
+ n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
+ memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
+ pStream->next_out += n;
+ pStream->avail_out -= n;
+ pStream->total_out += n;
+ pState->m_dict_avail -= n;
+ pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
+
+ if (status < 0)
+ return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */
+ else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in))
+ return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */
+ else if (flush == MZ_FINISH)
+ {
+ /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */
+ if (status == TINFL_STATUS_DONE)
+ return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END;
+ /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */
+ else if (!pStream->avail_out)
+ return MZ_BUF_ERROR;
+ }
+ else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail))
+ break;
+ }
+
+ return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
+}
+
+int mz_inflateEnd(mz_streamp pStream)
+{
+ if (!pStream)
+ return MZ_STREAM_ERROR;
+ if (pStream->state)
+ {
+ pStream->zfree(pStream->opaque, pStream->state);
+ pStream->state = NULL;
+ }
+ return MZ_OK;
+}
+int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len)
+{
+ mz_stream stream;
+ int status;
+ memset(&stream, 0, sizeof(stream));
+
+ /* In case mz_ulong is 64-bits (argh I hate longs). */
+ if ((mz_uint64)(*pSource_len | *pDest_len) > 0xFFFFFFFFU)
+ return MZ_PARAM_ERROR;
+
+ stream.next_in = pSource;
+ stream.avail_in = (mz_uint32)*pSource_len;
+ stream.next_out = pDest;
+ stream.avail_out = (mz_uint32)*pDest_len;
+
+ status = mz_inflateInit(&stream);
+ if (status != MZ_OK)
+ return status;
+
+ status = mz_inflate(&stream, MZ_FINISH);
+ *pSource_len = *pSource_len - stream.avail_in;
+ if (status != MZ_STREAM_END)
+ {
+ mz_inflateEnd(&stream);
+ return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status;
+ }
+ *pDest_len = stream.total_out;
+
+ return mz_inflateEnd(&stream);
+}
+
+int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
+{
+ return mz_uncompress2(pDest, pDest_len, pSource, &source_len);
+}
+
+#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/
+
+const char *mz_error(int err)
+{
+ static struct
+ {
+ int m_err;
+ const char *m_pDesc;
+ } s_error_descs[] =
+ {
+ { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" }
+ };
+ mz_uint i;
+ for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i)
+ if (s_error_descs[i].m_err == err)
+ return s_error_descs[i].m_pDesc;
+ return NULL;
+}
+
+#endif /*MINIZ_NO_ZLIB_APIS */
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+ This is free and unencumbered software released into the public domain.
+
+ Anyone is free to copy, modify, publish, use, compile, sell, or
+ distribute this software, either in source code form or as a compiled
+ binary, for any purpose, commercial or non-commercial, and by any
+ means.
+
+ In jurisdictions that recognize copyright laws, the author or authors
+ of this software dedicate any and all copyright interest in the
+ software to the public domain. We make this dedication for the benefit
+ of the public at large and to the detriment of our heirs and
+ successors. We intend this dedication to be an overt act of
+ relinquishment in perpetuity of all present and future rights to this
+ software under copyright law.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+
+ For more information, please refer to
+*/
+/**************************************************************************
+ *
+ * Copyright 2013-2014 RAD Game Tools and Valve Software
+ * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+
+#ifndef MINIZ_NO_DEFLATE_APIS
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ------------------- Low-level Compression (independent from all decompression API's) */
+
+/* Purposely making these tables static for faster init and thread safety. */
+static const mz_uint16 s_tdefl_len_sym[256] =
+ {
+ 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272,
+ 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276,
+ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
+ 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280,
+ 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281,
+ 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282,
+ 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285
+ };
+
+static const mz_uint8 s_tdefl_len_extra[256] =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0
+ };
+
+static const mz_uint8 s_tdefl_small_dist_sym[512] =
+ {
+ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11,
+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17
+ };
+
+static const mz_uint8 s_tdefl_small_dist_extra[512] =
+ {
+ 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7
+ };
+
+static const mz_uint8 s_tdefl_large_dist_sym[128] =
+ {
+ 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
+ };
+
+static const mz_uint8 s_tdefl_large_dist_extra[128] =
+ {
+ 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13
+ };
+
+/* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */
+typedef struct
+{
+ mz_uint16 m_key, m_sym_index;
+} tdefl_sym_freq;
+static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1)
+{
+ mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2];
+ tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1;
+ MZ_CLEAR_ARR(hist);
+ for (i = 0; i < num_syms; i++)
+ {
+ mz_uint freq = pSyms0[i].m_key;
+ hist[freq & 0xFF]++;
+ hist[256 + ((freq >> 8) & 0xFF)]++;
+ }
+ while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256]))
+ total_passes--;
+ for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8)
+ {
+ const mz_uint32 *pHist = &hist[pass << 8];
+ mz_uint offsets[256], cur_ofs = 0;
+ for (i = 0; i < 256; i++)
+ {
+ offsets[i] = cur_ofs;
+ cur_ofs += pHist[i];
+ }
+ for (i = 0; i < num_syms; i++)
+ pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i];
+ {
+ tdefl_sym_freq *t = pCur_syms;
+ pCur_syms = pNew_syms;
+ pNew_syms = t;
+ }
+ }
+ return pCur_syms;
+}
+
+/* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */
+static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n)
+{
+ int root, leaf, next, avbl, used, dpth;
+ if (n == 0)
+ return;
+ else if (n == 1)
+ {
+ A[0].m_key = 1;
+ return;
+ }
+ A[0].m_key += A[1].m_key;
+ root = 0;
+ leaf = 2;
+ for (next = 1; next < n - 1; next++)
+ {
+ if (leaf >= n || A[root].m_key < A[leaf].m_key)
+ {
+ A[next].m_key = A[root].m_key;
+ A[root++].m_key = (mz_uint16)next;
+ }
+ else
+ A[next].m_key = A[leaf++].m_key;
+ if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key))
+ {
+ A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key);
+ A[root++].m_key = (mz_uint16)next;
+ }
+ else
+ A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key);
+ }
+ A[n - 2].m_key = 0;
+ for (next = n - 3; next >= 0; next--)
+ A[next].m_key = A[A[next].m_key].m_key + 1;
+ avbl = 1;
+ used = dpth = 0;
+ root = n - 2;
+ next = n - 1;
+ while (avbl > 0)
+ {
+ while (root >= 0 && (int)A[root].m_key == dpth)
+ {
+ used++;
+ root--;
+ }
+ while (avbl > used)
+ {
+ A[next--].m_key = (mz_uint16)(dpth);
+ avbl--;
+ }
+ avbl = 2 * used;
+ dpth++;
+ used = 0;
+ }
+}
+
+/* Limits canonical Huffman code table's max code size. */
+enum
+{
+ TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32
+};
+static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size)
+{
+ int i;
+ mz_uint32 total = 0;
+ if (code_list_len <= 1)
+ return;
+ for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++)
+ pNum_codes[max_code_size] += pNum_codes[i];
+ for (i = max_code_size; i > 0; i--)
+ total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i));
+ while (total != (1UL << max_code_size))
+ {
+ pNum_codes[max_code_size]--;
+ for (i = max_code_size - 1; i > 0; i--)
+ if (pNum_codes[i])
+ {
+ pNum_codes[i]--;
+ pNum_codes[i + 1] += 2;
+ break;
+ }
+ total--;
+ }
+}
+
+static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table)
+{
+ int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE];
+ mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1];
+ MZ_CLEAR_ARR(num_codes);
+ if (static_table)
+ {
+ for (i = 0; i < table_len; i++)
+ num_codes[d->m_huff_code_sizes[table_num][i]]++;
+ }
+ else
+ {
+ tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms;
+ int num_used_syms = 0;
+ const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0];
+ for (i = 0; i < table_len; i++)
+ if (pSym_count[i])
+ {
+ syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i];
+ syms0[num_used_syms++].m_sym_index = (mz_uint16)i;
+ }
+
+ pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1);
+ tdefl_calculate_minimum_redundancy(pSyms, num_used_syms);
+
+ for (i = 0; i < num_used_syms; i++)
+ num_codes[pSyms[i].m_key]++;
+
+ tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit);
+
+ MZ_CLEAR_ARR(d->m_huff_code_sizes[table_num]);
+ MZ_CLEAR_ARR(d->m_huff_codes[table_num]);
+ for (i = 1, j = num_used_syms; i <= code_size_limit; i++)
+ for (l = num_codes[i]; l > 0; l--)
+ d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i);
+ }
+
+ next_code[1] = 0;
+ for (j = 0, i = 2; i <= code_size_limit; i++)
+ next_code[i] = j = ((j + num_codes[i - 1]) << 1);
+
+ for (i = 0; i < table_len; i++)
+ {
+ mz_uint rev_code = 0, code, code_size;
+ if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0)
+ continue;
+ code = next_code[code_size]++;
+ for (l = code_size; l > 0; l--, code >>= 1)
+ rev_code = (rev_code << 1) | (code & 1);
+ d->m_huff_codes[table_num][i] = (mz_uint16)rev_code;
+ }
+}
+
+#define TDEFL_PUT_BITS(b, l) \
+ do \
+ { \
+ mz_uint bits = b; \
+ mz_uint len = l; \
+ MZ_ASSERT(bits <= ((1U << len) - 1U)); \
+ d->m_bit_buffer |= (bits << d->m_bits_in); \
+ d->m_bits_in += len; \
+ while (d->m_bits_in >= 8) \
+ { \
+ if (d->m_pOutput_buf < d->m_pOutput_buf_end) \
+ *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \
+ d->m_bit_buffer >>= 8; \
+ d->m_bits_in -= 8; \
+ } \
+ } \
+ MZ_MACRO_END
+
+#define TDEFL_RLE_PREV_CODE_SIZE() \
+ { \
+ if (rle_repeat_count) \
+ { \
+ if (rle_repeat_count < 3) \
+ { \
+ d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \
+ while (rle_repeat_count--) \
+ packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \
+ } \
+ else \
+ { \
+ d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \
+ packed_code_sizes[num_packed_code_sizes++] = 16; \
+ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \
+ } \
+ rle_repeat_count = 0; \
+ } \
+ }
+
+#define TDEFL_RLE_ZERO_CODE_SIZE() \
+ { \
+ if (rle_z_count) \
+ { \
+ if (rle_z_count < 3) \
+ { \
+ d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \
+ while (rle_z_count--) \
+ packed_code_sizes[num_packed_code_sizes++] = 0; \
+ } \
+ else if (rle_z_count <= 10) \
+ { \
+ d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \
+ packed_code_sizes[num_packed_code_sizes++] = 17; \
+ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \
+ } \
+ else \
+ { \
+ d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \
+ packed_code_sizes[num_packed_code_sizes++] = 18; \
+ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \
+ } \
+ rle_z_count = 0; \
+ } \
+ }
+
+static const mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+
+static void tdefl_start_dynamic_block(tdefl_compressor *d)
+{
+ int num_lit_codes, num_dist_codes, num_bit_lengths;
+ mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index;
+ mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF;
+
+ d->m_huff_count[0][256] = 1;
+
+ tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE);
+ tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE);
+
+ for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--)
+ if (d->m_huff_code_sizes[0][num_lit_codes - 1])
+ break;
+ for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--)
+ if (d->m_huff_code_sizes[1][num_dist_codes - 1])
+ break;
+
+ memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes);
+ memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes);
+ total_code_sizes_to_pack = num_lit_codes + num_dist_codes;
+ num_packed_code_sizes = 0;
+ rle_z_count = 0;
+ rle_repeat_count = 0;
+
+ memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2);
+ for (i = 0; i < total_code_sizes_to_pack; i++)
+ {
+ mz_uint8 code_size = code_sizes_to_pack[i];
+ if (!code_size)
+ {
+ TDEFL_RLE_PREV_CODE_SIZE();
+ if (++rle_z_count == 138)
+ {
+ TDEFL_RLE_ZERO_CODE_SIZE();
+ }
+ }
+ else
+ {
+ TDEFL_RLE_ZERO_CODE_SIZE();
+ if (code_size != prev_code_size)
+ {
+ TDEFL_RLE_PREV_CODE_SIZE();
+ d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1);
+ packed_code_sizes[num_packed_code_sizes++] = code_size;
+ }
+ else if (++rle_repeat_count == 6)
+ {
+ TDEFL_RLE_PREV_CODE_SIZE();
+ }
+ }
+ prev_code_size = code_size;
+ }
+ if (rle_repeat_count)
+ {
+ TDEFL_RLE_PREV_CODE_SIZE();
+ }
+ else
+ {
+ TDEFL_RLE_ZERO_CODE_SIZE();
+ }
+
+ tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE);
+
+ TDEFL_PUT_BITS(2, 2);
+
+ TDEFL_PUT_BITS(num_lit_codes - 257, 5);
+ TDEFL_PUT_BITS(num_dist_codes - 1, 5);
+
+ for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--)
+ if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]])
+ break;
+ num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1));
+ TDEFL_PUT_BITS(num_bit_lengths - 4, 4);
+ for (i = 0; (int)i < num_bit_lengths; i++)
+ TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3);
+
+ for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;)
+ {
+ mz_uint code = packed_code_sizes[packed_code_sizes_index++];
+ MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2);
+ TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]);
+ if (code >= 16)
+ TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]);
+ }
+}
+
+static void tdefl_start_static_block(tdefl_compressor *d)
+{
+ mz_uint i;
+ mz_uint8 *p = &d->m_huff_code_sizes[0][0];
+
+ for (i = 0; i <= 143; ++i)
+ *p++ = 8;
+ for (; i <= 255; ++i)
+ *p++ = 9;
+ for (; i <= 279; ++i)
+ *p++ = 7;
+ for (; i <= 287; ++i)
+ *p++ = 8;
+
+ memset(d->m_huff_code_sizes[1], 5, 32);
+
+ tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE);
+ tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE);
+
+ TDEFL_PUT_BITS(1, 2);
+}
+
+static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
+
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS
+static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
+{
+ mz_uint flags;
+ mz_uint8 *pLZ_codes;
+ mz_uint8 *pOutput_buf = d->m_pOutput_buf;
+ mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf;
+ mz_uint64 bit_buffer = d->m_bit_buffer;
+ mz_uint bits_in = d->m_bits_in;
+
+#define TDEFL_PUT_BITS_FAST(b, l) \
+ { \
+ bit_buffer |= (((mz_uint64)(b)) << bits_in); \
+ bits_in += (l); \
+ }
+
+ flags = 1;
+ for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1)
+ {
+ if (flags == 1)
+ flags = *pLZ_codes++ | 0x100;
+
+ if (flags & 1)
+ {
+ mz_uint s0, s1, n0, n1, sym, num_extra_bits;
+ mz_uint match_len = pLZ_codes[0];
+ mz_uint match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8));
+ pLZ_codes += 3;
+
+ MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
+ TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
+ TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);
+
+ /* This sequence coaxes MSVC into using cmov's vs. jmp's. */
+ s0 = s_tdefl_small_dist_sym[match_dist & 511];
+ n0 = s_tdefl_small_dist_extra[match_dist & 511];
+ s1 = s_tdefl_large_dist_sym[match_dist >> 8];
+ n1 = s_tdefl_large_dist_extra[match_dist >> 8];
+ sym = (match_dist < 512) ? s0 : s1;
+ num_extra_bits = (match_dist < 512) ? n0 : n1;
+
+ MZ_ASSERT(d->m_huff_code_sizes[1][sym]);
+ TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);
+ TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);
+ }
+ else
+ {
+ mz_uint lit = *pLZ_codes++;
+ MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
+ TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
+
+ if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))
+ {
+ flags >>= 1;
+ lit = *pLZ_codes++;
+ MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
+ TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
+
+ if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))
+ {
+ flags >>= 1;
+ lit = *pLZ_codes++;
+ MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
+ TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
+ }
+ }
+ }
+
+ if (pOutput_buf >= d->m_pOutput_buf_end)
+ return MZ_FALSE;
+
+ memcpy(pOutput_buf, &bit_buffer, sizeof(mz_uint64));
+ pOutput_buf += (bits_in >> 3);
+ bit_buffer >>= (bits_in & ~7);
+ bits_in &= 7;
+ }
+
+#undef TDEFL_PUT_BITS_FAST
+
+ d->m_pOutput_buf = pOutput_buf;
+ d->m_bits_in = 0;
+ d->m_bit_buffer = 0;
+
+ while (bits_in)
+ {
+ mz_uint32 n = MZ_MIN(bits_in, 16);
+ TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n);
+ bit_buffer >>= n;
+ bits_in -= n;
+ }
+
+ TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);
+
+ return (d->m_pOutput_buf < d->m_pOutput_buf_end);
+}
+#else
+static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
+{
+ mz_uint flags;
+ mz_uint8 *pLZ_codes;
+
+ flags = 1;
+ for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1)
+ {
+ if (flags == 1)
+ flags = *pLZ_codes++ | 0x100;
+ if (flags & 1)
+ {
+ mz_uint sym, num_extra_bits;
+ mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8));
+ pLZ_codes += 3;
+
+ MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
+ TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
+ TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);
+
+ if (match_dist < 512)
+ {
+ sym = s_tdefl_small_dist_sym[match_dist];
+ num_extra_bits = s_tdefl_small_dist_extra[match_dist];
+ }
+ else
+ {
+ sym = s_tdefl_large_dist_sym[match_dist >> 8];
+ num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8];
+ }
+ MZ_ASSERT(d->m_huff_code_sizes[1][sym]);
+ TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);
+ TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);
+ }
+ else
+ {
+ mz_uint lit = *pLZ_codes++;
+ MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
+ TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
+ }
+ }
+
+ TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);
+
+ return (d->m_pOutput_buf < d->m_pOutput_buf_end);
+}
+#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */
+
+static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block)
+{
+ if (static_block)
+ tdefl_start_static_block(d);
+ else
+ tdefl_start_dynamic_block(d);
+ return tdefl_compress_lz_codes(d);
+}
+
+static const mz_uint s_tdefl_num_probes[11];
+
+static int tdefl_flush_block(tdefl_compressor *d, int flush)
+{
+ mz_uint saved_bit_buf, saved_bits_in;
+ mz_uint8 *pSaved_output_buf;
+ mz_bool comp_block_succeeded = MZ_FALSE;
+ int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size;
+ mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf;
+
+ d->m_pOutput_buf = pOutput_buf_start;
+ d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16;
+
+ MZ_ASSERT(!d->m_output_flush_remaining);
+ d->m_output_flush_ofs = 0;
+ d->m_output_flush_remaining = 0;
+
+ *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left);
+ d->m_pLZ_code_buf -= (d->m_num_flags_left == 8);
+
+ if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index))
+ {
+ const mz_uint8 cmf = 0x78;
+ mz_uint8 flg, flevel = 3;
+ mz_uint header, i, mz_un = sizeof(s_tdefl_num_probes) / sizeof(mz_uint);
+
+ /* Determine compression level by reversing the process in tdefl_create_comp_flags_from_zip_params() */
+ for (i = 0; i < mz_un; i++)
+ if (s_tdefl_num_probes[i] == (d->m_flags & 0xFFF)) break;
+
+ if (i < 2)
+ flevel = 0;
+ else if (i < 6)
+ flevel = 1;
+ else if (i == 6)
+ flevel = 2;
+
+ header = cmf << 8 | (flevel << 6);
+ header += 31 - (header % 31);
+ flg = header & 0xFF;
+
+ TDEFL_PUT_BITS(cmf, 8);
+ TDEFL_PUT_BITS(flg, 8);
+ }
+
+ TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1);
+
+ pSaved_output_buf = d->m_pOutput_buf;
+ saved_bit_buf = d->m_bit_buffer;
+ saved_bits_in = d->m_bits_in;
+
+ if (!use_raw_block)
+ comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48));
+
+ /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */
+ if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) &&
+ ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size))
+ {
+ mz_uint i;
+ d->m_pOutput_buf = pSaved_output_buf;
+ d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;
+ TDEFL_PUT_BITS(0, 2);
+ if (d->m_bits_in)
+ {
+ TDEFL_PUT_BITS(0, 8 - d->m_bits_in);
+ }
+ for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF)
+ {
+ TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16);
+ }
+ for (i = 0; i < d->m_total_lz_bytes; ++i)
+ {
+ TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8);
+ }
+ }
+ /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */
+ else if (!comp_block_succeeded)
+ {
+ d->m_pOutput_buf = pSaved_output_buf;
+ d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;
+ tdefl_compress_block(d, MZ_TRUE);
+ }
+
+ if (flush)
+ {
+ if (flush == TDEFL_FINISH)
+ {
+ if (d->m_bits_in)
+ {
+ TDEFL_PUT_BITS(0, 8 - d->m_bits_in);
+ }
+ if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER)
+ {
+ mz_uint i, a = d->m_adler32;
+ for (i = 0; i < 4; i++)
+ {
+ TDEFL_PUT_BITS((a >> 24) & 0xFF, 8);
+ a <<= 8;
+ }
+ }
+ }
+ else
+ {
+ mz_uint i, z = 0;
+ TDEFL_PUT_BITS(0, 3);
+ if (d->m_bits_in)
+ {
+ TDEFL_PUT_BITS(0, 8 - d->m_bits_in);
+ }
+ for (i = 2; i; --i, z ^= 0xFFFF)
+ {
+ TDEFL_PUT_BITS(z & 0xFFFF, 16);
+ }
+ }
+ }
+
+ MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end);
+
+ memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);
+ memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);
+
+ d->m_pLZ_code_buf = d->m_lz_code_buf + 1;
+ d->m_pLZ_flags = d->m_lz_code_buf;
+ d->m_num_flags_left = 8;
+ d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes;
+ d->m_total_lz_bytes = 0;
+ d->m_block_index++;
+
+ if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0)
+ {
+ if (d->m_pPut_buf_func)
+ {
+ *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;
+ if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user))
+ return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED);
+ }
+ else if (pOutput_buf_start == d->m_output_buf)
+ {
+ int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs));
+ memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy);
+ d->m_out_buf_ofs += bytes_to_copy;
+ if ((n -= bytes_to_copy) != 0)
+ {
+ d->m_output_flush_ofs = bytes_to_copy;
+ d->m_output_flush_remaining = n;
+ }
+ }
+ else
+ {
+ d->m_out_buf_ofs += n;
+ }
+ }
+
+ return d->m_output_flush_remaining;
+}
+
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
+#ifdef MINIZ_UNALIGNED_USE_MEMCPY
+static mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p)
+{
+ mz_uint16 ret;
+ memcpy(&ret, p, sizeof(mz_uint16));
+ return ret;
+}
+static mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p)
+{
+ mz_uint16 ret;
+ memcpy(&ret, p, sizeof(mz_uint16));
+ return ret;
+}
+#else
+#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p)
+#define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p)
+#endif
+static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)
+{
+ mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;
+ mz_uint num_probes_left = d->m_max_probes[match_len >= 32];
+ const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q;
+ mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s);
+ MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN);
+ if (max_match_len <= match_len)
+ return;
+ for (;;)
+ {
+ for (;;)
+ {
+ if (--num_probes_left == 0)
+ return;
+#define TDEFL_PROBE \
+ next_probe_pos = d->m_next[probe_pos]; \
+ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \
+ return; \
+ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \
+ if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \
+ break;
+ TDEFL_PROBE;
+ TDEFL_PROBE;
+ TDEFL_PROBE;
+ }
+ if (!dist)
+ break;
+ q = (const mz_uint16 *)(d->m_dict + probe_pos);
+ if (TDEFL_READ_UNALIGNED_WORD2(q) != s01)
+ continue;
+ p = s;
+ probe_len = 32;
+ do
+ {
+ } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) &&
+ (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0));
+ if (!probe_len)
+ {
+ *pMatch_dist = dist;
+ *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN);
+ break;
+ }
+ else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len)
+ {
+ *pMatch_dist = dist;
+ if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len)
+ break;
+ c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]);
+ }
+ }
+}
+#else
+static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)
+{
+ mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;
+ mz_uint num_probes_left = d->m_max_probes[match_len >= 32];
+ const mz_uint8 *s = d->m_dict + pos, *p, *q;
+ mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1];
+ MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN);
+ if (max_match_len <= match_len)
+ return;
+ for (;;)
+ {
+ for (;;)
+ {
+ if (--num_probes_left == 0)
+ return;
+#define TDEFL_PROBE \
+ next_probe_pos = d->m_next[probe_pos]; \
+ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \
+ return; \
+ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \
+ if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \
+ break;
+ TDEFL_PROBE;
+ TDEFL_PROBE;
+ TDEFL_PROBE;
+ }
+ if (!dist)
+ break;
+ p = s;
+ q = d->m_dict + probe_pos;
+ for (probe_len = 0; probe_len < max_match_len; probe_len++)
+ if (*p++ != *q++)
+ break;
+ if (probe_len > match_len)
+ {
+ *pMatch_dist = dist;
+ if ((*pMatch_len = match_len = probe_len) == max_match_len)
+ return;
+ c0 = d->m_dict[pos + match_len];
+ c1 = d->m_dict[pos + match_len - 1];
+ }
+ }
+}
+#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */
+
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
+#ifdef MINIZ_UNALIGNED_USE_MEMCPY
+static mz_uint32 TDEFL_READ_UNALIGNED_WORD32(const mz_uint8* p)
+{
+ mz_uint32 ret;
+ memcpy(&ret, p, sizeof(mz_uint32));
+ return ret;
+}
+#else
+#define TDEFL_READ_UNALIGNED_WORD32(p) *(const mz_uint32 *)(p)
+#endif
+static mz_bool tdefl_compress_fast(tdefl_compressor *d)
+{
+ /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */
+ mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left;
+ mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags;
+ mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;
+
+ while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size)))
+ {
+ const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096;
+ mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;
+ mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size);
+ d->m_src_buf_left -= num_bytes_to_process;
+ lookahead_size += num_bytes_to_process;
+
+ while (num_bytes_to_process)
+ {
+ mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process);
+ memcpy(d->m_dict + dst_pos, d->m_pSrc, n);
+ if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
+ memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos));
+ d->m_pSrc += n;
+ dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK;
+ num_bytes_to_process -= n;
+ }
+
+ dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size);
+ if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE))
+ break;
+
+ while (lookahead_size >= 4)
+ {
+ mz_uint cur_match_dist, cur_match_len = 1;
+ mz_uint8 *pCur_dict = d->m_dict + cur_pos;
+ mz_uint first_trigram = TDEFL_READ_UNALIGNED_WORD32(pCur_dict) & 0xFFFFFF;
+ mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK;
+ mz_uint probe_pos = d->m_hash[hash];
+ d->m_hash[hash] = (mz_uint16)lookahead_pos;
+
+ if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((TDEFL_READ_UNALIGNED_WORD32(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram))
+ {
+ const mz_uint16 *p = (const mz_uint16 *)pCur_dict;
+ const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos);
+ mz_uint32 probe_len = 32;
+ do
+ {
+ } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) &&
+ (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0));
+ cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q);
+ if (!probe_len)
+ cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0;
+
+ if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)))
+ {
+ cur_match_len = 1;
+ *pLZ_code_buf++ = (mz_uint8)first_trigram;
+ *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
+ d->m_huff_count[0][(mz_uint8)first_trigram]++;
+ }
+ else
+ {
+ mz_uint32 s0, s1;
+ cur_match_len = MZ_MIN(cur_match_len, lookahead_size);
+
+ MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE));
+
+ cur_match_dist--;
+
+ pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN);
+#ifdef MINIZ_UNALIGNED_USE_MEMCPY
+ memcpy(&pLZ_code_buf[1], &cur_match_dist, sizeof(cur_match_dist));
+#else
+ *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist;
+#endif
+ pLZ_code_buf += 3;
+ *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80);
+
+ s0 = s_tdefl_small_dist_sym[cur_match_dist & 511];
+ s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8];
+ d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++;
+
+ d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++;
+ }
+ }
+ else
+ {
+ *pLZ_code_buf++ = (mz_uint8)first_trigram;
+ *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
+ d->m_huff_count[0][(mz_uint8)first_trigram]++;
+ }
+
+ if (--num_flags_left == 0)
+ {
+ num_flags_left = 8;
+ pLZ_flags = pLZ_code_buf++;
+ }
+
+ total_lz_bytes += cur_match_len;
+ lookahead_pos += cur_match_len;
+ dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE);
+ cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK;
+ MZ_ASSERT(lookahead_size >= cur_match_len);
+ lookahead_size -= cur_match_len;
+
+ if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])
+ {
+ int n;
+ d->m_lookahead_pos = lookahead_pos;
+ d->m_lookahead_size = lookahead_size;
+ d->m_dict_size = dict_size;
+ d->m_total_lz_bytes = total_lz_bytes;
+ d->m_pLZ_code_buf = pLZ_code_buf;
+ d->m_pLZ_flags = pLZ_flags;
+ d->m_num_flags_left = num_flags_left;
+ if ((n = tdefl_flush_block(d, 0)) != 0)
+ return (n < 0) ? MZ_FALSE : MZ_TRUE;
+ total_lz_bytes = d->m_total_lz_bytes;
+ pLZ_code_buf = d->m_pLZ_code_buf;
+ pLZ_flags = d->m_pLZ_flags;
+ num_flags_left = d->m_num_flags_left;
+ }
+ }
+
+ while (lookahead_size)
+ {
+ mz_uint8 lit = d->m_dict[cur_pos];
+
+ total_lz_bytes++;
+ *pLZ_code_buf++ = lit;
+ *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
+ if (--num_flags_left == 0)
+ {
+ num_flags_left = 8;
+ pLZ_flags = pLZ_code_buf++;
+ }
+
+ d->m_huff_count[0][lit]++;
+
+ lookahead_pos++;
+ dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE);
+ cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;
+ lookahead_size--;
+
+ if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])
+ {
+ int n;
+ d->m_lookahead_pos = lookahead_pos;
+ d->m_lookahead_size = lookahead_size;
+ d->m_dict_size = dict_size;
+ d->m_total_lz_bytes = total_lz_bytes;
+ d->m_pLZ_code_buf = pLZ_code_buf;
+ d->m_pLZ_flags = pLZ_flags;
+ d->m_num_flags_left = num_flags_left;
+ if ((n = tdefl_flush_block(d, 0)) != 0)
+ return (n < 0) ? MZ_FALSE : MZ_TRUE;
+ total_lz_bytes = d->m_total_lz_bytes;
+ pLZ_code_buf = d->m_pLZ_code_buf;
+ pLZ_flags = d->m_pLZ_flags;
+ num_flags_left = d->m_num_flags_left;
+ }
+ }
+ }
+
+ d->m_lookahead_pos = lookahead_pos;
+ d->m_lookahead_size = lookahead_size;
+ d->m_dict_size = dict_size;
+ d->m_total_lz_bytes = total_lz_bytes;
+ d->m_pLZ_code_buf = pLZ_code_buf;
+ d->m_pLZ_flags = pLZ_flags;
+ d->m_num_flags_left = num_flags_left;
+ return MZ_TRUE;
+}
+#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */
+
+static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit)
+{
+ d->m_total_lz_bytes++;
+ *d->m_pLZ_code_buf++ = lit;
+ *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1);
+ if (--d->m_num_flags_left == 0)
+ {
+ d->m_num_flags_left = 8;
+ d->m_pLZ_flags = d->m_pLZ_code_buf++;
+ }
+ d->m_huff_count[0][lit]++;
+}
+
+static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist)
+{
+ mz_uint32 s0, s1;
+
+ MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE));
+
+ d->m_total_lz_bytes += match_len;
+
+ d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN);
+
+ match_dist -= 1;
+ d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF);
+ d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8);
+ d->m_pLZ_code_buf += 3;
+
+ *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80);
+ if (--d->m_num_flags_left == 0)
+ {
+ d->m_num_flags_left = 8;
+ d->m_pLZ_flags = d->m_pLZ_code_buf++;
+ }
+
+ s0 = s_tdefl_small_dist_sym[match_dist & 511];
+ s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127];
+ d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++;
+ d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++;
+}
+
+static mz_bool tdefl_compress_normal(tdefl_compressor *d)
+{
+ const mz_uint8 *pSrc = d->m_pSrc;
+ size_t src_buf_left = d->m_src_buf_left;
+ tdefl_flush flush = d->m_flush;
+
+ while ((src_buf_left) || ((flush) && (d->m_lookahead_size)))
+ {
+ mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos;
+ /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */
+ if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1))
+ {
+ mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2;
+ mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK];
+ mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size);
+ const mz_uint8 *pSrc_end = pSrc ? pSrc + num_bytes_to_process : NULL;
+ src_buf_left -= num_bytes_to_process;
+ d->m_lookahead_size += num_bytes_to_process;
+ while (pSrc != pSrc_end)
+ {
+ mz_uint8 c = *pSrc++;
+ d->m_dict[dst_pos] = c;
+ if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
+ d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;
+ hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);
+ d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash];
+ d->m_hash[hash] = (mz_uint16)(ins_pos);
+ dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;
+ ins_pos++;
+ }
+ }
+ else
+ {
+ while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))
+ {
+ mz_uint8 c = *pSrc++;
+ mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;
+ src_buf_left--;
+ d->m_dict[dst_pos] = c;
+ if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
+ d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;
+ if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN)
+ {
+ mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2;
+ mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);
+ d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash];
+ d->m_hash[hash] = (mz_uint16)(ins_pos);
+ }
+ }
+ }
+ d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size);
+ if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))
+ break;
+
+ /* Simple lazy/greedy parsing state machine. */
+ len_to_move = 1;
+ cur_match_dist = 0;
+ cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1);
+ cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;
+ if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS))
+ {
+ if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))
+ {
+ mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK];
+ cur_match_len = 0;
+ while (cur_match_len < d->m_lookahead_size)
+ {
+ if (d->m_dict[cur_pos + cur_match_len] != c)
+ break;
+ cur_match_len++;
+ }
+ if (cur_match_len < TDEFL_MIN_MATCH_LEN)
+ cur_match_len = 0;
+ else
+ cur_match_dist = 1;
+ }
+ }
+ else
+ {
+ tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len);
+ }
+ if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5)))
+ {
+ cur_match_dist = cur_match_len = 0;
+ }
+ if (d->m_saved_match_len)
+ {
+ if (cur_match_len > d->m_saved_match_len)
+ {
+ tdefl_record_literal(d, (mz_uint8)d->m_saved_lit);
+ if (cur_match_len >= 128)
+ {
+ tdefl_record_match(d, cur_match_len, cur_match_dist);
+ d->m_saved_match_len = 0;
+ len_to_move = cur_match_len;
+ }
+ else
+ {
+ d->m_saved_lit = d->m_dict[cur_pos];
+ d->m_saved_match_dist = cur_match_dist;
+ d->m_saved_match_len = cur_match_len;
+ }
+ }
+ else
+ {
+ tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist);
+ len_to_move = d->m_saved_match_len - 1;
+ d->m_saved_match_len = 0;
+ }
+ }
+ else if (!cur_match_dist)
+ tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]);
+ else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128))
+ {
+ tdefl_record_match(d, cur_match_len, cur_match_dist);
+ len_to_move = cur_match_len;
+ }
+ else
+ {
+ d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)];
+ d->m_saved_match_dist = cur_match_dist;
+ d->m_saved_match_len = cur_match_len;
+ }
+ /* Move the lookahead forward by len_to_move bytes. */
+ d->m_lookahead_pos += len_to_move;
+ MZ_ASSERT(d->m_lookahead_size >= len_to_move);
+ d->m_lookahead_size -= len_to_move;
+ d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE);
+ /* Check if it's time to flush the current LZ codes to the internal output buffer. */
+ if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) ||
+ ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))))
+ {
+ int n;
+ d->m_pSrc = pSrc;
+ d->m_src_buf_left = src_buf_left;
+ if ((n = tdefl_flush_block(d, 0)) != 0)
+ return (n < 0) ? MZ_FALSE : MZ_TRUE;
+ }
+ }
+
+ d->m_pSrc = pSrc;
+ d->m_src_buf_left = src_buf_left;
+ return MZ_TRUE;
+}
+
+static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d)
+{
+ if (d->m_pIn_buf_size)
+ {
+ *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;
+ }
+
+ if (d->m_pOut_buf_size)
+ {
+ size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining);
+ memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n);
+ d->m_output_flush_ofs += (mz_uint)n;
+ d->m_output_flush_remaining -= (mz_uint)n;
+ d->m_out_buf_ofs += n;
+
+ *d->m_pOut_buf_size = d->m_out_buf_ofs;
+ }
+
+ return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY;
+}
+
+tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush)
+{
+ if (!d)
+ {
+ if (pIn_buf_size)
+ *pIn_buf_size = 0;
+ if (pOut_buf_size)
+ *pOut_buf_size = 0;
+ return TDEFL_STATUS_BAD_PARAM;
+ }
+
+ d->m_pIn_buf = pIn_buf;
+ d->m_pIn_buf_size = pIn_buf_size;
+ d->m_pOut_buf = pOut_buf;
+ d->m_pOut_buf_size = pOut_buf_size;
+ d->m_pSrc = (const mz_uint8 *)(pIn_buf);
+ d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0;
+ d->m_out_buf_ofs = 0;
+ d->m_flush = flush;
+
+ if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) ||
+ (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf))
+ {
+ if (pIn_buf_size)
+ *pIn_buf_size = 0;
+ if (pOut_buf_size)
+ *pOut_buf_size = 0;
+ return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM);
+ }
+ d->m_wants_to_finish |= (flush == TDEFL_FINISH);
+
+ if ((d->m_output_flush_remaining) || (d->m_finished))
+ return (d->m_prev_return_status = tdefl_flush_output_buffer(d));
+
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
+ if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) &&
+ ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) &&
+ ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0))
+ {
+ if (!tdefl_compress_fast(d))
+ return d->m_prev_return_status;
+ }
+ else
+#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */
+ {
+ if (!tdefl_compress_normal(d))
+ return d->m_prev_return_status;
+ }
+
+ if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf))
+ d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf);
+
+ if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining))
+ {
+ if (tdefl_flush_block(d, flush) < 0)
+ return d->m_prev_return_status;
+ d->m_finished = (flush == TDEFL_FINISH);
+ if (flush == TDEFL_FULL_FLUSH)
+ {
+ MZ_CLEAR_ARR(d->m_hash);
+ MZ_CLEAR_ARR(d->m_next);
+ d->m_dict_size = 0;
+ }
+ }
+
+ return (d->m_prev_return_status = tdefl_flush_output_buffer(d));
+}
+
+tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush)
+{
+ MZ_ASSERT(d->m_pPut_buf_func);
+ return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush);
+}
+
+tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
+{
+ d->m_pPut_buf_func = pPut_buf_func;
+ d->m_pPut_buf_user = pPut_buf_user;
+ d->m_flags = (mz_uint)(flags);
+ d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3;
+ d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0;
+ d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3;
+ if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG))
+ MZ_CLEAR_ARR(d->m_hash);
+ d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0;
+ d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0;
+ d->m_pLZ_code_buf = d->m_lz_code_buf + 1;
+ d->m_pLZ_flags = d->m_lz_code_buf;
+ *d->m_pLZ_flags = 0;
+ d->m_num_flags_left = 8;
+ d->m_pOutput_buf = d->m_output_buf;
+ d->m_pOutput_buf_end = d->m_output_buf;
+ d->m_prev_return_status = TDEFL_STATUS_OKAY;
+ d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0;
+ d->m_adler32 = 1;
+ d->m_pIn_buf = NULL;
+ d->m_pOut_buf = NULL;
+ d->m_pIn_buf_size = NULL;
+ d->m_pOut_buf_size = NULL;
+ d->m_flush = TDEFL_NO_FLUSH;
+ d->m_pSrc = NULL;
+ d->m_src_buf_left = 0;
+ d->m_out_buf_ofs = 0;
+ if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG))
+ MZ_CLEAR_ARR(d->m_dict);
+ memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);
+ memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);
+ return TDEFL_STATUS_OKAY;
+}
+
+tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d)
+{
+ return d->m_prev_return_status;
+}
+
+mz_uint32 tdefl_get_adler32(tdefl_compressor *d)
+{
+ return d->m_adler32;
+}
+
+mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
+{
+ tdefl_compressor *pComp;
+ mz_bool succeeded;
+ if (((buf_len) && (!pBuf)) || (!pPut_buf_func))
+ return MZ_FALSE;
+ pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor));
+ if (!pComp)
+ return MZ_FALSE;
+ succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY);
+ succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE);
+ MZ_FREE(pComp);
+ return succeeded;
+}
+
+typedef struct
+{
+ size_t m_size, m_capacity;
+ mz_uint8 *m_pBuf;
+ mz_bool m_expandable;
+} tdefl_output_buffer;
+
+static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser)
+{
+ tdefl_output_buffer *p = (tdefl_output_buffer *)pUser;
+ size_t new_size = p->m_size + len;
+ if (new_size > p->m_capacity)
+ {
+ size_t new_capacity = p->m_capacity;
+ mz_uint8 *pNew_buf;
+ if (!p->m_expandable)
+ return MZ_FALSE;
+ do
+ {
+ new_capacity = MZ_MAX(128U, new_capacity << 1U);
+ } while (new_size > new_capacity);
+ pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity);
+ if (!pNew_buf)
+ return MZ_FALSE;
+ p->m_pBuf = pNew_buf;
+ p->m_capacity = new_capacity;
+ }
+ memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len);
+ p->m_size = new_size;
+ return MZ_TRUE;
+}
+
+void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
+{
+ tdefl_output_buffer out_buf;
+ MZ_CLEAR_OBJ(out_buf);
+ if (!pOut_len)
+ return MZ_FALSE;
+ else
+ *pOut_len = 0;
+ out_buf.m_expandable = MZ_TRUE;
+ if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags))
+ return NULL;
+ *pOut_len = out_buf.m_size;
+ return out_buf.m_pBuf;
+}
+
+size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
+{
+ tdefl_output_buffer out_buf;
+ MZ_CLEAR_OBJ(out_buf);
+ if (!pOut_buf)
+ return 0;
+ out_buf.m_pBuf = (mz_uint8 *)pOut_buf;
+ out_buf.m_capacity = out_buf_len;
+ if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags))
+ return 0;
+ return out_buf.m_size;
+}
+
+static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };
+
+/* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */
+mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy)
+{
+ mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
+ if (window_bits > 0)
+ comp_flags |= TDEFL_WRITE_ZLIB_HEADER;
+
+ if (!level)
+ comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
+ else if (strategy == MZ_FILTERED)
+ comp_flags |= TDEFL_FILTER_MATCHES;
+ else if (strategy == MZ_HUFFMAN_ONLY)
+ comp_flags &= ~TDEFL_MAX_PROBES_MASK;
+ else if (strategy == MZ_FIXED)
+ comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS;
+ else if (strategy == MZ_RLE)
+ comp_flags |= TDEFL_RLE_MATCHES;
+
+ return comp_flags;
+}
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */
+#endif
+
+/* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at
+ http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/.
+ This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */
+void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip)
+{
+ /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */
+ static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };
+ tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor));
+ tdefl_output_buffer out_buf;
+ int i, bpl = w * num_chans, y, z;
+ mz_uint32 c;
+ *pLen_out = 0;
+ if (!pComp)
+ return NULL;
+ MZ_CLEAR_OBJ(out_buf);
+ out_buf.m_expandable = MZ_TRUE;
+ out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h);
+ if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity)))
+ {
+ MZ_FREE(pComp);
+ return NULL;
+ }
+ /* write dummy header */
+ for (z = 41; z; --z)
+ tdefl_output_buffer_putter(&z, 1, &out_buf);
+ /* compress image data */
+ tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER);
+ for (y = 0; y < h; ++y)
+ {
+ tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH);
+ tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH);
+ }
+ if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE)
+ {
+ MZ_FREE(pComp);
+ MZ_FREE(out_buf.m_pBuf);
+ return NULL;
+ }
+ /* write real header */
+ *pLen_out = out_buf.m_size - 41;
+ {
+ static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 };
+ mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d,
+ 0x0a, 0x1a, 0x0a, 0x00, 0x00,
+ 0x00, 0x0d, 0x49, 0x48, 0x44,
+ 0x52, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x49, 0x44, 0x41,
+ 0x54 };
+ pnghdr[18] = (mz_uint8)(w >> 8);
+ pnghdr[19] = (mz_uint8)w;
+ pnghdr[22] = (mz_uint8)(h >> 8);
+ pnghdr[23] = (mz_uint8)h;
+ pnghdr[25] = chans[num_chans];
+ pnghdr[33] = (mz_uint8)(*pLen_out >> 24);
+ pnghdr[34] = (mz_uint8)(*pLen_out >> 16);
+ pnghdr[35] = (mz_uint8)(*pLen_out >> 8);
+ pnghdr[36] = (mz_uint8)*pLen_out;
+ c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17);
+ for (i = 0; i < 4; ++i, c <<= 8)
+ ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24);
+ memcpy(out_buf.m_pBuf, pnghdr, 41);
+ }
+ /* write footer (IDAT CRC-32, followed by IEND chunk) */
+ if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf))
+ {
+ *pLen_out = 0;
+ MZ_FREE(pComp);
+ MZ_FREE(out_buf.m_pBuf);
+ return NULL;
+ }
+ c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4);
+ for (i = 0; i < 4; ++i, c <<= 8)
+ (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24);
+ /* compute final size of file, grab compressed data buffer and return */
+ *pLen_out += 57;
+ MZ_FREE(pComp);
+ return out_buf.m_pBuf;
+}
+void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out)
+{
+ /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */
+ return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE);
+}
+
+#ifndef MINIZ_NO_MALLOC
+/* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */
+/* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */
+/* structure size and allocation mechanism. */
+tdefl_compressor *tdefl_compressor_alloc(void)
+{
+ return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor));
+}
+
+void tdefl_compressor_free(tdefl_compressor *pComp)
+{
+ MZ_FREE(pComp);
+}
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/
+ /**************************************************************************
+ *
+ * Copyright 2013-2014 RAD Game Tools and Valve Software
+ * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+
+#ifndef MINIZ_NO_INFLATE_APIS
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ------------------- Low-level Decompression (completely independent from all compression API's) */
+
+#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l)
+#define TINFL_MEMSET(p, c, l) memset(p, c, l)
+
+#define TINFL_CR_BEGIN \
+ switch (r->m_state) \
+ { \
+ case 0:
+#define TINFL_CR_RETURN(state_index, result) \
+ do \
+ { \
+ status = result; \
+ r->m_state = state_index; \
+ goto common_exit; \
+ case state_index:; \
+ } \
+ MZ_MACRO_END
+#define TINFL_CR_RETURN_FOREVER(state_index, result) \
+ do \
+ { \
+ for (;;) \
+ { \
+ TINFL_CR_RETURN(state_index, result); \
+ } \
+ } \
+ MZ_MACRO_END
+#define TINFL_CR_FINISH }
+
+#define TINFL_GET_BYTE(state_index, c) \
+ do \
+ { \
+ while (pIn_buf_cur >= pIn_buf_end) \
+ { \
+ TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \
+ } \
+ c = *pIn_buf_cur++; \
+ } \
+ MZ_MACRO_END
+
+#define TINFL_NEED_BITS(state_index, n) \
+ do \
+ { \
+ mz_uint c; \
+ TINFL_GET_BYTE(state_index, c); \
+ bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \
+ num_bits += 8; \
+ } while (num_bits < (mz_uint)(n))
+#define TINFL_SKIP_BITS(state_index, n) \
+ do \
+ { \
+ if (num_bits < (mz_uint)(n)) \
+ { \
+ TINFL_NEED_BITS(state_index, n); \
+ } \
+ bit_buf >>= (n); \
+ num_bits -= (n); \
+ } \
+ MZ_MACRO_END
+#define TINFL_GET_BITS(state_index, b, n) \
+ do \
+ { \
+ if (num_bits < (mz_uint)(n)) \
+ { \
+ TINFL_NEED_BITS(state_index, n); \
+ } \
+ b = bit_buf & ((1 << (n)) - 1); \
+ bit_buf >>= (n); \
+ num_bits -= (n); \
+ } \
+ MZ_MACRO_END
+
+/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */
+/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */
+/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */
+/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */
+#define TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree) \
+ do \
+ { \
+ temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \
+ if (temp >= 0) \
+ { \
+ code_len = temp >> 9; \
+ if ((code_len) && (num_bits >= code_len)) \
+ break; \
+ } \
+ else if (num_bits > TINFL_FAST_LOOKUP_BITS) \
+ { \
+ code_len = TINFL_FAST_LOOKUP_BITS; \
+ do \
+ { \
+ temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \
+ } while ((temp < 0) && (num_bits >= (code_len + 1))); \
+ if (temp >= 0) \
+ break; \
+ } \
+ TINFL_GET_BYTE(state_index, c); \
+ bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \
+ num_bits += 8; \
+ } while (num_bits < 15);
+
+/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */
+/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */
+/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */
+/* The slow path is only executed at the very end of the input buffer. */
+/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */
+/* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */
+#define TINFL_HUFF_DECODE(state_index, sym, pLookUp, pTree) \
+ do \
+ { \
+ int temp; \
+ mz_uint code_len, c; \
+ if (num_bits < 15) \
+ { \
+ if ((pIn_buf_end - pIn_buf_cur) < 2) \
+ { \
+ TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree); \
+ } \
+ else \
+ { \
+ bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \
+ pIn_buf_cur += 2; \
+ num_bits += 16; \
+ } \
+ } \
+ if ((temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \
+ code_len = temp >> 9, temp &= 511; \
+ else \
+ { \
+ code_len = TINFL_FAST_LOOKUP_BITS; \
+ do \
+ { \
+ temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \
+ } while (temp < 0); \
+ } \
+ sym = temp; \
+ bit_buf >>= code_len; \
+ num_bits -= code_len; \
+ } \
+ MZ_MACRO_END
+
+static void tinfl_clear_tree(tinfl_decompressor *r)
+{
+ if (r->m_type == 0)
+ MZ_CLEAR_ARR(r->m_tree_0);
+ else if (r->m_type == 1)
+ MZ_CLEAR_ARR(r->m_tree_1);
+ else
+ MZ_CLEAR_ARR(r->m_tree_2);
+}
+
+tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags)
+{
+ static const mz_uint16 s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 };
+ static const mz_uint8 s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 };
+ static const mz_uint16 s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 };
+ static const mz_uint8 s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 };
+ static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+ static const mz_uint16 s_min_table_sizes[3] = { 257, 1, 4 };
+
+ mz_int16 *pTrees[3];
+ mz_uint8 *pCode_sizes[3];
+
+ tinfl_status status = TINFL_STATUS_FAILED;
+ mz_uint32 num_bits, dist, counter, num_extra;
+ tinfl_bit_buf_t bit_buf;
+ const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size;
+ mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next ? pOut_buf_next + *pOut_buf_size : NULL;
+ size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start;
+
+ /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */
+ if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start))
+ {
+ *pIn_buf_size = *pOut_buf_size = 0;
+ return TINFL_STATUS_BAD_PARAM;
+ }
+
+ pTrees[0] = r->m_tree_0;
+ pTrees[1] = r->m_tree_1;
+ pTrees[2] = r->m_tree_2;
+ pCode_sizes[0] = r->m_code_size_0;
+ pCode_sizes[1] = r->m_code_size_1;
+ pCode_sizes[2] = r->m_code_size_2;
+
+ num_bits = r->m_num_bits;
+ bit_buf = r->m_bit_buf;
+ dist = r->m_dist;
+ counter = r->m_counter;
+ num_extra = r->m_num_extra;
+ dist_from_out_buf_start = r->m_dist_from_out_buf_start;
+ TINFL_CR_BEGIN
+
+ bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0;
+ r->m_z_adler32 = r->m_check_adler32 = 1;
+ if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
+ {
+ TINFL_GET_BYTE(1, r->m_zhdr0);
+ TINFL_GET_BYTE(2, r->m_zhdr1);
+ counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8));
+ if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
+ counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)((size_t)1 << (8U + (r->m_zhdr0 >> 4)))));
+ if (counter)
+ {
+ TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED);
+ }
+ }
+
+ do
+ {
+ TINFL_GET_BITS(3, r->m_final, 3);
+ r->m_type = r->m_final >> 1;
+ if (r->m_type == 0)
+ {
+ TINFL_SKIP_BITS(5, num_bits & 7);
+ for (counter = 0; counter < 4; ++counter)
+ {
+ if (num_bits)
+ TINFL_GET_BITS(6, r->m_raw_header[counter], 8);
+ else
+ TINFL_GET_BYTE(7, r->m_raw_header[counter]);
+ }
+ if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8))))
+ {
+ TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED);
+ }
+ while ((counter) && (num_bits))
+ {
+ TINFL_GET_BITS(51, dist, 8);
+ while (pOut_buf_cur >= pOut_buf_end)
+ {
+ TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT);
+ }
+ *pOut_buf_cur++ = (mz_uint8)dist;
+ counter--;
+ }
+ while (counter)
+ {
+ size_t n;
+ while (pOut_buf_cur >= pOut_buf_end)
+ {
+ TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT);
+ }
+ while (pIn_buf_cur >= pIn_buf_end)
+ {
+ TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS);
+ }
+ n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter);
+ TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n);
+ pIn_buf_cur += n;
+ pOut_buf_cur += n;
+ counter -= (mz_uint)n;
+ }
+ }
+ else if (r->m_type == 3)
+ {
+ TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED);
+ }
+ else
+ {
+ if (r->m_type == 1)
+ {
+ mz_uint8 *p = r->m_code_size_0;
+ mz_uint i;
+ r->m_table_sizes[0] = 288;
+ r->m_table_sizes[1] = 32;
+ TINFL_MEMSET(r->m_code_size_1, 5, 32);
+ for (i = 0; i <= 143; ++i)
+ *p++ = 8;
+ for (; i <= 255; ++i)
+ *p++ = 9;
+ for (; i <= 279; ++i)
+ *p++ = 7;
+ for (; i <= 287; ++i)
+ *p++ = 8;
+ }
+ else
+ {
+ for (counter = 0; counter < 3; counter++)
+ {
+ TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]);
+ r->m_table_sizes[counter] += s_min_table_sizes[counter];
+ }
+ MZ_CLEAR_ARR(r->m_code_size_2);
+ for (counter = 0; counter < r->m_table_sizes[2]; counter++)
+ {
+ mz_uint s;
+ TINFL_GET_BITS(14, s, 3);
+ r->m_code_size_2[s_length_dezigzag[counter]] = (mz_uint8)s;
+ }
+ r->m_table_sizes[2] = 19;
+ }
+ for (; (int)r->m_type >= 0; r->m_type--)
+ {
+ int tree_next, tree_cur;
+ mz_int16 *pLookUp;
+ mz_int16 *pTree;
+ mz_uint8 *pCode_size;
+ mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16];
+ pLookUp = r->m_look_up[r->m_type];
+ pTree = pTrees[r->m_type];
+ pCode_size = pCode_sizes[r->m_type];
+ MZ_CLEAR_ARR(total_syms);
+ TINFL_MEMSET(pLookUp, 0, sizeof(r->m_look_up[0]));
+ tinfl_clear_tree(r);
+ for (i = 0; i < r->m_table_sizes[r->m_type]; ++i)
+ total_syms[pCode_size[i]]++;
+ used_syms = 0, total = 0;
+ next_code[0] = next_code[1] = 0;
+ for (i = 1; i <= 15; ++i)
+ {
+ used_syms += total_syms[i];
+ next_code[i + 1] = (total = ((total + total_syms[i]) << 1));
+ }
+ if ((65536 != total) && (used_syms > 1))
+ {
+ TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED);
+ }
+ for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index)
+ {
+ mz_uint rev_code = 0, l, cur_code, code_size = pCode_size[sym_index];
+ if (!code_size)
+ continue;
+ cur_code = next_code[code_size]++;
+ for (l = code_size; l > 0; l--, cur_code >>= 1)
+ rev_code = (rev_code << 1) | (cur_code & 1);
+ if (code_size <= TINFL_FAST_LOOKUP_BITS)
+ {
+ mz_int16 k = (mz_int16)((code_size << 9) | sym_index);
+ while (rev_code < TINFL_FAST_LOOKUP_SIZE)
+ {
+ pLookUp[rev_code] = k;
+ rev_code += (1 << code_size);
+ }
+ continue;
+ }
+ if (0 == (tree_cur = pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)]))
+ {
+ pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next;
+ tree_cur = tree_next;
+ tree_next -= 2;
+ }
+ rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1);
+ for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--)
+ {
+ tree_cur -= ((rev_code >>= 1) & 1);
+ if (!pTree[-tree_cur - 1])
+ {
+ pTree[-tree_cur - 1] = (mz_int16)tree_next;
+ tree_cur = tree_next;
+ tree_next -= 2;
+ }
+ else
+ tree_cur = pTree[-tree_cur - 1];
+ }
+ tree_cur -= ((rev_code >>= 1) & 1);
+ pTree[-tree_cur - 1] = (mz_int16)sym_index;
+ }
+ if (r->m_type == 2)
+ {
+ for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);)
+ {
+ mz_uint s;
+ TINFL_HUFF_DECODE(16, dist, r->m_look_up[2], r->m_tree_2);
+ if (dist < 16)
+ {
+ r->m_len_codes[counter++] = (mz_uint8)dist;
+ continue;
+ }
+ if ((dist == 16) && (!counter))
+ {
+ TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED);
+ }
+ num_extra = "\02\03\07"[dist - 16];
+ TINFL_GET_BITS(18, s, num_extra);
+ s += "\03\03\013"[dist - 16];
+ TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s);
+ counter += s;
+ }
+ if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter)
+ {
+ TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED);
+ }
+ TINFL_MEMCPY(r->m_code_size_0, r->m_len_codes, r->m_table_sizes[0]);
+ TINFL_MEMCPY(r->m_code_size_1, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]);
+ }
+ }
+ for (;;)
+ {
+ mz_uint8 *pSrc;
+ for (;;)
+ {
+ if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2))
+ {
+ TINFL_HUFF_DECODE(23, counter, r->m_look_up[0], r->m_tree_0);
+ if (counter >= 256)
+ break;
+ while (pOut_buf_cur >= pOut_buf_end)
+ {
+ TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT);
+ }
+ *pOut_buf_cur++ = (mz_uint8)counter;
+ }
+ else
+ {
+ int sym2;
+ mz_uint code_len;
+#if TINFL_USE_64BIT_BITBUF
+ if (num_bits < 30)
+ {
+ bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits);
+ pIn_buf_cur += 4;
+ num_bits += 32;
+ }
+#else
+ if (num_bits < 15)
+ {
+ bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits);
+ pIn_buf_cur += 2;
+ num_bits += 16;
+ }
+#endif
+ if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
+ code_len = sym2 >> 9;
+ else
+ {
+ code_len = TINFL_FAST_LOOKUP_BITS;
+ do
+ {
+ sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)];
+ } while (sym2 < 0);
+ }
+ counter = sym2;
+ bit_buf >>= code_len;
+ num_bits -= code_len;
+ if (counter & 256)
+ break;
+
+#if !TINFL_USE_64BIT_BITBUF
+ if (num_bits < 15)
+ {
+ bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits);
+ pIn_buf_cur += 2;
+ num_bits += 16;
+ }
+#endif
+ if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
+ code_len = sym2 >> 9;
+ else
+ {
+ code_len = TINFL_FAST_LOOKUP_BITS;
+ do
+ {
+ sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)];
+ } while (sym2 < 0);
+ }
+ bit_buf >>= code_len;
+ num_bits -= code_len;
+
+ pOut_buf_cur[0] = (mz_uint8)counter;
+ if (sym2 & 256)
+ {
+ pOut_buf_cur++;
+ counter = sym2;
+ break;
+ }
+ pOut_buf_cur[1] = (mz_uint8)sym2;
+ pOut_buf_cur += 2;
+ }
+ }
+ if ((counter &= 511) == 256)
+ break;
+
+ num_extra = s_length_extra[counter - 257];
+ counter = s_length_base[counter - 257];
+ if (num_extra)
+ {
+ mz_uint extra_bits;
+ TINFL_GET_BITS(25, extra_bits, num_extra);
+ counter += extra_bits;
+ }
+
+ TINFL_HUFF_DECODE(26, dist, r->m_look_up[1], r->m_tree_1);
+ num_extra = s_dist_extra[dist];
+ dist = s_dist_base[dist];
+ if (num_extra)
+ {
+ mz_uint extra_bits;
+ TINFL_GET_BITS(27, extra_bits, num_extra);
+ dist += extra_bits;
+ }
+
+ dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start;
+ if ((dist == 0 || dist > dist_from_out_buf_start || dist_from_out_buf_start == 0) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
+ {
+ TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED);
+ }
+
+ pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask);
+
+ if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end)
+ {
+ while (counter--)
+ {
+ while (pOut_buf_cur >= pOut_buf_end)
+ {
+ TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT);
+ }
+ *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask];
+ }
+ continue;
+ }
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
+ else if ((counter >= 9) && (counter <= dist))
+ {
+ const mz_uint8 *pSrc_end = pSrc + (counter & ~7);
+ do
+ {
+#ifdef MINIZ_UNALIGNED_USE_MEMCPY
+ memcpy(pOut_buf_cur, pSrc, sizeof(mz_uint32)*2);
+#else
+ ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0];
+ ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1];
+#endif
+ pOut_buf_cur += 8;
+ } while ((pSrc += 8) < pSrc_end);
+ if ((counter &= 7) < 3)
+ {
+ if (counter)
+ {
+ pOut_buf_cur[0] = pSrc[0];
+ if (counter > 1)
+ pOut_buf_cur[1] = pSrc[1];
+ pOut_buf_cur += counter;
+ }
+ continue;
+ }
+ }
+#endif
+ while(counter>2)
+ {
+ pOut_buf_cur[0] = pSrc[0];
+ pOut_buf_cur[1] = pSrc[1];
+ pOut_buf_cur[2] = pSrc[2];
+ pOut_buf_cur += 3;
+ pSrc += 3;
+ counter -= 3;
+ }
+ if (counter > 0)
+ {
+ pOut_buf_cur[0] = pSrc[0];
+ if (counter > 1)
+ pOut_buf_cur[1] = pSrc[1];
+ pOut_buf_cur += counter;
+ }
+ }
+ }
+ } while (!(r->m_final & 1));
+
+ /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */
+ /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */
+ TINFL_SKIP_BITS(32, num_bits & 7);
+ while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8))
+ {
+ --pIn_buf_cur;
+ num_bits -= 8;
+ }
+ bit_buf &= ~(~(tinfl_bit_buf_t)0 << num_bits);
+ MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */
+
+ if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
+ {
+ for (counter = 0; counter < 4; ++counter)
+ {
+ mz_uint s;
+ if (num_bits)
+ TINFL_GET_BITS(41, s, 8);
+ else
+ TINFL_GET_BYTE(42, s);
+ r->m_z_adler32 = (r->m_z_adler32 << 8) | s;
+ }
+ }
+ TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE);
+
+ TINFL_CR_FINISH
+
+common_exit:
+ /* As long as we aren't telling the caller that we NEED more input to make forward progress: */
+ /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */
+ /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */
+ if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS))
+ {
+ while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8))
+ {
+ --pIn_buf_cur;
+ num_bits -= 8;
+ }
+ }
+ r->m_num_bits = num_bits;
+ r->m_bit_buf = bit_buf & ~(~(tinfl_bit_buf_t)0 << num_bits);
+ r->m_dist = dist;
+ r->m_counter = counter;
+ r->m_num_extra = num_extra;
+ r->m_dist_from_out_buf_start = dist_from_out_buf_start;
+ *pIn_buf_size = pIn_buf_cur - pIn_buf_next;
+ *pOut_buf_size = pOut_buf_cur - pOut_buf_next;
+ if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
+ {
+ const mz_uint8 *ptr = pOut_buf_next;
+ size_t buf_len = *pOut_buf_size;
+ mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16;
+ size_t block_len = buf_len % 5552;
+ while (buf_len)
+ {
+ for (i = 0; i + 7 < block_len; i += 8, ptr += 8)
+ {
+ s1 += ptr[0], s2 += s1;
+ s1 += ptr[1], s2 += s1;
+ s1 += ptr[2], s2 += s1;
+ s1 += ptr[3], s2 += s1;
+ s1 += ptr[4], s2 += s1;
+ s1 += ptr[5], s2 += s1;
+ s1 += ptr[6], s2 += s1;
+ s1 += ptr[7], s2 += s1;
+ }
+ for (; i < block_len; ++i)
+ s1 += *ptr++, s2 += s1;
+ s1 %= 65521U, s2 %= 65521U;
+ buf_len -= block_len;
+ block_len = 5552;
+ }
+ r->m_check_adler32 = (s2 << 16) + s1;
+ if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32))
+ status = TINFL_STATUS_ADLER32_MISMATCH;
+ }
+ return status;
+}
+
+/* Higher level helper functions. */
+void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
+{
+ tinfl_decompressor decomp;
+ void *pBuf = NULL, *pNew_buf;
+ size_t src_buf_ofs = 0, out_buf_capacity = 0;
+ *pOut_len = 0;
+ tinfl_init(&decomp);
+ for (;;)
+ {
+ size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity;
+ tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size,
+ (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
+ if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT))
+ {
+ MZ_FREE(pBuf);
+ *pOut_len = 0;
+ return NULL;
+ }
+ src_buf_ofs += src_buf_size;
+ *pOut_len += dst_buf_size;
+ if (status == TINFL_STATUS_DONE)
+ break;
+ new_out_buf_capacity = out_buf_capacity * 2;
+ if (new_out_buf_capacity < 128)
+ new_out_buf_capacity = 128;
+ pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity);
+ if (!pNew_buf)
+ {
+ MZ_FREE(pBuf);
+ *pOut_len = 0;
+ return NULL;
+ }
+ pBuf = pNew_buf;
+ out_buf_capacity = new_out_buf_capacity;
+ }
+ return pBuf;
+}
+
+size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
+{
+ tinfl_decompressor decomp;
+ tinfl_status status;
+ tinfl_init(&decomp);
+ status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
+ return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len;
+}
+
+int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
+{
+ int result = 0;
+ tinfl_decompressor decomp;
+ mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE);
+ size_t in_buf_ofs = 0, dict_ofs = 0;
+ if (!pDict)
+ return TINFL_STATUS_FAILED;
+ memset(pDict,0,TINFL_LZ_DICT_SIZE);
+ tinfl_init(&decomp);
+ for (;;)
+ {
+ size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs;
+ tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size,
+ (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)));
+ in_buf_ofs += in_buf_size;
+ if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user)))
+ break;
+ if (status != TINFL_STATUS_HAS_MORE_OUTPUT)
+ {
+ result = (status == TINFL_STATUS_DONE);
+ break;
+ }
+ dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1);
+ }
+ MZ_FREE(pDict);
+ *pIn_buf_size = in_buf_ofs;
+ return result;
+}
+
+#ifndef MINIZ_NO_MALLOC
+tinfl_decompressor *tinfl_decompressor_alloc(void)
+{
+ tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor));
+ if (pDecomp)
+ tinfl_init(pDecomp);
+ return pDecomp;
+}
+
+void tinfl_decompressor_free(tinfl_decompressor *pDecomp)
+{
+ MZ_FREE(pDecomp);
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/
+ /**************************************************************************
+ *
+ * Copyright 2013-2014 RAD Game Tools and Valve Software
+ * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
+ * Copyright 2016 Martin Raiber
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#ifndef MINIZ_NO_ARCHIVE_APIS
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ------------------- .ZIP archive reading */
+
+#ifdef MINIZ_NO_STDIO
+#define MZ_FILE void *
+#else
+#include
+
+#if defined(_MSC_VER) || defined(__MINGW64__)
+
+#define WIN32_LEAN_AND_MEAN
+#include
+
+static WCHAR* mz_utf8z_to_widechar(const char* str)
+{
+ int reqChars = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
+ WCHAR* wStr = (WCHAR*)malloc(reqChars * sizeof(WCHAR));
+ MultiByteToWideChar(CP_UTF8, 0, str, -1, wStr, reqChars);
+ return wStr;
+}
+
+static FILE *mz_fopen(const char *pFilename, const char *pMode)
+{
+ WCHAR* wFilename = mz_utf8z_to_widechar(pFilename);
+ WCHAR* wMode = mz_utf8z_to_widechar(pMode);
+ FILE* pFile = NULL;
+ errno_t err = _wfopen_s(&pFile, wFilename, wMode);
+ free(wFilename);
+ free(wMode);
+ return err ? NULL : pFile;
+}
+
+static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream)
+{
+ WCHAR* wPath = mz_utf8z_to_widechar(pPath);
+ WCHAR* wMode = mz_utf8z_to_widechar(pMode);
+ FILE* pFile = NULL;
+ errno_t err = _wfreopen_s(&pFile, wPath, wMode, pStream);
+ free(wPath);
+ free(wMode);
+ return err ? NULL : pFile;
+}
+
+static int mz_stat64(const char *path, struct __stat64 *buffer)
+{
+ WCHAR* wPath = mz_utf8z_to_widechar(path);
+ int res = _wstat64(wPath, buffer);
+ free(wPath);
+ return res;
+}
+
+#ifndef MINIZ_NO_TIME
+#include
+#endif
+#define MZ_FOPEN mz_fopen
+#define MZ_FCLOSE fclose
+#define MZ_FREAD fread
+#define MZ_FWRITE fwrite
+#define MZ_FTELL64 _ftelli64
+#define MZ_FSEEK64 _fseeki64
+#define MZ_FILE_STAT_STRUCT _stat64
+#define MZ_FILE_STAT mz_stat64
+#define MZ_FFLUSH fflush
+#define MZ_FREOPEN mz_freopen
+#define MZ_DELETE_FILE remove
+
+#elif defined(__MINGW32__) || defined(__WATCOMC__)
+#ifndef MINIZ_NO_TIME
+#include
+#endif
+#define MZ_FOPEN(f, m) fopen(f, m)
+#define MZ_FCLOSE fclose
+#define MZ_FREAD fread
+#define MZ_FWRITE fwrite
+#define MZ_FTELL64 _ftelli64
+#define MZ_FSEEK64 _fseeki64
+#define MZ_FILE_STAT_STRUCT stat
+#define MZ_FILE_STAT stat
+#define MZ_FFLUSH fflush
+#define MZ_FREOPEN(f, m, s) freopen(f, m, s)
+#define MZ_DELETE_FILE remove
+
+#elif defined(__TINYC__)
+#ifndef MINIZ_NO_TIME
+#include
+#endif
+#define MZ_FOPEN(f, m) fopen(f, m)
+#define MZ_FCLOSE fclose
+#define MZ_FREAD fread
+#define MZ_FWRITE fwrite
+#define MZ_FTELL64 ftell
+#define MZ_FSEEK64 fseek
+#define MZ_FILE_STAT_STRUCT stat
+#define MZ_FILE_STAT stat
+#define MZ_FFLUSH fflush
+#define MZ_FREOPEN(f, m, s) freopen(f, m, s)
+#define MZ_DELETE_FILE remove
+
+#elif defined(__USE_LARGEFILE64) /* gcc, clang */
+#ifndef MINIZ_NO_TIME
+#include
+#endif
+#define MZ_FOPEN(f, m) fopen64(f, m)
+#define MZ_FCLOSE fclose
+#define MZ_FREAD fread
+#define MZ_FWRITE fwrite
+#define MZ_FTELL64 ftello64
+#define MZ_FSEEK64 fseeko64
+#define MZ_FILE_STAT_STRUCT stat64
+#define MZ_FILE_STAT stat64
+#define MZ_FFLUSH fflush
+#define MZ_FREOPEN(p, m, s) freopen64(p, m, s)
+#define MZ_DELETE_FILE remove
+
+#elif defined(__APPLE__) || defined(__FreeBSD__)
+#ifndef MINIZ_NO_TIME
+#include
+#endif
+#define MZ_FOPEN(f, m) fopen(f, m)
+#define MZ_FCLOSE fclose
+#define MZ_FREAD fread
+#define MZ_FWRITE fwrite
+#define MZ_FTELL64 ftello
+#define MZ_FSEEK64 fseeko
+#define MZ_FILE_STAT_STRUCT stat
+#define MZ_FILE_STAT stat
+#define MZ_FFLUSH fflush
+#define MZ_FREOPEN(p, m, s) freopen(p, m, s)
+#define MZ_DELETE_FILE remove
+
+#else
+#pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.")
+#ifndef MINIZ_NO_TIME
+#include
+#endif
+#define MZ_FOPEN(f, m) fopen(f, m)
+#define MZ_FCLOSE fclose
+#define MZ_FREAD fread
+#define MZ_FWRITE fwrite
+#ifdef __STRICT_ANSI__
+#define MZ_FTELL64 ftell
+#define MZ_FSEEK64 fseek
+#else
+#define MZ_FTELL64 ftello
+#define MZ_FSEEK64 fseeko
+#endif
+#define MZ_FILE_STAT_STRUCT stat
+#define MZ_FILE_STAT stat
+#define MZ_FFLUSH fflush
+#define MZ_FREOPEN(f, m, s) freopen(f, m, s)
+#define MZ_DELETE_FILE remove
+#endif /* #ifdef _MSC_VER */
+#endif /* #ifdef MINIZ_NO_STDIO */
+
+#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c))
+
+/* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */
+enum
+{
+ /* ZIP archive identifiers and record sizes */
+ MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50,
+ MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50,
+ MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50,
+ MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30,
+ MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46,
+ MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22,
+
+ /* ZIP64 archive identifier and record sizes */
+ MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50,
+ MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50,
+ MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56,
+ MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20,
+ MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001,
+ MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50,
+ MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24,
+ MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16,
+
+ /* Central directory header record offsets */
+ MZ_ZIP_CDH_SIG_OFS = 0,
+ MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4,
+ MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6,
+ MZ_ZIP_CDH_BIT_FLAG_OFS = 8,
+ MZ_ZIP_CDH_METHOD_OFS = 10,
+ MZ_ZIP_CDH_FILE_TIME_OFS = 12,
+ MZ_ZIP_CDH_FILE_DATE_OFS = 14,
+ MZ_ZIP_CDH_CRC32_OFS = 16,
+ MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20,
+ MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24,
+ MZ_ZIP_CDH_FILENAME_LEN_OFS = 28,
+ MZ_ZIP_CDH_EXTRA_LEN_OFS = 30,
+ MZ_ZIP_CDH_COMMENT_LEN_OFS = 32,
+ MZ_ZIP_CDH_DISK_START_OFS = 34,
+ MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36,
+ MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38,
+ MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42,
+
+ /* Local directory header offsets */
+ MZ_ZIP_LDH_SIG_OFS = 0,
+ MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4,
+ MZ_ZIP_LDH_BIT_FLAG_OFS = 6,
+ MZ_ZIP_LDH_METHOD_OFS = 8,
+ MZ_ZIP_LDH_FILE_TIME_OFS = 10,
+ MZ_ZIP_LDH_FILE_DATE_OFS = 12,
+ MZ_ZIP_LDH_CRC32_OFS = 14,
+ MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18,
+ MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22,
+ MZ_ZIP_LDH_FILENAME_LEN_OFS = 26,
+ MZ_ZIP_LDH_EXTRA_LEN_OFS = 28,
+ MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3,
+
+ /* End of central directory offsets */
+ MZ_ZIP_ECDH_SIG_OFS = 0,
+ MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4,
+ MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6,
+ MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8,
+ MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10,
+ MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12,
+ MZ_ZIP_ECDH_CDIR_OFS_OFS = 16,
+ MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20,
+
+ /* ZIP64 End of central directory locator offsets */
+ MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */
+ MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */
+ MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */
+ MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */
+
+ /* ZIP64 End of central directory header offsets */
+ MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */
+ MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */
+ MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */
+ MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */
+ MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */
+ MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */
+ MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */
+ MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */
+ MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */
+ MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */
+ MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0,
+ MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10,
+ MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1,
+ MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32,
+ MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64,
+ MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192,
+ MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11
+};
+
+typedef struct
+{
+ void *m_p;
+ size_t m_size, m_capacity;
+ mz_uint m_element_size;
+} mz_zip_array;
+
+struct mz_zip_internal_state_tag
+{
+ mz_zip_array m_central_dir;
+ mz_zip_array m_central_dir_offsets;
+ mz_zip_array m_sorted_central_dir_offsets;
+
+ /* The flags passed in when the archive is initially opened. */
+ mz_uint32 m_init_flags;
+
+ /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */
+ mz_bool m_zip64;
+
+ /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */
+ mz_bool m_zip64_has_extended_info_fields;
+
+ /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */
+ MZ_FILE *m_pFile;
+ mz_uint64 m_file_archive_start_ofs;
+
+ void *m_pMem;
+ size_t m_mem_size;
+ size_t m_mem_capacity;
+};
+
+#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size
+
+#if defined(DEBUG) || defined(_DEBUG)
+static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index)
+{
+ MZ_ASSERT(index < pArray->m_size);
+ return index;
+}
+#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)]
+#else
+#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index]
+#endif
+
+static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size)
+{
+ memset(pArray, 0, sizeof(mz_zip_array));
+ pArray->m_element_size = element_size;
+}
+
+static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray)
+{
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p);
+ memset(pArray, 0, sizeof(mz_zip_array));
+}
+
+static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing)
+{
+ void *pNew_p;
+ size_t new_capacity = min_new_capacity;
+ MZ_ASSERT(pArray->m_element_size);
+ if (pArray->m_capacity >= min_new_capacity)
+ return MZ_TRUE;
+ if (growing)
+ {
+ new_capacity = MZ_MAX(1, pArray->m_capacity);
+ while (new_capacity < min_new_capacity)
+ new_capacity *= 2;
+ }
+ if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity)))
+ return MZ_FALSE;
+ pArray->m_p = pNew_p;
+ pArray->m_capacity = new_capacity;
+ return MZ_TRUE;
+}
+
+static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing)
+{
+ if (new_capacity > pArray->m_capacity)
+ {
+ if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing))
+ return MZ_FALSE;
+ }
+ return MZ_TRUE;
+}
+
+static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing)
+{
+ if (new_size > pArray->m_capacity)
+ {
+ if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing))
+ return MZ_FALSE;
+ }
+ pArray->m_size = new_size;
+ return MZ_TRUE;
+}
+
+static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n)
+{
+ return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE);
+}
+
+static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n)
+{
+ size_t orig_size = pArray->m_size;
+ if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE))
+ return MZ_FALSE;
+ if (n > 0)
+ memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size);
+ return MZ_TRUE;
+}
+
+#ifndef MINIZ_NO_TIME
+static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date)
+{
+ struct tm tm;
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_isdst = -1;
+ tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900;
+ tm.tm_mon = ((dos_date >> 5) & 15) - 1;
+ tm.tm_mday = dos_date & 31;
+ tm.tm_hour = (dos_time >> 11) & 31;
+ tm.tm_min = (dos_time >> 5) & 63;
+ tm.tm_sec = (dos_time << 1) & 62;
+ return mktime(&tm);
+}
+
+#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
+static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date)
+{
+#ifdef _MSC_VER
+ struct tm tm_struct;
+ struct tm *tm = &tm_struct;
+ errno_t err = localtime_s(tm, &time);
+ if (err)
+ {
+ *pDOS_date = 0;
+ *pDOS_time = 0;
+ return;
+ }
+#else
+ struct tm *tm = localtime(&time);
+#endif /* #ifdef _MSC_VER */
+
+ *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1));
+ *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday);
+}
+#endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */
+
+#ifndef MINIZ_NO_STDIO
+#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
+static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime)
+{
+ struct MZ_FILE_STAT_STRUCT file_stat;
+
+ /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */
+ if (MZ_FILE_STAT(pFilename, &file_stat) != 0)
+ return MZ_FALSE;
+
+ *pTime = file_stat.st_mtime;
+
+ return MZ_TRUE;
+}
+#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/
+
+static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time)
+{
+ struct utimbuf t;
+
+ memset(&t, 0, sizeof(t));
+ t.actime = access_time;
+ t.modtime = modified_time;
+
+ return !utime(pFilename, &t);
+}
+#endif /* #ifndef MINIZ_NO_STDIO */
+#endif /* #ifndef MINIZ_NO_TIME */
+
+static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num)
+{
+ if (pZip)
+ pZip->m_last_error = err_num;
+ return MZ_FALSE;
+}
+
+static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags)
+{
+ (void)flags;
+ if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ if (!pZip->m_pAlloc)
+ pZip->m_pAlloc = miniz_def_alloc_func;
+ if (!pZip->m_pFree)
+ pZip->m_pFree = miniz_def_free_func;
+ if (!pZip->m_pRealloc)
+ pZip->m_pRealloc = miniz_def_realloc_func;
+
+ pZip->m_archive_size = 0;
+ pZip->m_central_directory_file_ofs = 0;
+ pZip->m_total_files = 0;
+ pZip->m_last_error = MZ_ZIP_NO_ERROR;
+
+ if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+
+ memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));
+ MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));
+ MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));
+ MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));
+ pZip->m_pState->m_init_flags = flags;
+ pZip->m_pState->m_zip64 = MZ_FALSE;
+ pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE;
+
+ pZip->m_zip_mode = MZ_ZIP_MODE_READING;
+
+ return MZ_TRUE;
+}
+
+static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index)
+{
+ const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;
+ const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index));
+ mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS);
+ mz_uint8 l = 0, r = 0;
+ pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
+ pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
+ pE = pL + MZ_MIN(l_len, r_len);
+ while (pL < pE)
+ {
+ if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
+ break;
+ pL++;
+ pR++;
+ }
+ return (pL == pE) ? (l_len < r_len) : (l < r);
+}
+
+#define MZ_SWAP_UINT32(a, b) \
+ do \
+ { \
+ mz_uint32 t = a; \
+ a = b; \
+ b = t; \
+ } \
+ MZ_MACRO_END
+
+/* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */
+static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip)
+{
+ mz_zip_internal_state *pState = pZip->m_pState;
+ const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
+ const mz_zip_array *pCentral_dir = &pState->m_central_dir;
+ mz_uint32 *pIndices;
+ mz_uint32 start, end;
+ const mz_uint32 size = pZip->m_total_files;
+
+ if (size <= 1U)
+ return;
+
+ pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
+
+ start = (size - 2U) >> 1U;
+ for (;;)
+ {
+ mz_uint64 child, root = start;
+ for (;;)
+ {
+ if ((child = (root << 1U) + 1U) >= size)
+ break;
+ child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])));
+ if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
+ break;
+ MZ_SWAP_UINT32(pIndices[root], pIndices[child]);
+ root = child;
+ }
+ if (!start)
+ break;
+ start--;
+ }
+
+ end = size - 1;
+ while (end > 0)
+ {
+ mz_uint64 child, root = 0;
+ MZ_SWAP_UINT32(pIndices[end], pIndices[0]);
+ for (;;)
+ {
+ if ((child = (root << 1U) + 1U) >= end)
+ break;
+ child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]));
+ if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
+ break;
+ MZ_SWAP_UINT32(pIndices[root], pIndices[child]);
+ root = child;
+ }
+ end--;
+ }
+}
+
+static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs)
+{
+ mz_int64 cur_file_ofs;
+ mz_uint32 buf_u32[4096 / sizeof(mz_uint32)];
+ mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
+
+ /* Basic sanity checks - reject files which are too small */
+ if (pZip->m_archive_size < record_size)
+ return MZ_FALSE;
+
+ /* Find the record by scanning the file from the end towards the beginning. */
+ cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0);
+ for (;;)
+ {
+ int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs);
+
+ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n)
+ return MZ_FALSE;
+
+ for (i = n - 4; i >= 0; --i)
+ {
+ mz_uint s = MZ_READ_LE32(pBuf + i);
+ if (s == record_sig)
+ {
+ if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size)
+ break;
+ }
+ }
+
+ if (i >= 0)
+ {
+ cur_file_ofs += i;
+ break;
+ }
+
+ /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */
+ if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size)))
+ return MZ_FALSE;
+
+ cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0);
+ }
+
+ *pOfs = cur_file_ofs;
+ return MZ_TRUE;
+}
+
+static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags)
+{
+ mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0;
+ mz_uint64 cdir_ofs = 0;
+ mz_int64 cur_file_ofs = 0;
+ const mz_uint8 *p;
+
+ mz_uint32 buf_u32[4096 / sizeof(mz_uint32)];
+ mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
+ mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0);
+ mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];
+ mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32;
+
+ mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];
+ mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32;
+
+ mz_uint64 zip64_end_of_central_dir_ofs = 0;
+
+ /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */
+ if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
+ return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
+
+ if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs))
+ return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR);
+
+ /* Read and verify the end of central directory record. */
+ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+
+ if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG)
+ return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
+
+ if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE))
+ {
+ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE)
+ {
+ if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG)
+ {
+ zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS);
+ if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE))
+ return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
+
+ if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)
+ {
+ if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG)
+ {
+ pZip->m_pState->m_zip64 = MZ_TRUE;
+ }
+ }
+ }
+ }
+ }
+
+ pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS);
+ cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS);
+ num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS);
+ cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS);
+ cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS);
+ cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS);
+
+ if (pZip->m_pState->m_zip64)
+ {
+ mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS);
+ mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS);
+ mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS);
+ mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS);
+ mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS);
+
+ if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ if (zip64_total_num_of_disks != 1U)
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
+
+ /* Check for miniz's practical limits */
+ if (zip64_cdir_total_entries > MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
+
+ pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries;
+
+ if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
+
+ cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk;
+
+ /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */
+ if (zip64_size_of_central_directory > MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);
+
+ cdir_size = (mz_uint32)zip64_size_of_central_directory;
+
+ num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS);
+
+ cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS);
+
+ cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS);
+ }
+
+ if (pZip->m_total_files != cdir_entries_on_this_disk)
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
+
+ if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1)))
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
+
+ if (cdir_size < (mz_uint64)pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ pZip->m_central_directory_file_ofs = cdir_ofs;
+
+ if (pZip->m_total_files)
+ {
+ mz_uint i, n;
+ /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */
+ if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) ||
+ (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE)))
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+
+ if (sort_central_dir)
+ {
+ if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE))
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ }
+
+ if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+
+ /* Now create an index into the central directory file records, do some basic sanity checking on each record */
+ p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p;
+ for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i)
+ {
+ mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size;
+ mz_uint64 comp_size, decomp_size, local_header_ofs;
+
+ if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p);
+
+ if (sort_central_dir)
+ MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i;
+
+ comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
+ decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
+ local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
+ filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
+ ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS);
+
+ if ((!pZip->m_pState->m_zip64_has_extended_info_fields) &&
+ (ext_data_size) &&
+ (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX))
+ {
+ /* Attempt to find zip64 extended information field in the entry's extra data */
+ mz_uint32 extra_size_remaining = ext_data_size;
+
+ if (extra_size_remaining)
+ {
+ const mz_uint8 *pExtra_data;
+ void* buf = NULL;
+
+ if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size > n)
+ {
+ buf = MZ_MALLOC(ext_data_size);
+ if(buf==NULL)
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+
+ if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size, buf, ext_data_size) != ext_data_size)
+ {
+ MZ_FREE(buf);
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+ }
+
+ pExtra_data = (mz_uint8*)buf;
+ }
+ else
+ {
+ pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size;
+ }
+
+ do
+ {
+ mz_uint32 field_id;
+ mz_uint32 field_data_size;
+
+ if (extra_size_remaining < (sizeof(mz_uint16) * 2))
+ {
+ MZ_FREE(buf);
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+ }
+
+ field_id = MZ_READ_LE16(pExtra_data);
+ field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));
+
+ if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining)
+ {
+ MZ_FREE(buf);
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+ }
+
+ if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)
+ {
+ /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */
+ pZip->m_pState->m_zip64 = MZ_TRUE;
+ pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE;
+ break;
+ }
+
+ pExtra_data += sizeof(mz_uint16) * 2 + field_data_size;
+ extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size;
+ } while (extra_size_remaining);
+
+ MZ_FREE(buf);
+ }
+ }
+
+ /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */
+ if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX))
+ {
+ if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+ }
+
+ disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS);
+ if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1)))
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
+
+ if (comp_size != MZ_UINT32_MAX)
+ {
+ if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+ }
+
+ bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
+ if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED)
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);
+
+ if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ n -= total_header_size;
+ p += total_header_size;
+ }
+ }
+
+ if (sort_central_dir)
+ mz_zip_reader_sort_central_dir_offsets_by_filename(pZip);
+
+ return MZ_TRUE;
+}
+
+void mz_zip_zero_struct(mz_zip_archive *pZip)
+{
+ if (pZip)
+ MZ_CLEAR_PTR(pZip);
+}
+
+static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error)
+{
+ mz_bool status = MZ_TRUE;
+
+ if (!pZip)
+ return MZ_FALSE;
+
+ if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
+ {
+ if (set_last_error)
+ pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER;
+
+ return MZ_FALSE;
+ }
+
+ if (pZip->m_pState)
+ {
+ mz_zip_internal_state *pState = pZip->m_pState;
+ pZip->m_pState = NULL;
+
+ mz_zip_array_clear(pZip, &pState->m_central_dir);
+ mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
+ mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);
+
+#ifndef MINIZ_NO_STDIO
+ if (pState->m_pFile)
+ {
+ if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE)
+ {
+ if (MZ_FCLOSE(pState->m_pFile) == EOF)
+ {
+ if (set_last_error)
+ pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED;
+ status = MZ_FALSE;
+ }
+ }
+ pState->m_pFile = NULL;
+ }
+#endif /* #ifndef MINIZ_NO_STDIO */
+
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
+ }
+ pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
+
+ return status;
+}
+
+mz_bool mz_zip_reader_end(mz_zip_archive *pZip)
+{
+ return mz_zip_reader_end_internal(pZip, MZ_TRUE);
+}
+mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags)
+{
+ if ((!pZip) || (!pZip->m_pRead))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ if (!mz_zip_reader_init_internal(pZip, flags))
+ return MZ_FALSE;
+
+ pZip->m_zip_type = MZ_ZIP_TYPE_USER;
+ pZip->m_archive_size = size;
+
+ if (!mz_zip_reader_read_central_dir(pZip, flags))
+ {
+ mz_zip_reader_end_internal(pZip, MZ_FALSE);
+ return MZ_FALSE;
+ }
+
+ return MZ_TRUE;
+}
+
+static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
+{
+ mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
+ size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n);
+ memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s);
+ return s;
+}
+
+mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags)
+{
+ if (!pMem)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
+ return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
+
+ if (!mz_zip_reader_init_internal(pZip, flags))
+ return MZ_FALSE;
+
+ pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY;
+ pZip->m_archive_size = size;
+ pZip->m_pRead = mz_zip_mem_read_func;
+ pZip->m_pIO_opaque = pZip;
+ pZip->m_pNeeds_keepalive = NULL;
+
+#ifdef __cplusplus
+ pZip->m_pState->m_pMem = const_cast(pMem);
+#else
+ pZip->m_pState->m_pMem = (void *)pMem;
+#endif
+
+ pZip->m_pState->m_mem_size = size;
+
+ if (!mz_zip_reader_read_central_dir(pZip, flags))
+ {
+ mz_zip_reader_end_internal(pZip, MZ_FALSE);
+ return MZ_FALSE;
+ }
+
+ return MZ_TRUE;
+}
+
+#ifndef MINIZ_NO_STDIO
+static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
+{
+ mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
+ mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
+
+ file_ofs += pZip->m_pState->m_file_archive_start_ofs;
+
+ if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))
+ return 0;
+
+ return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile);
+}
+
+mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags)
+{
+ return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0);
+}
+
+mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size)
+{
+ mz_uint64 file_size;
+ MZ_FILE *pFile;
+
+ if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ pFile = MZ_FOPEN(pFilename, "rb");
+ if (!pFile)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);
+
+ file_size = archive_size;
+ if (!file_size)
+ {
+ if (MZ_FSEEK64(pFile, 0, SEEK_END))
+ {
+ MZ_FCLOSE(pFile);
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED);
+ }
+
+ file_size = MZ_FTELL64(pFile);
+ }
+
+ /* TODO: Better sanity check archive_size and the # of actual remaining bytes */
+
+ if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
+ {
+ MZ_FCLOSE(pFile);
+ return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
+ }
+
+ if (!mz_zip_reader_init_internal(pZip, flags))
+ {
+ MZ_FCLOSE(pFile);
+ return MZ_FALSE;
+ }
+
+ pZip->m_zip_type = MZ_ZIP_TYPE_FILE;
+ pZip->m_pRead = mz_zip_file_read_func;
+ pZip->m_pIO_opaque = pZip;
+ pZip->m_pState->m_pFile = pFile;
+ pZip->m_archive_size = file_size;
+ pZip->m_pState->m_file_archive_start_ofs = file_start_ofs;
+
+ if (!mz_zip_reader_read_central_dir(pZip, flags))
+ {
+ mz_zip_reader_end_internal(pZip, MZ_FALSE);
+ return MZ_FALSE;
+ }
+
+ return MZ_TRUE;
+}
+
+mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags)
+{
+ mz_uint64 cur_file_ofs;
+
+ if ((!pZip) || (!pFile))
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);
+
+ cur_file_ofs = MZ_FTELL64(pFile);
+
+ if (!archive_size)
+ {
+ if (MZ_FSEEK64(pFile, 0, SEEK_END))
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED);
+
+ archive_size = MZ_FTELL64(pFile) - cur_file_ofs;
+
+ if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
+ return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
+ }
+
+ if (!mz_zip_reader_init_internal(pZip, flags))
+ return MZ_FALSE;
+
+ pZip->m_zip_type = MZ_ZIP_TYPE_CFILE;
+ pZip->m_pRead = mz_zip_file_read_func;
+
+ pZip->m_pIO_opaque = pZip;
+ pZip->m_pState->m_pFile = pFile;
+ pZip->m_archive_size = archive_size;
+ pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs;
+
+ if (!mz_zip_reader_read_central_dir(pZip, flags))
+ {
+ mz_zip_reader_end_internal(pZip, MZ_FALSE);
+ return MZ_FALSE;
+ }
+
+ return MZ_TRUE;
+}
+
+#endif /* #ifndef MINIZ_NO_STDIO */
+
+static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index)
+{
+ if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files))
+ return NULL;
+ return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));
+}
+
+mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index)
+{
+ mz_uint m_bit_flag;
+ const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);
+ if (!p)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+ return MZ_FALSE;
+ }
+
+ m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
+ return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0;
+}
+
+mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index)
+{
+ mz_uint bit_flag;
+ mz_uint method;
+
+ const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);
+ if (!p)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+ return MZ_FALSE;
+ }
+
+ method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS);
+ bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
+
+ if ((method != 0) && (method != MZ_DEFLATED))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD);
+ return MZ_FALSE;
+ }
+
+ if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);
+ return MZ_FALSE;
+ }
+
+ if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE);
+ return MZ_FALSE;
+ }
+
+ return MZ_TRUE;
+}
+
+mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index)
+{
+ mz_uint filename_len, attribute_mapping_id, external_attr;
+ const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);
+ if (!p)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+ return MZ_FALSE;
+ }
+
+ filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
+ if (filename_len)
+ {
+ if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/')
+ return MZ_TRUE;
+ }
+
+ /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. */
+ /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. */
+ /* FIXME: Remove this check? Is it necessary - we already check the filename. */
+ attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8;
+ (void)attribute_mapping_id;
+
+ external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
+ if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0)
+ {
+ return MZ_TRUE;
+ }
+
+ return MZ_FALSE;
+}
+
+static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data)
+{
+ mz_uint n;
+ const mz_uint8 *p = pCentral_dir_header;
+
+ if (pFound_zip64_extra_data)
+ *pFound_zip64_extra_data = MZ_FALSE;
+
+ if ((!p) || (!pStat))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ /* Extract fields from the central directory record. */
+ pStat->m_file_index = file_index;
+ pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index);
+ pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS);
+ pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS);
+ pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
+ pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS);
+#ifndef MINIZ_NO_TIME
+ pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS));
+#endif
+ pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS);
+ pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
+ pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
+ pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS);
+ pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
+ pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
+
+ /* Copy as much of the filename and comment as possible. */
+ n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
+ n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1);
+ memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n);
+ pStat->m_filename[n] = '\0';
+
+ n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS);
+ n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1);
+ pStat->m_comment_size = n;
+ memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n);
+ pStat->m_comment[n] = '\0';
+
+ /* Set some flags for convienance */
+ pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index);
+ pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index);
+ pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index);
+
+ /* See if we need to read any zip64 extended information fields. */
+ /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */
+ if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX)
+ {
+ /* Attempt to find zip64 extended information field in the entry's extra data */
+ mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS);
+
+ if (extra_size_remaining)
+ {
+ const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
+
+ do
+ {
+ mz_uint32 field_id;
+ mz_uint32 field_data_size;
+
+ if (extra_size_remaining < (sizeof(mz_uint16) * 2))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ field_id = MZ_READ_LE16(pExtra_data);
+ field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));
+
+ if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)
+ {
+ const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2;
+ mz_uint32 field_data_remaining = field_data_size;
+
+ if (pFound_zip64_extra_data)
+ *pFound_zip64_extra_data = MZ_TRUE;
+
+ if (pStat->m_uncomp_size == MZ_UINT32_MAX)
+ {
+ if (field_data_remaining < sizeof(mz_uint64))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ pStat->m_uncomp_size = MZ_READ_LE64(pField_data);
+ pField_data += sizeof(mz_uint64);
+ field_data_remaining -= sizeof(mz_uint64);
+ }
+
+ if (pStat->m_comp_size == MZ_UINT32_MAX)
+ {
+ if (field_data_remaining < sizeof(mz_uint64))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ pStat->m_comp_size = MZ_READ_LE64(pField_data);
+ pField_data += sizeof(mz_uint64);
+ field_data_remaining -= sizeof(mz_uint64);
+ }
+
+ if (pStat->m_local_header_ofs == MZ_UINT32_MAX)
+ {
+ if (field_data_remaining < sizeof(mz_uint64))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ pStat->m_local_header_ofs = MZ_READ_LE64(pField_data);
+ pField_data += sizeof(mz_uint64);
+ field_data_remaining -= sizeof(mz_uint64);
+ }
+
+ break;
+ }
+
+ pExtra_data += sizeof(mz_uint16) * 2 + field_data_size;
+ extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size;
+ } while (extra_size_remaining);
+ }
+ }
+
+ return MZ_TRUE;
+}
+
+static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags)
+{
+ mz_uint i;
+ if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE)
+ return 0 == memcmp(pA, pB, len);
+ for (i = 0; i < len; ++i)
+ if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i]))
+ return MZ_FALSE;
+ return MZ_TRUE;
+}
+
+static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len)
+{
+ const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;
+ mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS);
+ mz_uint8 l = 0, r = 0;
+ pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
+ pE = pL + MZ_MIN(l_len, r_len);
+ while (pL < pE)
+ {
+ if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
+ break;
+ pL++;
+ pR++;
+ }
+ return (pL == pE) ? (int)(l_len - r_len) : (l - r);
+}
+
+static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex)
+{
+ mz_zip_internal_state *pState = pZip->m_pState;
+ const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
+ const mz_zip_array *pCentral_dir = &pState->m_central_dir;
+ mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
+ const mz_uint32 size = pZip->m_total_files;
+ const mz_uint filename_len = (mz_uint)strlen(pFilename);
+
+ if (pIndex)
+ *pIndex = 0;
+
+ if (size)
+ {
+ /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */
+ /* honestly the major expense here on 32-bit CPU's will still be the filename compare */
+ mz_int64 l = 0, h = (mz_int64)size - 1;
+
+ while (l <= h)
+ {
+ mz_int64 m = l + ((h - l) >> 1);
+ mz_uint32 file_index = pIndices[(mz_uint32)m];
+
+ int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len);
+ if (!comp)
+ {
+ if (pIndex)
+ *pIndex = file_index;
+ return MZ_TRUE;
+ }
+ else if (comp < 0)
+ l = m + 1;
+ else
+ h = m - 1;
+ }
+ }
+
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND);
+}
+
+int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags)
+{
+ mz_uint32 index;
+ if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index))
+ return -1;
+ else
+ return (int)index;
+}
+
+mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex)
+{
+ mz_uint file_index;
+ size_t name_len, comment_len;
+
+ if (pIndex)
+ *pIndex = 0;
+
+ if ((!pZip) || (!pZip->m_pState) || (!pName))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ /* See if we can use a binary search */
+ if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) &&
+ (pZip->m_zip_mode == MZ_ZIP_MODE_READING) &&
+ ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size))
+ {
+ return mz_zip_locate_file_binary_search(pZip, pName, pIndex);
+ }
+
+ /* Locate the entry by scanning the entire central directory */
+ name_len = strlen(pName);
+ if (name_len > MZ_UINT16_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ comment_len = pComment ? strlen(pComment) : 0;
+ if (comment_len > MZ_UINT16_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ for (file_index = 0; file_index < pZip->m_total_files; file_index++)
+ {
+ const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));
+ mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS);
+ const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
+ if (filename_len < name_len)
+ continue;
+ if (comment_len)
+ {
+ mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS);
+ const char *pFile_comment = pFilename + filename_len + file_extra_len;
+ if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags)))
+ continue;
+ }
+ if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len))
+ {
+ int ofs = filename_len - 1;
+ do
+ {
+ if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':'))
+ break;
+ } while (--ofs >= 0);
+ ofs++;
+ pFilename += ofs;
+ filename_len -= ofs;
+ }
+ if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags)))
+ {
+ if (pIndex)
+ *pIndex = file_index;
+ return MZ_TRUE;
+ }
+ }
+
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND);
+}
+
+static
+mz_bool mz_zip_reader_extract_to_mem_no_alloc1(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size, const mz_zip_archive_file_stat *st)
+{
+ int status = TINFL_STATUS_DONE;
+ mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail;
+ mz_zip_archive_file_stat file_stat;
+ void *pRead_buf;
+ mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];
+ mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
+ tinfl_decompressor inflator;
+
+ if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ if (st) {
+ file_stat = *st;
+ } else
+ if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
+ return MZ_FALSE;
+
+ /* A directory or zero length file */
+ if ((file_stat.m_is_directory) || (!file_stat.m_comp_size))
+ return MZ_TRUE;
+
+ /* Encryption and patch files are not supported. */
+ if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG))
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);
+
+ /* This function only supports decompressing stored and deflate. */
+ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD);
+
+ /* Ensure supplied output buffer is large enough. */
+ needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size;
+ if (buf_size < needed_size)
+ return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL);
+
+ /* Read and parse the local directory entry. */
+ cur_file_ofs = file_stat.m_local_header_ofs;
+ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+
+ if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
+ if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
+ {
+ /* The file is stored or the caller has requested the compressed data. */
+ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+
+#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
+ if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0)
+ {
+ if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)
+ return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED);
+ }
+#endif
+
+ return MZ_TRUE;
+ }
+
+ /* Decompress the file either directly from memory or from a file input buffer. */
+ tinfl_init(&inflator);
+
+ if (pZip->m_pState->m_pMem)
+ {
+ /* Read directly from the archive in memory. */
+ pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;
+ read_buf_size = read_buf_avail = file_stat.m_comp_size;
+ comp_remaining = 0;
+ }
+ else if (pUser_read_buf)
+ {
+ /* Use a user provided read buffer. */
+ if (!user_read_buf_size)
+ return MZ_FALSE;
+ pRead_buf = (mz_uint8 *)pUser_read_buf;
+ read_buf_size = user_read_buf_size;
+ read_buf_avail = 0;
+ comp_remaining = file_stat.m_comp_size;
+ }
+ else
+ {
+ /* Temporarily allocate a read buffer. */
+ read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE);
+ if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF))
+ return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
+
+ if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+
+ read_buf_avail = 0;
+ comp_remaining = file_stat.m_comp_size;
+ }
+
+ do
+ {
+ /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */
+ size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs);
+ if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))
+ {
+ read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
+ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
+ {
+ status = TINFL_STATUS_FAILED;
+ mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED);
+ break;
+ }
+ cur_file_ofs += read_buf_avail;
+ comp_remaining -= read_buf_avail;
+ read_buf_ofs = 0;
+ }
+ in_buf_size = (size_t)read_buf_avail;
+ status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0));
+ read_buf_avail -= in_buf_size;
+ read_buf_ofs += in_buf_size;
+ out_buf_ofs += out_buf_size;
+ } while (status == TINFL_STATUS_NEEDS_MORE_INPUT);
+
+ if (status == TINFL_STATUS_DONE)
+ {
+ /* Make sure the entire file was decompressed, and check its CRC. */
+ if (out_buf_ofs != file_stat.m_uncomp_size)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE);
+ status = TINFL_STATUS_FAILED;
+ }
+#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
+ else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED);
+ status = TINFL_STATUS_FAILED;
+ }
+#endif
+ }
+
+ if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf))
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
+
+ return status == TINFL_STATUS_DONE;
+}
+
+mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
+{
+ return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL);
+}
+
+mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
+{
+ mz_uint32 file_index;
+ if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index))
+ return MZ_FALSE;
+ return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL);
+}
+
+mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags)
+{
+ return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, NULL, 0, NULL);
+}
+
+mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags)
+{
+ return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0);
+}
+
+void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags)
+{
+ mz_zip_archive_file_stat file_stat;
+ mz_uint64 alloc_size;
+ void *pBuf;
+
+ if (pSize)
+ *pSize = 0;
+
+ if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
+ return NULL;
+
+ alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size;
+ if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
+ return NULL;
+ }
+
+ if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size)))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ return NULL;
+ }
+
+ if (!mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, (size_t)alloc_size, flags, NULL, 0, &file_stat))
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
+ return NULL;
+ }
+
+ if (pSize)
+ *pSize = (size_t)alloc_size;
+ return pBuf;
+}
+
+void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags)
+{
+ mz_uint32 file_index;
+ if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index))
+ {
+ if (pSize)
+ *pSize = 0;
+ return MZ_FALSE;
+ }
+ return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags);
+}
+
+mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)
+{
+ int status = TINFL_STATUS_DONE;
+#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
+ mz_uint file_crc32 = MZ_CRC32_INIT;
+#endif
+ mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs;
+ mz_zip_archive_file_stat file_stat;
+ void *pRead_buf = NULL;
+ void *pWrite_buf = NULL;
+ mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];
+ mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
+
+ if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
+ return MZ_FALSE;
+
+ /* A directory or zero length file */
+ if ((file_stat.m_is_directory) || (!file_stat.m_comp_size))
+ return MZ_TRUE;
+
+ /* Encryption and patch files are not supported. */
+ if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG))
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);
+
+ /* This function only supports decompressing stored and deflate. */
+ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD);
+
+ /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */
+ cur_file_ofs = file_stat.m_local_header_ofs;
+ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+
+ if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
+ if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ /* Decompress the file either directly from memory or from a file input buffer. */
+ if (pZip->m_pState->m_pMem)
+ {
+ pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;
+ read_buf_size = read_buf_avail = file_stat.m_comp_size;
+ comp_remaining = 0;
+ }
+ else
+ {
+ read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE);
+ if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+
+ read_buf_avail = 0;
+ comp_remaining = file_stat.m_comp_size;
+ }
+
+ if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
+ {
+ /* The file is stored or the caller has requested the compressed data. */
+ if (pZip->m_pState->m_pMem)
+ {
+ if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX))
+ return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
+
+ if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED);
+ status = TINFL_STATUS_FAILED;
+ }
+ else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
+ {
+#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
+ file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size);
+#endif
+ }
+
+ cur_file_ofs += file_stat.m_comp_size;
+ out_buf_ofs += file_stat.m_comp_size;
+ comp_remaining = 0;
+ }
+ else
+ {
+ while (comp_remaining)
+ {
+ read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
+ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+ status = TINFL_STATUS_FAILED;
+ break;
+ }
+
+#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
+ if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
+ {
+ file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail);
+ }
+#endif
+
+ if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED);
+ status = TINFL_STATUS_FAILED;
+ break;
+ }
+
+ cur_file_ofs += read_buf_avail;
+ out_buf_ofs += read_buf_avail;
+ comp_remaining -= read_buf_avail;
+ }
+ }
+ }
+ else
+ {
+ tinfl_decompressor inflator;
+ tinfl_init(&inflator);
+
+ if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE)))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ status = TINFL_STATUS_FAILED;
+ }
+ else
+ {
+ do
+ {
+ mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
+ size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
+ if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))
+ {
+ read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
+ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+ status = TINFL_STATUS_FAILED;
+ break;
+ }
+ cur_file_ofs += read_buf_avail;
+ comp_remaining -= read_buf_avail;
+ read_buf_ofs = 0;
+ }
+
+ in_buf_size = (size_t)read_buf_avail;
+ status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0);
+ read_buf_avail -= in_buf_size;
+ read_buf_ofs += in_buf_size;
+
+ if (out_buf_size)
+ {
+ if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED);
+ status = TINFL_STATUS_FAILED;
+ break;
+ }
+
+#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
+ file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size);
+#endif
+ if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED);
+ status = TINFL_STATUS_FAILED;
+ break;
+ }
+ }
+ } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT));
+ }
+ }
+
+ if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)))
+ {
+ /* Make sure the entire file was decompressed, and check its CRC. */
+ if (out_buf_ofs != file_stat.m_uncomp_size)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE);
+ status = TINFL_STATUS_FAILED;
+ }
+#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
+ else if (file_crc32 != file_stat.m_crc32)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED);
+ status = TINFL_STATUS_FAILED;
+ }
+#endif
+ }
+
+ if (!pZip->m_pState->m_pMem)
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
+
+ if (pWrite_buf)
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf);
+
+ return status == TINFL_STATUS_DONE;
+}
+
+mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)
+{
+ mz_uint32 file_index;
+ if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index))
+ return MZ_FALSE;
+
+ return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags);
+}
+
+mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags)
+{
+ mz_zip_reader_extract_iter_state *pState;
+ mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];
+ mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
+
+ /* Argument sanity check */
+ if ((!pZip) || (!pZip->m_pState))
+ return NULL;
+
+ /* Allocate an iterator status structure */
+ pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state));
+ if (!pState)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ return NULL;
+ }
+
+ /* Fetch file details */
+ if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat))
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
+ return NULL;
+ }
+
+ /* Encryption and patch files are not supported. */
+ if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
+ return NULL;
+ }
+
+ /* This function only supports decompressing stored and deflate. */
+ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD);
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
+ return NULL;
+ }
+
+ /* Init state - save args */
+ pState->pZip = pZip;
+ pState->flags = flags;
+
+ /* Init state - reset variables to defaults */
+ pState->status = TINFL_STATUS_DONE;
+#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
+ pState->file_crc32 = MZ_CRC32_INIT;
+#endif
+ pState->read_buf_ofs = 0;
+ pState->out_buf_ofs = 0;
+ pState->pRead_buf = NULL;
+ pState->pWrite_buf = NULL;
+ pState->out_blk_remain = 0;
+
+ /* Read and parse the local directory entry. */
+ pState->cur_file_ofs = pState->file_stat.m_local_header_ofs;
+ if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
+ return NULL;
+ }
+
+ if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
+ return NULL;
+ }
+
+ pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
+ if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
+ return NULL;
+ }
+
+ /* Decompress the file either directly from memory or from a file input buffer. */
+ if (pZip->m_pState->m_pMem)
+ {
+ pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs;
+ pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size;
+ pState->comp_remaining = pState->file_stat.m_comp_size;
+ }
+ else
+ {
+ if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)))
+ {
+ /* Decompression required, therefore intermediate read buffer required */
+ pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE);
+ if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size)))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
+ return NULL;
+ }
+ }
+ else
+ {
+ /* Decompression not required - we will be reading directly into user buffer, no temp buf required */
+ pState->read_buf_size = 0;
+ }
+ pState->read_buf_avail = 0;
+ pState->comp_remaining = pState->file_stat.m_comp_size;
+ }
+
+ if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)))
+ {
+ /* Decompression required, init decompressor */
+ tinfl_init( &pState->inflator );
+
+ /* Allocate write buffer */
+ if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE)))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ if (pState->pRead_buf)
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf);
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
+ return NULL;
+ }
+ }
+
+ return pState;
+}
+
+mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags)
+{
+ mz_uint32 file_index;
+
+ /* Locate file index by name */
+ if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index))
+ return NULL;
+
+ /* Construct iterator */
+ return mz_zip_reader_extract_iter_new(pZip, file_index, flags);
+}
+
+size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size)
+{
+ size_t copied_to_caller = 0;
+
+ /* Argument sanity check */
+ if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf))
+ return 0;
+
+ if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))
+ {
+ /* The file is stored or the caller has requested the compressed data, calc amount to return. */
+ copied_to_caller = (size_t)MZ_MIN( buf_size, pState->comp_remaining );
+
+ /* Zip is in memory....or requires reading from a file? */
+ if (pState->pZip->m_pState->m_pMem)
+ {
+ /* Copy data to caller's buffer */
+ memcpy( pvBuf, pState->pRead_buf, copied_to_caller );
+ pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller;
+ }
+ else
+ {
+ /* Read directly into caller's buffer */
+ if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller)
+ {
+ /* Failed to read all that was asked for, flag failure and alert user */
+ mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED);
+ pState->status = TINFL_STATUS_FAILED;
+ copied_to_caller = 0;
+ }
+ }
+
+#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
+ /* Compute CRC if not returning compressed data only */
+ if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
+ pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller);
+#endif
+
+ /* Advance offsets, dec counters */
+ pState->cur_file_ofs += copied_to_caller;
+ pState->out_buf_ofs += copied_to_caller;
+ pState->comp_remaining -= copied_to_caller;
+ }
+ else
+ {
+ do
+ {
+ /* Calc ptr to write buffer - given current output pos and block size */
+ mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
+
+ /* Calc max output size - given current output pos and block size */
+ size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
+
+ if (!pState->out_blk_remain)
+ {
+ /* Read more data from file if none available (and reading from file) */
+ if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem))
+ {
+ /* Calc read size */
+ pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining);
+ if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail)
+ {
+ mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED);
+ pState->status = TINFL_STATUS_FAILED;
+ break;
+ }
+
+ /* Advance offsets, dec counters */
+ pState->cur_file_ofs += pState->read_buf_avail;
+ pState->comp_remaining -= pState->read_buf_avail;
+ pState->read_buf_ofs = 0;
+ }
+
+ /* Perform decompression */
+ in_buf_size = (size_t)pState->read_buf_avail;
+ pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0);
+ pState->read_buf_avail -= in_buf_size;
+ pState->read_buf_ofs += in_buf_size;
+
+ /* Update current output block size remaining */
+ pState->out_blk_remain = out_buf_size;
+ }
+
+ if (pState->out_blk_remain)
+ {
+ /* Calc amount to return. */
+ size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain );
+
+ /* Copy data to caller's buffer */
+ memcpy( (mz_uint8*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy );
+
+#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
+ /* Perform CRC */
+ pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy);
+#endif
+
+ /* Decrement data consumed from block */
+ pState->out_blk_remain -= to_copy;
+
+ /* Inc output offset, while performing sanity check */
+ if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size)
+ {
+ mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED);
+ pState->status = TINFL_STATUS_FAILED;
+ break;
+ }
+
+ /* Increment counter of data copied to caller */
+ copied_to_caller += to_copy;
+ }
+ } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) );
+ }
+
+ /* Return how many bytes were copied into user buffer */
+ return copied_to_caller;
+}
+
+mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState)
+{
+ int status;
+
+ /* Argument sanity check */
+ if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState))
+ return MZ_FALSE;
+
+ /* Was decompression completed and requested? */
+ if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)))
+ {
+ /* Make sure the entire file was decompressed, and check its CRC. */
+ if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size)
+ {
+ mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE);
+ pState->status = TINFL_STATUS_FAILED;
+ }
+#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
+ else if (pState->file_crc32 != pState->file_stat.m_crc32)
+ {
+ mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED);
+ pState->status = TINFL_STATUS_FAILED;
+ }
+#endif
+ }
+
+ /* Free buffers */
+ if (!pState->pZip->m_pState->m_pMem)
+ pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf);
+ if (pState->pWrite_buf)
+ pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf);
+
+ /* Save status */
+ status = pState->status;
+
+ /* Free context */
+ pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState);
+
+ return status == TINFL_STATUS_DONE;
+}
+
+#ifndef MINIZ_NO_STDIO
+static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n)
+{
+ (void)ofs;
+
+ return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque);
+}
+
+mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags)
+{
+ mz_bool status;
+ mz_zip_archive_file_stat file_stat;
+ MZ_FILE *pFile;
+
+ if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
+ return MZ_FALSE;
+
+ if ((file_stat.m_is_directory) || (!file_stat.m_is_supported))
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE);
+
+ pFile = MZ_FOPEN(pDst_filename, "wb");
+ if (!pFile)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);
+
+ status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags);
+
+ if (MZ_FCLOSE(pFile) == EOF)
+ {
+ if (status)
+ mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED);
+
+ status = MZ_FALSE;
+ }
+
+#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO)
+ if (status)
+ mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time);
+#endif
+
+ return status;
+}
+
+mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags)
+{
+ mz_uint32 file_index;
+ if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index))
+ return MZ_FALSE;
+
+ return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags);
+}
+
+mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags)
+{
+ mz_zip_archive_file_stat file_stat;
+
+ if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
+ return MZ_FALSE;
+
+ if ((file_stat.m_is_directory) || (!file_stat.m_is_supported))
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE);
+
+ return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags);
+}
+
+mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags)
+{
+ mz_uint32 file_index;
+ if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index))
+ return MZ_FALSE;
+
+ return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags);
+}
+#endif /* #ifndef MINIZ_NO_STDIO */
+
+static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
+{
+ mz_uint32 *p = (mz_uint32 *)pOpaque;
+ (void)file_ofs;
+ *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n);
+ return n;
+}
+
+mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags)
+{
+ mz_zip_archive_file_stat file_stat;
+ mz_zip_internal_state *pState;
+ const mz_uint8 *pCentral_dir_header;
+ mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE;
+ mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE;
+ mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];
+ mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
+ mz_uint64 local_header_ofs = 0;
+ mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32;
+ mz_uint64 local_header_comp_size, local_header_uncomp_size;
+ mz_uint32 uncomp_crc32 = MZ_CRC32_INIT;
+ mz_bool has_data_descriptor;
+ mz_uint32 local_header_bit_flags;
+
+ mz_zip_array file_data_array;
+ mz_zip_array_init(&file_data_array, 1);
+
+ if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ if (file_index > pZip->m_total_files)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ pState = pZip->m_pState;
+
+ pCentral_dir_header = mz_zip_get_cdh(pZip, file_index);
+
+ if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir))
+ return MZ_FALSE;
+
+ /* A directory or zero length file */
+ if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size))
+ return MZ_TRUE;
+
+ /* Encryption and patch files are not supported. */
+ if (file_stat.m_is_encrypted)
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);
+
+ /* This function only supports stored and deflate. */
+ if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD);
+
+ if (!file_stat.m_is_supported)
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE);
+
+ /* Read and parse the local directory entry. */
+ local_header_ofs = file_stat.m_local_header_ofs;
+ if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+
+ if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS);
+ local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
+ local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS);
+ local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS);
+ local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS);
+ local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS);
+ has_data_descriptor = (local_header_bit_flags & 8) != 0;
+
+ if (local_header_filename_len != strlen(file_stat.m_filename))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ goto handle_failure;
+ }
+
+ if (local_header_filename_len)
+ {
+ if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+ goto handle_failure;
+ }
+
+ /* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation. */
+ if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
+ goto handle_failure;
+ }
+ }
+
+ if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX)))
+ {
+ mz_uint32 extra_size_remaining = local_header_extra_len;
+ const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p;
+
+ if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+ goto handle_failure;
+ }
+
+ do
+ {
+ mz_uint32 field_id, field_data_size, field_total_size;
+
+ if (extra_size_remaining < (sizeof(mz_uint16) * 2))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+ goto handle_failure;
+ }
+
+ field_id = MZ_READ_LE16(pExtra_data);
+ field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));
+ field_total_size = field_data_size + sizeof(mz_uint16) * 2;
+
+ if (field_total_size > extra_size_remaining)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+ goto handle_failure;
+ }
+
+ if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)
+ {
+ const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32);
+
+ if (field_data_size < sizeof(mz_uint64) * 2)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+ goto handle_failure;
+ }
+
+ local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data);
+ local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64));
+
+ found_zip64_ext_data_in_ldir = MZ_TRUE;
+ break;
+ }
+
+ pExtra_data += field_total_size;
+ extra_size_remaining -= field_total_size;
+ } while (extra_size_remaining);
+ }
+
+ /* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip) */
+ /* I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors */
+ if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32))
+ {
+ mz_uint8 descriptor_buf[32];
+ mz_bool has_id;
+ const mz_uint8 *pSrc;
+ mz_uint32 file_crc32;
+ mz_uint64 comp_size = 0, uncomp_size = 0;
+
+ mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4;
+
+ if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+ goto handle_failure;
+ }
+
+ has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID);
+ pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf;
+
+ file_crc32 = MZ_READ_LE32(pSrc);
+
+ if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir))
+ {
+ comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32));
+ uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64));
+ }
+ else
+ {
+ comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32));
+ uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32));
+ }
+
+ if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
+ goto handle_failure;
+ }
+ }
+ else
+ {
+ if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
+ goto handle_failure;
+ }
+ }
+
+ mz_zip_array_clear(pZip, &file_data_array);
+
+ if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0)
+ {
+ if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0))
+ return MZ_FALSE;
+
+ /* 1 more check to be sure, although the extract checks too. */
+ if (uncomp_crc32 != file_stat.m_crc32)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
+ return MZ_FALSE;
+ }
+ }
+
+ return MZ_TRUE;
+
+handle_failure:
+ mz_zip_array_clear(pZip, &file_data_array);
+ return MZ_FALSE;
+}
+
+mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags)
+{
+ mz_zip_internal_state *pState;
+ mz_uint32 i;
+
+ if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ pState = pZip->m_pState;
+
+ /* Basic sanity checks */
+ if (!pState->m_zip64)
+ {
+ if (pZip->m_total_files > MZ_UINT16_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
+
+ if (pZip->m_archive_size > MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
+ }
+ else
+ {
+ if (pState->m_central_dir.m_size >= MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
+ }
+
+ for (i = 0; i < pZip->m_total_files; i++)
+ {
+ if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags)
+ {
+ mz_uint32 found_index;
+ mz_zip_archive_file_stat stat;
+
+ if (!mz_zip_reader_file_stat(pZip, i, &stat))
+ return MZ_FALSE;
+
+ if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index))
+ return MZ_FALSE;
+
+ /* This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user) */
+ if (found_index != i)
+ return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
+ }
+
+ if (!mz_zip_validate_file(pZip, i, flags))
+ return MZ_FALSE;
+ }
+
+ return MZ_TRUE;
+}
+
+mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr)
+{
+ mz_bool success = MZ_TRUE;
+ mz_zip_archive zip;
+ mz_zip_error actual_err = MZ_ZIP_NO_ERROR;
+
+ if ((!pMem) || (!size))
+ {
+ if (pErr)
+ *pErr = MZ_ZIP_INVALID_PARAMETER;
+ return MZ_FALSE;
+ }
+
+ mz_zip_zero_struct(&zip);
+
+ if (!mz_zip_reader_init_mem(&zip, pMem, size, flags))
+ {
+ if (pErr)
+ *pErr = zip.m_last_error;
+ return MZ_FALSE;
+ }
+
+ if (!mz_zip_validate_archive(&zip, flags))
+ {
+ actual_err = zip.m_last_error;
+ success = MZ_FALSE;
+ }
+
+ if (!mz_zip_reader_end_internal(&zip, success))
+ {
+ if (!actual_err)
+ actual_err = zip.m_last_error;
+ success = MZ_FALSE;
+ }
+
+ if (pErr)
+ *pErr = actual_err;
+
+ return success;
+}
+
+#ifndef MINIZ_NO_STDIO
+mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr)
+{
+ mz_bool success = MZ_TRUE;
+ mz_zip_archive zip;
+ mz_zip_error actual_err = MZ_ZIP_NO_ERROR;
+
+ if (!pFilename)
+ {
+ if (pErr)
+ *pErr = MZ_ZIP_INVALID_PARAMETER;
+ return MZ_FALSE;
+ }
+
+ mz_zip_zero_struct(&zip);
+
+ if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0))
+ {
+ if (pErr)
+ *pErr = zip.m_last_error;
+ return MZ_FALSE;
+ }
+
+ if (!mz_zip_validate_archive(&zip, flags))
+ {
+ actual_err = zip.m_last_error;
+ success = MZ_FALSE;
+ }
+
+ if (!mz_zip_reader_end_internal(&zip, success))
+ {
+ if (!actual_err)
+ actual_err = zip.m_last_error;
+ success = MZ_FALSE;
+ }
+
+ if (pErr)
+ *pErr = actual_err;
+
+ return success;
+}
+#endif /* #ifndef MINIZ_NO_STDIO */
+
+/* ------------------- .ZIP archive writing */
+
+#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
+
+static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v)
+{
+ p[0] = (mz_uint8)v;
+ p[1] = (mz_uint8)(v >> 8);
+}
+static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v)
+{
+ p[0] = (mz_uint8)v;
+ p[1] = (mz_uint8)(v >> 8);
+ p[2] = (mz_uint8)(v >> 16);
+ p[3] = (mz_uint8)(v >> 24);
+}
+static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v)
+{
+ mz_write_le32(p, (mz_uint32)v);
+ mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32));
+}
+
+#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v))
+#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v))
+#define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v))
+
+static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
+{
+ mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
+ mz_zip_internal_state *pState = pZip->m_pState;
+ mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size);
+
+ if (!n)
+ return 0;
+
+ /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */
+ if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE);
+ return 0;
+ }
+
+ if (new_size > pState->m_mem_capacity)
+ {
+ void *pNew_block;
+ size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity);
+
+ while (new_capacity < new_size)
+ new_capacity *= 2;
+
+ if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity)))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ return 0;
+ }
+
+ pState->m_pMem = pNew_block;
+ pState->m_mem_capacity = new_capacity;
+ }
+ memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n);
+ pState->m_mem_size = (size_t)new_size;
+ return n;
+}
+
+static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error)
+{
+ mz_zip_internal_state *pState;
+ mz_bool status = MZ_TRUE;
+
+ if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)))
+ {
+ if (set_last_error)
+ mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+ return MZ_FALSE;
+ }
+
+ pState = pZip->m_pState;
+ pZip->m_pState = NULL;
+ mz_zip_array_clear(pZip, &pState->m_central_dir);
+ mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
+ mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);
+
+#ifndef MINIZ_NO_STDIO
+ if (pState->m_pFile)
+ {
+ if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE)
+ {
+ if (MZ_FCLOSE(pState->m_pFile) == EOF)
+ {
+ if (set_last_error)
+ mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED);
+ status = MZ_FALSE;
+ }
+ }
+
+ pState->m_pFile = NULL;
+ }
+#endif /* #ifndef MINIZ_NO_STDIO */
+
+ if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem))
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem);
+ pState->m_pMem = NULL;
+ }
+
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
+ pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
+ return status;
+}
+
+mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags)
+{
+ mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0;
+
+ if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING)
+ {
+ if (!pZip->m_pRead)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+ }
+
+ if (pZip->m_file_offset_alignment)
+ {
+ /* Ensure user specified file offset alignment is a power of 2. */
+ if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+ }
+
+ if (!pZip->m_pAlloc)
+ pZip->m_pAlloc = miniz_def_alloc_func;
+ if (!pZip->m_pFree)
+ pZip->m_pFree = miniz_def_free_func;
+ if (!pZip->m_pRealloc)
+ pZip->m_pRealloc = miniz_def_realloc_func;
+
+ pZip->m_archive_size = existing_size;
+ pZip->m_central_directory_file_ofs = 0;
+ pZip->m_total_files = 0;
+
+ if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+
+ memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));
+
+ MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));
+ MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));
+ MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));
+
+ pZip->m_pState->m_zip64 = zip64;
+ pZip->m_pState->m_zip64_has_extended_info_fields = zip64;
+
+ pZip->m_zip_type = MZ_ZIP_TYPE_USER;
+ pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
+
+ return MZ_TRUE;
+}
+
+mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size)
+{
+ return mz_zip_writer_init_v2(pZip, existing_size, 0);
+}
+
+mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags)
+{
+ pZip->m_pWrite = mz_zip_heap_write_func;
+ pZip->m_pNeeds_keepalive = NULL;
+
+ if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING)
+ pZip->m_pRead = mz_zip_mem_read_func;
+
+ pZip->m_pIO_opaque = pZip;
+
+ if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags))
+ return MZ_FALSE;
+
+ pZip->m_zip_type = MZ_ZIP_TYPE_HEAP;
+
+ if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning)))
+ {
+ if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size)))
+ {
+ mz_zip_writer_end_internal(pZip, MZ_FALSE);
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ }
+ pZip->m_pState->m_mem_capacity = initial_allocation_size;
+ }
+
+ return MZ_TRUE;
+}
+
+mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size)
+{
+ return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0);
+}
+
+#ifndef MINIZ_NO_STDIO
+static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
+{
+ mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
+ mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
+
+ file_ofs += pZip->m_pState->m_file_archive_start_ofs;
+
+ if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED);
+ return 0;
+ }
+
+ return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile);
+}
+
+mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning)
+{
+ return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0);
+}
+
+mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags)
+{
+ MZ_FILE *pFile;
+
+ pZip->m_pWrite = mz_zip_file_write_func;
+ pZip->m_pNeeds_keepalive = NULL;
+
+ if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING)
+ pZip->m_pRead = mz_zip_file_read_func;
+
+ pZip->m_pIO_opaque = pZip;
+
+ if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags))
+ return MZ_FALSE;
+
+ if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb")))
+ {
+ mz_zip_writer_end(pZip);
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);
+ }
+
+ pZip->m_pState->m_pFile = pFile;
+ pZip->m_zip_type = MZ_ZIP_TYPE_FILE;
+
+ if (size_to_reserve_at_beginning)
+ {
+ mz_uint64 cur_ofs = 0;
+ char buf[4096];
+
+ MZ_CLEAR_ARR(buf);
+
+ do
+ {
+ size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning);
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n)
+ {
+ mz_zip_writer_end(pZip);
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+ }
+ cur_ofs += n;
+ size_to_reserve_at_beginning -= n;
+ } while (size_to_reserve_at_beginning);
+ }
+
+ return MZ_TRUE;
+}
+
+mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags)
+{
+ pZip->m_pWrite = mz_zip_file_write_func;
+ pZip->m_pNeeds_keepalive = NULL;
+
+ if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING)
+ pZip->m_pRead = mz_zip_file_read_func;
+
+ pZip->m_pIO_opaque = pZip;
+
+ if (!mz_zip_writer_init_v2(pZip, 0, flags))
+ return MZ_FALSE;
+
+ pZip->m_pState->m_pFile = pFile;
+ pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
+ pZip->m_zip_type = MZ_ZIP_TYPE_CFILE;
+
+ return MZ_TRUE;
+}
+#endif /* #ifndef MINIZ_NO_STDIO */
+
+mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags)
+{
+ mz_zip_internal_state *pState;
+
+ if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ if (flags & MZ_ZIP_FLAG_WRITE_ZIP64)
+ {
+ /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */
+ if (!pZip->m_pState->m_zip64)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+ }
+
+ /* No sense in trying to write to an archive that's already at the support max size */
+ if (pZip->m_pState->m_zip64)
+ {
+ if (pZip->m_total_files == MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
+ }
+ else
+ {
+ if (pZip->m_total_files == MZ_UINT16_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
+
+ if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE);
+ }
+
+ pState = pZip->m_pState;
+
+ if (pState->m_pFile)
+ {
+#ifdef MINIZ_NO_STDIO
+ (void)pFilename;
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+#else
+ if (pZip->m_pIO_opaque != pZip)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE)
+ {
+ if (!pFilename)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */
+ if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile)))
+ {
+ /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */
+ mz_zip_reader_end_internal(pZip, MZ_FALSE);
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);
+ }
+ }
+
+ pZip->m_pWrite = mz_zip_file_write_func;
+ pZip->m_pNeeds_keepalive = NULL;
+#endif /* #ifdef MINIZ_NO_STDIO */
+ }
+ else if (pState->m_pMem)
+ {
+ /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */
+ if (pZip->m_pIO_opaque != pZip)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ pState->m_mem_capacity = pState->m_mem_size;
+ pZip->m_pWrite = mz_zip_heap_write_func;
+ pZip->m_pNeeds_keepalive = NULL;
+ }
+ /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */
+ else if (!pZip->m_pWrite)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ /* Start writing new files at the archive's current central directory location. */
+ /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */
+ pZip->m_archive_size = pZip->m_central_directory_file_ofs;
+ pZip->m_central_directory_file_ofs = 0;
+
+ /* Clear the sorted central dir offsets, they aren't useful or maintained now. */
+ /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */
+ /* TODO: We could easily maintain the sorted central directory offsets. */
+ mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets);
+
+ pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
+
+ return MZ_TRUE;
+}
+
+mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename)
+{
+ return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0);
+}
+
+/* TODO: pArchive_name is a terrible name here! */
+mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags)
+{
+ return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0);
+}
+
+typedef struct
+{
+ mz_zip_archive *m_pZip;
+ mz_uint64 m_cur_archive_file_ofs;
+ mz_uint64 m_comp_size;
+} mz_zip_writer_add_state;
+
+static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser)
+{
+ mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser;
+ if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len)
+ return MZ_FALSE;
+
+ pState->m_cur_archive_file_ofs += len;
+ pState->m_comp_size += len;
+ return MZ_TRUE;
+}
+
+#define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2)
+#define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3)
+static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs)
+{
+ mz_uint8 *pDst = pBuf;
+ mz_uint32 field_size = 0;
+
+ MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID);
+ MZ_WRITE_LE16(pDst + 2, 0);
+ pDst += sizeof(mz_uint16) * 2;
+
+ if (pUncomp_size)
+ {
+ MZ_WRITE_LE64(pDst, *pUncomp_size);
+ pDst += sizeof(mz_uint64);
+ field_size += sizeof(mz_uint64);
+ }
+
+ if (pComp_size)
+ {
+ MZ_WRITE_LE64(pDst, *pComp_size);
+ pDst += sizeof(mz_uint64);
+ field_size += sizeof(mz_uint64);
+ }
+
+ if (pLocal_header_ofs)
+ {
+ MZ_WRITE_LE64(pDst, *pLocal_header_ofs);
+ pDst += sizeof(mz_uint64);
+ field_size += sizeof(mz_uint64);
+ }
+
+ MZ_WRITE_LE16(pBuf + 2, field_size);
+
+ return (mz_uint32)(pDst - pBuf);
+}
+
+static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date)
+{
+ (void)pZip;
+ memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE);
+ MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG);
+ MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0);
+ MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags);
+ MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method);
+ MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time);
+ MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date);
+ MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32);
+ MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX));
+ MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX));
+ MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size);
+ MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size);
+ return MZ_TRUE;
+}
+
+static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst,
+ mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size,
+ mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32,
+ mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date,
+ mz_uint64 local_header_ofs, mz_uint32 ext_attributes)
+{
+ (void)pZip;
+ memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
+ MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG);
+ MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0);
+ MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags);
+ MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method);
+ MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time);
+ MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date);
+ MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32);
+ MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX));
+ MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX));
+ MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size);
+ MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size);
+ MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size);
+ MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes);
+ MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX));
+ return MZ_TRUE;
+}
+
+static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size,
+ const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size,
+ mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32,
+ mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date,
+ mz_uint64 local_header_ofs, mz_uint32 ext_attributes,
+ const char *user_extra_data, mz_uint user_extra_data_len)
+{
+ mz_zip_internal_state *pState = pZip->m_pState;
+ mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size;
+ size_t orig_central_dir_size = pState->m_central_dir.m_size;
+ mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];
+
+ if (!pZip->m_pState->m_zip64)
+ {
+ if (local_header_ofs > 0xFFFFFFFF)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE);
+ }
+
+ /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */
+ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);
+
+ if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, (mz_uint16)(extra_size + user_extra_data_len), comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes))
+ return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
+
+ if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) ||
+ (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) ||
+ (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) ||
+ (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) ||
+ (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) ||
+ (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1)))
+ {
+ /* Try to resize the central directory array back into its original state. */
+ mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ }
+
+ return MZ_TRUE;
+}
+
+static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name)
+{
+ /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */
+ if (*pArchive_name == '/')
+ return MZ_FALSE;
+
+ /* Making sure the name does not contain drive letters or DOS style backward slashes is the responsibility of the program using miniz*/
+
+ return MZ_TRUE;
+}
+
+static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip)
+{
+ mz_uint32 n;
+ if (!pZip->m_file_offset_alignment)
+ return 0;
+ n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1));
+ return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1));
+}
+
+static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n)
+{
+ char buf[4096];
+ memset(buf, 0, MZ_MIN(sizeof(buf), n));
+ while (n)
+ {
+ mz_uint32 s = MZ_MIN(sizeof(buf), n);
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+
+ cur_file_ofs += s;
+ n -= s;
+ }
+ return MZ_TRUE;
+}
+
+mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,
+ mz_uint64 uncomp_size, mz_uint32 uncomp_crc32)
+{
+ return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0);
+}
+
+mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size,
+ mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified,
+ const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len)
+{
+ mz_uint16 method = 0, dos_time = 0, dos_date = 0;
+ mz_uint level, ext_attributes = 0, num_alignment_padding_bytes;
+ mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0;
+ size_t archive_name_size;
+ mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
+ tdefl_compressor *pComp = NULL;
+ mz_bool store_data_uncompressed;
+ mz_zip_internal_state *pState;
+ mz_uint8 *pExtra_data = NULL;
+ mz_uint32 extra_size = 0;
+ mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE];
+ mz_uint16 bit_flags = 0;
+
+ if ((int)level_and_flags < 0)
+ level_and_flags = MZ_DEFAULT_LEVEL;
+
+ if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)))
+ bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR;
+
+ if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME))
+ bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8;
+
+ level = level_and_flags & 0xF;
+ store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA));
+
+ if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ pState = pZip->m_pState;
+
+ if (pState->m_zip64)
+ {
+ if (pZip->m_total_files == MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
+ }
+ else
+ {
+ if (pZip->m_total_files == MZ_UINT16_MAX)
+ {
+ pState->m_zip64 = MZ_TRUE;
+ /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */
+ }
+ if (((mz_uint64)buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF))
+ {
+ pState->m_zip64 = MZ_TRUE;
+ /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */
+ }
+ }
+
+ if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ if (!mz_zip_writer_validate_archive_name(pArchive_name))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME);
+
+#ifndef MINIZ_NO_TIME
+ if (last_modified != NULL)
+ {
+ mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date);
+ }
+ else
+ {
+ MZ_TIME_T cur_time;
+ time(&cur_time);
+ mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date);
+ }
+#endif /* #ifndef MINIZ_NO_TIME */
+
+ if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
+ {
+ uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size);
+ uncomp_size = buf_size;
+ if (uncomp_size <= 3)
+ {
+ level = 0;
+ store_data_uncompressed = MZ_TRUE;
+ }
+ }
+
+ archive_name_size = strlen(pArchive_name);
+ if (archive_name_size > MZ_UINT16_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME);
+
+ num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
+
+ /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */
+ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);
+
+ if (!pState->m_zip64)
+ {
+ /* Bail early if the archive would obviously become too large */
+ if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size
+ + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len +
+ pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len
+ + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF)
+ {
+ pState->m_zip64 = MZ_TRUE;
+ /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */
+ }
+ }
+
+ if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/'))
+ {
+ /* Set DOS Subdirectory attribute bit. */
+ ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG;
+
+ /* Subdirectories cannot contain data. */
+ if ((buf_size) || (uncomp_size))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+ }
+
+ /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */
+ if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1)))
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+
+ if ((!store_data_uncompressed) && (buf_size))
+ {
+ if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor))))
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ }
+
+ if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes))
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
+ return MZ_FALSE;
+ }
+
+ local_dir_header_ofs += num_alignment_padding_bytes;
+ if (pZip->m_file_offset_alignment)
+ {
+ MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0);
+ }
+ cur_archive_file_ofs += num_alignment_padding_bytes;
+
+ MZ_CLEAR_ARR(local_dir_header);
+
+ if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
+ {
+ method = MZ_DEFLATED;
+ }
+
+ if (pState->m_zip64)
+ {
+ if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX)
+ {
+ pExtra_data = extra_data;
+ extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL,
+ (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);
+ }
+
+ if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, bit_flags, dos_time, dos_date))
+ return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
+
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+
+ cur_archive_file_ofs += sizeof(local_dir_header);
+
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+ }
+ cur_archive_file_ofs += archive_name_size;
+
+ if (pExtra_data != NULL)
+ {
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+
+ cur_archive_file_ofs += extra_size;
+ }
+ }
+ else
+ {
+ if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX))
+ return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
+ if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date))
+ return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
+
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+
+ cur_archive_file_ofs += sizeof(local_dir_header);
+
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+ }
+ cur_archive_file_ofs += archive_name_size;
+ }
+
+ if (user_extra_data_len > 0)
+ {
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+
+ cur_archive_file_ofs += user_extra_data_len;
+ }
+
+ if (store_data_uncompressed)
+ {
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size)
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+ }
+
+ cur_archive_file_ofs += buf_size;
+ comp_size = buf_size;
+ }
+ else if (buf_size)
+ {
+ mz_zip_writer_add_state state;
+
+ state.m_pZip = pZip;
+ state.m_cur_archive_file_ofs = cur_archive_file_ofs;
+ state.m_comp_size = 0;
+
+ if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) ||
+ (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE))
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
+ return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED);
+ }
+
+ comp_size = state.m_comp_size;
+ cur_archive_file_ofs = state.m_cur_archive_file_ofs;
+ }
+
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
+ pComp = NULL;
+
+ if (uncomp_size)
+ {
+ mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64];
+ mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32;
+
+ MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR);
+
+ MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID);
+ MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32);
+ if (pExtra_data == NULL)
+ {
+ if (comp_size > MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
+
+ MZ_WRITE_LE32(local_dir_footer + 8, comp_size);
+ MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size);
+ }
+ else
+ {
+ MZ_WRITE_LE64(local_dir_footer + 8, comp_size);
+ MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size);
+ local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64;
+ }
+
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size)
+ return MZ_FALSE;
+
+ cur_archive_file_ofs += local_dir_footer_size;
+ }
+
+ if (pExtra_data != NULL)
+ {
+ extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL,
+ (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);
+ }
+
+ if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment,
+ comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes,
+ user_extra_data_central, user_extra_data_central_len))
+ return MZ_FALSE;
+
+ pZip->m_total_files++;
+ pZip->m_archive_size = cur_archive_file_ofs;
+
+ return MZ_TRUE;
+}
+
+mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,
+ const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len)
+{
+ mz_uint16 gen_flags;
+ mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes;
+ mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0;
+ mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0;
+ size_t archive_name_size;
+ mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
+ mz_uint8 *pExtra_data = NULL;
+ mz_uint32 extra_size = 0;
+ mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE];
+ mz_zip_internal_state *pState;
+ mz_uint64 file_ofs = 0, cur_archive_header_file_ofs;
+
+ if ((int)level_and_flags < 0)
+ level_and_flags = MZ_DEFAULT_LEVEL;
+ level = level_and_flags & 0xF;
+
+ gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR;
+
+ if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME))
+ gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8;
+
+ /* Sanity checks */
+ if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ pState = pZip->m_pState;
+
+ if ((!pState->m_zip64) && (max_size > MZ_UINT32_MAX))
+ {
+ /* Source file is too large for non-zip64 */
+ /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */
+ pState->m_zip64 = MZ_TRUE;
+ }
+
+ /* We could support this, but why? */
+ if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ if (!mz_zip_writer_validate_archive_name(pArchive_name))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME);
+
+ if (pState->m_zip64)
+ {
+ if (pZip->m_total_files == MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
+ }
+ else
+ {
+ if (pZip->m_total_files == MZ_UINT16_MAX)
+ {
+ pState->m_zip64 = MZ_TRUE;
+ /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */
+ }
+ }
+
+ archive_name_size = strlen(pArchive_name);
+ if (archive_name_size > MZ_UINT16_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME);
+
+ num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
+
+ /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */
+ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);
+
+ if (!pState->m_zip64)
+ {
+ /* Bail early if the archive would obviously become too large */
+ if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE
+ + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024
+ + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF)
+ {
+ pState->m_zip64 = MZ_TRUE;
+ /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */
+ }
+ }
+
+#ifndef MINIZ_NO_TIME
+ if (pFile_time)
+ {
+ mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date);
+ }
+#endif
+
+ if (max_size <= 3)
+ level = 0;
+
+ if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes))
+ {
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+ }
+
+ cur_archive_file_ofs += num_alignment_padding_bytes;
+ local_dir_header_ofs = cur_archive_file_ofs;
+
+ if (pZip->m_file_offset_alignment)
+ {
+ MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0);
+ }
+
+ if (max_size && level)
+ {
+ method = MZ_DEFLATED;
+ }
+
+ MZ_CLEAR_ARR(local_dir_header);
+ if (pState->m_zip64)
+ {
+ if (max_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX)
+ {
+ pExtra_data = extra_data;
+ if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE)
+ extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL,
+ (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL,
+ (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);
+ else
+ extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, NULL,
+ NULL,
+ (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);
+ }
+
+ if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, gen_flags, dos_time, dos_date))
+ return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
+
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+
+ cur_archive_file_ofs += sizeof(local_dir_header);
+
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
+ {
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+ }
+
+ cur_archive_file_ofs += archive_name_size;
+
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+
+ cur_archive_file_ofs += extra_size;
+ }
+ else
+ {
+ if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX))
+ return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
+ if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date))
+ return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
+
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+
+ cur_archive_file_ofs += sizeof(local_dir_header);
+
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
+ {
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+ }
+
+ cur_archive_file_ofs += archive_name_size;
+ }
+
+ if (user_extra_data_len > 0)
+ {
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+
+ cur_archive_file_ofs += user_extra_data_len;
+ }
+
+ if (max_size)
+ {
+ void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE);
+ if (!pRead_buf)
+ {
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ }
+
+ if (!level)
+ {
+ while (1)
+ {
+ size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE);
+ if (n == 0)
+ break;
+
+ if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size))
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+ }
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+ }
+ file_ofs += n;
+ uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n);
+ cur_archive_file_ofs += n;
+ }
+ uncomp_size = file_ofs;
+ comp_size = uncomp_size;
+ }
+ else
+ {
+ mz_bool result = MZ_FALSE;
+ mz_zip_writer_add_state state;
+ tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor));
+ if (!pComp)
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ }
+
+ state.m_pZip = pZip;
+ state.m_cur_archive_file_ofs = cur_archive_file_ofs;
+ state.m_comp_size = 0;
+
+ if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY)
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
+ return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
+ }
+
+ for (;;)
+ {
+ tdefl_status status;
+ tdefl_flush flush = TDEFL_NO_FLUSH;
+
+ size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE);
+ if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size))
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+ break;
+ }
+
+ file_ofs += n;
+ uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n);
+
+ if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque))
+ flush = TDEFL_FULL_FLUSH;
+
+ if (n == 0)
+ flush = TDEFL_FINISH;
+
+ status = tdefl_compress_buffer(pComp, pRead_buf, n, flush);
+ if (status == TDEFL_STATUS_DONE)
+ {
+ result = MZ_TRUE;
+ break;
+ }
+ else if (status != TDEFL_STATUS_OKAY)
+ {
+ mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED);
+ break;
+ }
+ }
+
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
+
+ if (!result)
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
+ return MZ_FALSE;
+ }
+
+ uncomp_size = file_ofs;
+ comp_size = state.m_comp_size;
+ cur_archive_file_ofs = state.m_cur_archive_file_ofs;
+ }
+
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
+ }
+
+ if (!(level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE))
+ {
+ mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64];
+ mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32;
+
+ MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID);
+ MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32);
+ if (pExtra_data == NULL)
+ {
+ if (comp_size > MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
+
+ MZ_WRITE_LE32(local_dir_footer + 8, comp_size);
+ MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size);
+ }
+ else
+ {
+ MZ_WRITE_LE64(local_dir_footer + 8, comp_size);
+ MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size);
+ local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64;
+ }
+
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size)
+ return MZ_FALSE;
+
+ cur_archive_file_ofs += local_dir_footer_size;
+ }
+
+ if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE)
+ {
+ if (pExtra_data != NULL)
+ {
+ extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL,
+ (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);
+ }
+
+ if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header,
+ (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len),
+ (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : uncomp_size,
+ (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : comp_size,
+ uncomp_crc32, method, gen_flags, dos_time, dos_date))
+ return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
+
+ cur_archive_header_file_ofs = local_dir_header_ofs;
+
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+
+ if (pExtra_data != NULL)
+ {
+ cur_archive_header_file_ofs += sizeof(local_dir_header);
+
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
+ {
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+ }
+
+ cur_archive_header_file_ofs += archive_name_size;
+
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, extra_data, extra_size) != extra_size)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+
+ cur_archive_header_file_ofs += extra_size;
+ }
+ }
+
+ if (pExtra_data != NULL)
+ {
+ extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL,
+ (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);
+ }
+
+ if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, comment_size,
+ uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes,
+ user_extra_data_central, user_extra_data_central_len))
+ return MZ_FALSE;
+
+ pZip->m_total_files++;
+ pZip->m_archive_size = cur_archive_file_ofs;
+
+ return MZ_TRUE;
+}
+
+#ifndef MINIZ_NO_STDIO
+
+static size_t mz_file_read_func_stdio(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
+{
+ MZ_FILE *pSrc_file = (MZ_FILE *)pOpaque;
+ mz_int64 cur_ofs = MZ_FTELL64(pSrc_file);
+
+ if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pSrc_file, (mz_int64)file_ofs, SEEK_SET))))
+ return 0;
+
+ return MZ_FREAD(pBuf, 1, n, pSrc_file);
+}
+
+mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,
+ const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len)
+{
+ return mz_zip_writer_add_read_buf_callback(pZip, pArchive_name, mz_file_read_func_stdio, pSrc_file, max_size, pFile_time, pComment, comment_size, level_and_flags,
+ user_extra_data, user_extra_data_len, user_extra_data_central, user_extra_data_central_len);
+}
+
+mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)
+{
+ MZ_FILE *pSrc_file = NULL;
+ mz_uint64 uncomp_size = 0;
+ MZ_TIME_T file_modified_time;
+ MZ_TIME_T *pFile_time = NULL;
+ mz_bool status;
+
+ memset(&file_modified_time, 0, sizeof(file_modified_time));
+
+#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO)
+ pFile_time = &file_modified_time;
+ if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time))
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED);
+#endif
+
+ pSrc_file = MZ_FOPEN(pSrc_filename, "rb");
+ if (!pSrc_file)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);
+
+ MZ_FSEEK64(pSrc_file, 0, SEEK_END);
+ uncomp_size = MZ_FTELL64(pSrc_file);
+ MZ_FSEEK64(pSrc_file, 0, SEEK_SET);
+
+ status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0);
+
+ MZ_FCLOSE(pSrc_file);
+
+ return status;
+}
+#endif /* #ifndef MINIZ_NO_STDIO */
+
+static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, mz_uint32 ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start)
+{
+ /* + 64 should be enough for any new zip64 data */
+ if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE))
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+
+ mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE);
+
+ if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start))
+ {
+ mz_uint8 new_ext_block[64];
+ mz_uint8 *pDst = new_ext_block;
+ mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID);
+ mz_write_le16(pDst + sizeof(mz_uint16), 0);
+ pDst += sizeof(mz_uint16) * 2;
+
+ if (pUncomp_size)
+ {
+ mz_write_le64(pDst, *pUncomp_size);
+ pDst += sizeof(mz_uint64);
+ }
+
+ if (pComp_size)
+ {
+ mz_write_le64(pDst, *pComp_size);
+ pDst += sizeof(mz_uint64);
+ }
+
+ if (pLocal_header_ofs)
+ {
+ mz_write_le64(pDst, *pLocal_header_ofs);
+ pDst += sizeof(mz_uint64);
+ }
+
+ if (pDisk_start)
+ {
+ mz_write_le32(pDst, *pDisk_start);
+ pDst += sizeof(mz_uint32);
+ }
+
+ mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2));
+
+ if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block))
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ }
+
+ if ((pExt) && (ext_len))
+ {
+ mz_uint32 extra_size_remaining = ext_len;
+ const mz_uint8 *pExtra_data = pExt;
+
+ do
+ {
+ mz_uint32 field_id, field_data_size, field_total_size;
+
+ if (extra_size_remaining < (sizeof(mz_uint16) * 2))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ field_id = MZ_READ_LE16(pExtra_data);
+ field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));
+ field_total_size = field_data_size + sizeof(mz_uint16) * 2;
+
+ if (field_total_size > extra_size_remaining)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)
+ {
+ if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size))
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ }
+
+ pExtra_data += field_total_size;
+ extra_size_remaining -= field_total_size;
+ } while (extra_size_remaining);
+ }
+
+ return MZ_TRUE;
+}
+
+/* TODO: This func is now pretty freakin complex due to zip64, split it up? */
+mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index)
+{
+ mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size;
+ mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs;
+ mz_uint64 cur_src_file_ofs, cur_dst_file_ofs;
+ mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];
+ mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
+ mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];
+ size_t orig_central_dir_size;
+ mz_zip_internal_state *pState;
+ void *pBuf;
+ const mz_uint8 *pSrc_central_header;
+ mz_zip_archive_file_stat src_file_stat;
+ mz_uint32 src_filename_len, src_comment_len, src_ext_len;
+ mz_uint32 local_header_filename_size, local_header_extra_len;
+ mz_uint64 local_header_comp_size, local_header_uncomp_size;
+ mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE;
+
+ /* Sanity checks */
+ if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ pState = pZip->m_pState;
+
+ /* Don't support copying files from zip64 archives to non-zip64, even though in some cases this is possible */
+ if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ /* Get pointer to the source central dir header and crack it */
+ if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index)))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS);
+ src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS);
+ src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS);
+ src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len;
+
+ /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case we need to add more extra data) */
+ if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);
+
+ num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
+
+ if (!pState->m_zip64)
+ {
+ if (pZip->m_total_files == MZ_UINT16_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
+ }
+ else
+ {
+ /* TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing. */
+ if (pZip->m_total_files == MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
+ }
+
+ if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL))
+ return MZ_FALSE;
+
+ cur_src_file_ofs = src_file_stat.m_local_header_ofs;
+ cur_dst_file_ofs = pZip->m_archive_size;
+
+ /* Read the source archive's local dir header */
+ if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+
+ if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+ cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;
+
+ /* Compute the total size we need to copy (filename+extra data+compressed data) */
+ local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS);
+ local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
+ local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS);
+ local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS);
+ src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size;
+
+ /* Try to find a zip64 extended information field */
+ if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX)))
+ {
+ mz_zip_array file_data_array;
+ const mz_uint8 *pExtra_data;
+ mz_uint32 extra_size_remaining = local_header_extra_len;
+
+ mz_zip_array_init(&file_data_array, 1);
+ if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE))
+ {
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ }
+
+ if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len)
+ {
+ mz_zip_array_clear(pZip, &file_data_array);
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+ }
+
+ pExtra_data = (const mz_uint8 *)file_data_array.m_p;
+
+ do
+ {
+ mz_uint32 field_id, field_data_size, field_total_size;
+
+ if (extra_size_remaining < (sizeof(mz_uint16) * 2))
+ {
+ mz_zip_array_clear(pZip, &file_data_array);
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+ }
+
+ field_id = MZ_READ_LE16(pExtra_data);
+ field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));
+ field_total_size = field_data_size + sizeof(mz_uint16) * 2;
+
+ if (field_total_size > extra_size_remaining)
+ {
+ mz_zip_array_clear(pZip, &file_data_array);
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+ }
+
+ if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)
+ {
+ const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32);
+
+ if (field_data_size < sizeof(mz_uint64) * 2)
+ {
+ mz_zip_array_clear(pZip, &file_data_array);
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+ }
+
+ local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data);
+ local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */
+
+ found_zip64_ext_data_in_ldir = MZ_TRUE;
+ break;
+ }
+
+ pExtra_data += field_total_size;
+ extra_size_remaining -= field_total_size;
+ } while (extra_size_remaining);
+
+ mz_zip_array_clear(pZip, &file_data_array);
+ }
+
+ if (!pState->m_zip64)
+ {
+ /* Try to detect if the new archive will most likely wind up too big and bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a fudge factor). */
+ /* We also check when the archive is finalized so this doesn't need to be perfect. */
+ mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) +
+ pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64;
+
+ if (approx_new_archive_size >= MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
+ }
+
+ /* Write dest archive padding */
+ if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes))
+ return MZ_FALSE;
+
+ cur_dst_file_ofs += num_alignment_padding_bytes;
+
+ local_dir_header_ofs = cur_dst_file_ofs;
+ if (pZip->m_file_offset_alignment)
+ {
+ MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0);
+ }
+
+ /* The original zip's local header+ext block doesn't change, even with zip64, so we can just copy it over to the dest zip */
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+
+ cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;
+
+ /* Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to handle optional data descriptor */
+ if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining)))))
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+
+ while (src_archive_bytes_remaining)
+ {
+ n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining);
+ if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n)
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+ }
+ cur_src_file_ofs += n;
+
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+ }
+ cur_dst_file_ofs += n;
+
+ src_archive_bytes_remaining -= n;
+ }
+
+ /* Now deal with the optional data descriptor */
+ bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS);
+ if (bit_flags & 8)
+ {
+ /* Copy data descriptor */
+ if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir))
+ {
+ /* src is zip64, dest must be zip64 */
+
+ /* name uint32_t's */
+ /* id 1 (optional in zip64?) */
+ /* crc 1 */
+ /* comp_size 2 */
+ /* uncomp_size 2 */
+ if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6))
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+ }
+
+ n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5);
+ }
+ else
+ {
+ /* src is NOT zip64 */
+ mz_bool has_id;
+
+ if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4)
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+ }
+
+ has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID);
+
+ if (pZip->m_pState->m_zip64)
+ {
+ /* dest is zip64, so upgrade the data descriptor */
+ const mz_uint8 *pSrc_descriptor = (const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0);
+ const mz_uint32 src_crc32 = MZ_READ_LE32(pSrc_descriptor);
+ const mz_uint64 src_comp_size = MZ_READ_LE32(pSrc_descriptor + sizeof(mz_uint32));
+ const mz_uint64 src_uncomp_size = MZ_READ_LE32(pSrc_descriptor + 2*sizeof(mz_uint32));
+
+ mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID);
+ mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32);
+ mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size);
+ mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size);
+
+ n = sizeof(mz_uint32) * 6;
+ }
+ else
+ {
+ /* dest is NOT zip64, just copy it as-is */
+ n = sizeof(mz_uint32) * (has_id ? 4 : 3);
+ }
+ }
+
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
+ {
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+ }
+
+ cur_src_file_ofs += n;
+ cur_dst_file_ofs += n;
+ }
+ pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
+
+ /* Finally, add the new central dir header */
+ orig_central_dir_size = pState->m_central_dir.m_size;
+
+ memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
+
+ if (pState->m_zip64)
+ {
+ /* This is the painful part: We need to write a new central dir header + ext block with updated zip64 fields, and ensure the old fields (if any) are not included. */
+ const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len;
+ mz_zip_array new_ext_block;
+
+ mz_zip_array_init(&new_ext_block, sizeof(mz_uint8));
+
+ MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX);
+ MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX);
+ MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX);
+
+ if (!mz_zip_writer_update_zip64_extension_block(&new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL))
+ {
+ mz_zip_array_clear(pZip, &new_ext_block);
+ return MZ_FALSE;
+ }
+
+ MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size);
+
+ if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE))
+ {
+ mz_zip_array_clear(pZip, &new_ext_block);
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ }
+
+ if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len))
+ {
+ mz_zip_array_clear(pZip, &new_ext_block);
+ mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ }
+
+ if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size))
+ {
+ mz_zip_array_clear(pZip, &new_ext_block);
+ mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ }
+
+ if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len))
+ {
+ mz_zip_array_clear(pZip, &new_ext_block);
+ mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ }
+
+ mz_zip_array_clear(pZip, &new_ext_block);
+ }
+ else
+ {
+ /* sanity checks */
+ if (cur_dst_file_ofs > MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
+
+ if (local_dir_header_ofs >= MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
+
+ MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs);
+
+ if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE))
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+
+ if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size))
+ {
+ mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ }
+ }
+
+ /* This shouldn't trigger unless we screwed up during the initial sanity checks */
+ if (pState->m_central_dir.m_size >= MZ_UINT32_MAX)
+ {
+ /* TODO: Support central dirs >= 32-bits in size */
+ mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
+ return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);
+ }
+
+ n = (mz_uint32)orig_central_dir_size;
+ if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1))
+ {
+ mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
+ return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+ }
+
+ pZip->m_total_files++;
+ pZip->m_archive_size = cur_dst_file_ofs;
+
+ return MZ_TRUE;
+}
+
+mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip)
+{
+ mz_zip_internal_state *pState;
+ mz_uint64 central_dir_ofs, central_dir_size;
+ mz_uint8 hdr[256];
+
+ if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ pState = pZip->m_pState;
+
+ if (pState->m_zip64)
+ {
+ if ((mz_uint64)pState->m_central_dir.m_size >= MZ_UINT32_MAX)
+ return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
+ }
+ else
+ {
+ if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX))
+ return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
+ }
+
+ central_dir_ofs = 0;
+ central_dir_size = 0;
+ if (pZip->m_total_files)
+ {
+ /* Write central directory */
+ central_dir_ofs = pZip->m_archive_size;
+ central_dir_size = pState->m_central_dir.m_size;
+ pZip->m_central_directory_file_ofs = central_dir_ofs;
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+
+ pZip->m_archive_size += central_dir_size;
+ }
+
+ if (pState->m_zip64)
+ {
+ /* Write zip64 end of central directory header */
+ mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size;
+
+ MZ_CLEAR_ARR(hdr);
+ MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG);
+ MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64));
+ MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */
+ MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D);
+ MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files);
+ MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files);
+ MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size);
+ MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs);
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+
+ pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE;
+
+ /* Write zip64 end of central directory locator */
+ MZ_CLEAR_ARR(hdr);
+ MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG);
+ MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr);
+ MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1);
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+
+ pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE;
+ }
+
+ /* Write end of central directory record */
+ MZ_CLEAR_ARR(hdr);
+ MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG);
+ MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files));
+ MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files));
+ MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size));
+ MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs));
+
+ if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
+
+#ifndef MINIZ_NO_STDIO
+ if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF))
+ return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED);
+#endif /* #ifndef MINIZ_NO_STDIO */
+
+ pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE;
+
+ pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED;
+ return MZ_TRUE;
+}
+
+mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize)
+{
+ if ((!ppBuf) || (!pSize))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ *ppBuf = NULL;
+ *pSize = 0;
+
+ if ((!pZip) || (!pZip->m_pState))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ if (pZip->m_pWrite != mz_zip_heap_write_func)
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ if (!mz_zip_writer_finalize_archive(pZip))
+ return MZ_FALSE;
+
+ *ppBuf = pZip->m_pState->m_pMem;
+ *pSize = pZip->m_pState->m_mem_size;
+ pZip->m_pState->m_pMem = NULL;
+ pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0;
+
+ return MZ_TRUE;
+}
+
+mz_bool mz_zip_writer_end(mz_zip_archive *pZip)
+{
+ return mz_zip_writer_end_internal(pZip, MZ_TRUE);
+}
+
+#ifndef MINIZ_NO_STDIO
+mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)
+{
+ return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL);
+}
+
+mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr)
+{
+ mz_bool status, created_new_archive = MZ_FALSE;
+ mz_zip_archive zip_archive;
+ struct MZ_FILE_STAT_STRUCT file_stat;
+ mz_zip_error actual_err = MZ_ZIP_NO_ERROR;
+
+ mz_zip_zero_struct(&zip_archive);
+ if ((int)level_and_flags < 0)
+ level_and_flags = MZ_DEFAULT_LEVEL;
+
+ if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION))
+ {
+ if (pErr)
+ *pErr = MZ_ZIP_INVALID_PARAMETER;
+ return MZ_FALSE;
+ }
+
+ if (!mz_zip_writer_validate_archive_name(pArchive_name))
+ {
+ if (pErr)
+ *pErr = MZ_ZIP_INVALID_FILENAME;
+ return MZ_FALSE;
+ }
+
+ /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */
+ /* So be sure to compile with _LARGEFILE64_SOURCE 1 */
+ if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0)
+ {
+ /* Create a new archive. */
+ if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags))
+ {
+ if (pErr)
+ *pErr = zip_archive.m_last_error;
+ return MZ_FALSE;
+ }
+
+ created_new_archive = MZ_TRUE;
+ }
+ else
+ {
+ /* Append to an existing archive. */
+ if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0))
+ {
+ if (pErr)
+ *pErr = zip_archive.m_last_error;
+ return MZ_FALSE;
+ }
+
+ if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags))
+ {
+ if (pErr)
+ *pErr = zip_archive.m_last_error;
+
+ mz_zip_reader_end_internal(&zip_archive, MZ_FALSE);
+
+ return MZ_FALSE;
+ }
+ }
+
+ status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0);
+ actual_err = zip_archive.m_last_error;
+
+ /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */
+ if (!mz_zip_writer_finalize_archive(&zip_archive))
+ {
+ if (!actual_err)
+ actual_err = zip_archive.m_last_error;
+
+ status = MZ_FALSE;
+ }
+
+ if (!mz_zip_writer_end_internal(&zip_archive, status))
+ {
+ if (!actual_err)
+ actual_err = zip_archive.m_last_error;
+
+ status = MZ_FALSE;
+ }
+
+ if ((!status) && (created_new_archive))
+ {
+ /* It's a new archive and something went wrong, so just delete it. */
+ int ignoredStatus = MZ_DELETE_FILE(pZip_filename);
+ (void)ignoredStatus;
+ }
+
+ if (pErr)
+ *pErr = actual_err;
+
+ return status;
+}
+
+void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr)
+{
+ mz_uint32 file_index;
+ mz_zip_archive zip_archive;
+ void *p = NULL;
+
+ if (pSize)
+ *pSize = 0;
+
+ if ((!pZip_filename) || (!pArchive_name))
+ {
+ if (pErr)
+ *pErr = MZ_ZIP_INVALID_PARAMETER;
+
+ return NULL;
+ }
+
+ mz_zip_zero_struct(&zip_archive);
+ if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0))
+ {
+ if (pErr)
+ *pErr = zip_archive.m_last_error;
+
+ return NULL;
+ }
+
+ if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index))
+ {
+ p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags);
+ }
+
+ mz_zip_reader_end_internal(&zip_archive, p != NULL);
+
+ if (pErr)
+ *pErr = zip_archive.m_last_error;
+
+ return p;
+}
+
+void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags)
+{
+ return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL);
+}
+
+#endif /* #ifndef MINIZ_NO_STDIO */
+
+#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */
+
+/* ------------------- Misc utils */
+
+mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip)
+{
+ return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID;
+}
+
+mz_zip_type mz_zip_get_type(mz_zip_archive *pZip)
+{
+ return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID;
+}
+
+mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num)
+{
+ mz_zip_error prev_err;
+
+ if (!pZip)
+ return MZ_ZIP_INVALID_PARAMETER;
+
+ prev_err = pZip->m_last_error;
+
+ pZip->m_last_error = err_num;
+ return prev_err;
+}
+
+mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip)
+{
+ if (!pZip)
+ return MZ_ZIP_INVALID_PARAMETER;
+
+ return pZip->m_last_error;
+}
+
+mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip)
+{
+ return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR);
+}
+
+mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip)
+{
+ mz_zip_error prev_err;
+
+ if (!pZip)
+ return MZ_ZIP_INVALID_PARAMETER;
+
+ prev_err = pZip->m_last_error;
+
+ pZip->m_last_error = MZ_ZIP_NO_ERROR;
+ return prev_err;
+}
+
+const char *mz_zip_get_error_string(mz_zip_error mz_err)
+{
+ switch (mz_err)
+ {
+ case MZ_ZIP_NO_ERROR:
+ return "no error";
+ case MZ_ZIP_UNDEFINED_ERROR:
+ return "undefined error";
+ case MZ_ZIP_TOO_MANY_FILES:
+ return "too many files";
+ case MZ_ZIP_FILE_TOO_LARGE:
+ return "file too large";
+ case MZ_ZIP_UNSUPPORTED_METHOD:
+ return "unsupported method";
+ case MZ_ZIP_UNSUPPORTED_ENCRYPTION:
+ return "unsupported encryption";
+ case MZ_ZIP_UNSUPPORTED_FEATURE:
+ return "unsupported feature";
+ case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR:
+ return "failed finding central directory";
+ case MZ_ZIP_NOT_AN_ARCHIVE:
+ return "not a ZIP archive";
+ case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED:
+ return "invalid header or archive is corrupted";
+ case MZ_ZIP_UNSUPPORTED_MULTIDISK:
+ return "unsupported multidisk archive";
+ case MZ_ZIP_DECOMPRESSION_FAILED:
+ return "decompression failed or archive is corrupted";
+ case MZ_ZIP_COMPRESSION_FAILED:
+ return "compression failed";
+ case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE:
+ return "unexpected decompressed size";
+ case MZ_ZIP_CRC_CHECK_FAILED:
+ return "CRC-32 check failed";
+ case MZ_ZIP_UNSUPPORTED_CDIR_SIZE:
+ return "unsupported central directory size";
+ case MZ_ZIP_ALLOC_FAILED:
+ return "allocation failed";
+ case MZ_ZIP_FILE_OPEN_FAILED:
+ return "file open failed";
+ case MZ_ZIP_FILE_CREATE_FAILED:
+ return "file create failed";
+ case MZ_ZIP_FILE_WRITE_FAILED:
+ return "file write failed";
+ case MZ_ZIP_FILE_READ_FAILED:
+ return "file read failed";
+ case MZ_ZIP_FILE_CLOSE_FAILED:
+ return "file close failed";
+ case MZ_ZIP_FILE_SEEK_FAILED:
+ return "file seek failed";
+ case MZ_ZIP_FILE_STAT_FAILED:
+ return "file stat failed";
+ case MZ_ZIP_INVALID_PARAMETER:
+ return "invalid parameter";
+ case MZ_ZIP_INVALID_FILENAME:
+ return "invalid filename";
+ case MZ_ZIP_BUF_TOO_SMALL:
+ return "buffer too small";
+ case MZ_ZIP_INTERNAL_ERROR:
+ return "internal error";
+ case MZ_ZIP_FILE_NOT_FOUND:
+ return "file not found";
+ case MZ_ZIP_ARCHIVE_TOO_LARGE:
+ return "archive is too large";
+ case MZ_ZIP_VALIDATION_FAILED:
+ return "validation failed";
+ case MZ_ZIP_WRITE_CALLBACK_FAILED:
+ return "write callback failed";
+ case MZ_ZIP_TOTAL_ERRORS:
+ return "total errors";
+ default:
+ break;
+ }
+
+ return "unknown error";
+}
+
+/* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh. */
+mz_bool mz_zip_is_zip64(mz_zip_archive *pZip)
+{
+ if ((!pZip) || (!pZip->m_pState))
+ return MZ_FALSE;
+
+ return pZip->m_pState->m_zip64;
+}
+
+size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip)
+{
+ if ((!pZip) || (!pZip->m_pState))
+ return 0;
+
+ return pZip->m_pState->m_central_dir.m_size;
+}
+
+mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip)
+{
+ return pZip ? pZip->m_total_files : 0;
+}
+
+mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip)
+{
+ if (!pZip)
+ return 0;
+ return pZip->m_archive_size;
+}
+
+mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip)
+{
+ if ((!pZip) || (!pZip->m_pState))
+ return 0;
+ return pZip->m_pState->m_file_archive_start_ofs;
+}
+
+MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip)
+{
+ if ((!pZip) || (!pZip->m_pState))
+ return 0;
+ return pZip->m_pState->m_pFile;
+}
+
+size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n)
+{
+ if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead))
+ return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+
+ return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n);
+}
+
+mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size)
+{
+ mz_uint n;
+ const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);
+ if (!p)
+ {
+ if (filename_buf_size)
+ pFilename[0] = '\0';
+ mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
+ return 0;
+ }
+ n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
+ if (filename_buf_size)
+ {
+ n = MZ_MIN(n, filename_buf_size - 1);
+ memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n);
+ pFilename[n] = '\0';
+ }
+ return n + 1;
+}
+
+mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat)
+{
+ return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL);
+}
+
+mz_bool mz_zip_end(mz_zip_archive *pZip)
+{
+ if (!pZip)
+ return MZ_FALSE;
+
+ if (pZip->m_zip_mode == MZ_ZIP_MODE_READING)
+ return mz_zip_reader_end(pZip);
+#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
+ else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))
+ return mz_zip_writer_end(pZip);
+#endif
+
+ return MZ_FALSE;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/
diff --git a/source/engine/miniz.h b/source/engine/miniz.h
new file mode 100644
index 0000000..9fcfffc
--- /dev/null
+++ b/source/engine/miniz.h
@@ -0,0 +1,1422 @@
+#ifndef MINIZ_EXPORT
+#define MINIZ_EXPORT
+#endif
+/* miniz.c 3.0.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing
+ See "unlicense" statement at the end of this file.
+ Rich Geldreich , last updated Oct. 13, 2013
+ Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt
+
+ Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define
+ MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros).
+
+ * Low-level Deflate/Inflate implementation notes:
+
+ Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or
+ greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses
+ approximately as well as zlib.
+
+ Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function
+ coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory
+ block large enough to hold the entire file.
+
+ The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation.
+
+ * zlib-style API notes:
+
+ miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in
+ zlib replacement in many apps:
+ The z_stream struct, optional memory allocation callbacks
+ deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound
+ inflateInit/inflateInit2/inflate/inflateReset/inflateEnd
+ compress, compress2, compressBound, uncompress
+ CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines.
+ Supports raw deflate streams or standard zlib streams with adler-32 checking.
+
+ Limitations:
+ The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries.
+ I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but
+ there are no guarantees that miniz.c pulls this off perfectly.
+
+ * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by
+ Alex Evans. Supports 1-4 bytes/pixel images.
+
+ * ZIP archive API notes:
+
+ The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to
+ get the job done with minimal fuss. There are simple API's to retrieve file information, read files from
+ existing archives, create new archives, append new files to existing archives, or clone archive data from
+ one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h),
+ or you can specify custom file read/write callbacks.
+
+ - Archive reading: Just call this function to read a single file from a disk archive:
+
+ void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name,
+ size_t *pSize, mz_uint zip_flags);
+
+ For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central
+ directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files.
+
+ - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file:
+
+ int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
+
+ The locate operation can optionally check file comments too, which (as one example) can be used to identify
+ multiple versions of the same file in an archive. This function uses a simple linear search through the central
+ directory, so it's not very fast.
+
+ Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and
+ retrieve detailed info on each file by calling mz_zip_reader_file_stat().
+
+ - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data
+ to disk and builds an exact image of the central directory in memory. The central directory image is written
+ all at once at the end of the archive file when the archive is finalized.
+
+ The archive writer can optionally align each file's local header and file data to any power of 2 alignment,
+ which can be useful when the archive will be read from optical media. Also, the writer supports placing
+ arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still
+ readable by any ZIP tool.
+
+ - Archive appending: The simple way to add a single file to an archive is to call this function:
+
+ mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name,
+ const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
+
+ The archive will be created if it doesn't already exist, otherwise it'll be appended to.
+ Note the appending is done in-place and is not an atomic operation, so if something goes wrong
+ during the operation it's possible the archive could be left without a central directory (although the local
+ file headers and file data will be fine, so the archive will be recoverable).
+
+ For more complex archive modification scenarios:
+ 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to
+ preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the
+ compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and
+ you're done. This is safe but requires a bunch of temporary disk space or heap memory.
+
+ 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(),
+ append new files as needed, then finalize the archive which will write an updated central directory to the
+ original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a
+ possibility that the archive's central directory could be lost with this method if anything goes wrong, though.
+
+ - ZIP archive support limitations:
+ No spanning support. Extraction functions can only handle unencrypted, stored or deflated files.
+ Requires streams capable of seeking.
+
+ * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the
+ below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it.
+
+ * Important: For best perf. be sure to customize the below macros for your target platform:
+ #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
+ #define MINIZ_LITTLE_ENDIAN 1
+ #define MINIZ_HAS_64BIT_REGISTERS 1
+
+ * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz
+ uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files
+ (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes).
+*/
+#pragma once
+
+
+
+/* Defines to completely disable specific portions of miniz.c:
+ If all macros here are defined the only functionality remaining will be CRC-32 and adler-32. */
+
+/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */
+/*#define MINIZ_NO_STDIO */
+
+/* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */
+/* get/set file times, and the C run-time funcs that get/set times won't be called. */
+/* The current downside is the times written to your archives will be from 1979. */
+/*#define MINIZ_NO_TIME */
+
+/* Define MINIZ_NO_DEFLATE_APIS to disable all compression API's. */
+/*#define MINIZ_NO_DEFLATE_APIS */
+
+/* Define MINIZ_NO_INFLATE_APIS to disable all decompression API's. */
+/*#define MINIZ_NO_INFLATE_APIS */
+
+/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */
+/*#define MINIZ_NO_ARCHIVE_APIS */
+
+/* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */
+/*#define MINIZ_NO_ARCHIVE_WRITING_APIS */
+
+/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */
+/*#define MINIZ_NO_ZLIB_APIS */
+
+/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */
+/*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */
+
+/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc.
+ Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc
+ callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user
+ functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */
+/*#define MINIZ_NO_MALLOC */
+
+#ifdef MINIZ_NO_INFLATE_APIS
+#define MINIZ_NO_ARCHIVE_APIS
+#endif
+
+#ifdef MINIZ_NO_DEFLATE_APIS
+#define MINIZ_NO_ARCHIVE_WRITING_APIS
+#endif
+
+#if defined(__TINYC__) && (defined(__linux) || defined(__linux__))
+/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */
+#define MINIZ_NO_TIME
+#endif
+
+#include
+
+#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS)
+#include
+#endif
+
+#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__)
+/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */
+#define MINIZ_X86_OR_X64_CPU 1
+#else
+#define MINIZ_X86_OR_X64_CPU 0
+#endif
+
+/* Set MINIZ_LITTLE_ENDIAN only if not set */
+#if !defined(MINIZ_LITTLE_ENDIAN)
+#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__)
+
+#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */
+#define MINIZ_LITTLE_ENDIAN 1
+#else
+#define MINIZ_LITTLE_ENDIAN 0
+#endif
+
+#else
+
+#if MINIZ_X86_OR_X64_CPU
+#define MINIZ_LITTLE_ENDIAN 1
+#else
+#define MINIZ_LITTLE_ENDIAN 0
+#endif
+
+#endif
+#endif
+
+/* Using unaligned loads and stores causes errors when using UBSan */
+#if defined(__has_feature)
+#if __has_feature(undefined_behavior_sanitizer)
+#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0
+#endif
+#endif
+
+/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */
+#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES)
+#if MINIZ_X86_OR_X64_CPU
+/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */
+#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0
+#define MINIZ_UNALIGNED_USE_MEMCPY
+#else
+#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0
+#endif
+#endif
+
+#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__)
+/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */
+#define MINIZ_HAS_64BIT_REGISTERS 1
+#else
+#define MINIZ_HAS_64BIT_REGISTERS 0
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ------------------- zlib-style API Definitions. */
+
+/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */
+typedef unsigned long mz_ulong;
+
+/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */
+MINIZ_EXPORT void mz_free(void *p);
+
+#define MZ_ADLER32_INIT (1)
+/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */
+MINIZ_EXPORT mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len);
+
+#define MZ_CRC32_INIT (0)
+/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */
+MINIZ_EXPORT mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len);
+
+/* Compression strategies. */
+enum
+{
+ MZ_DEFAULT_STRATEGY = 0,
+ MZ_FILTERED = 1,
+ MZ_HUFFMAN_ONLY = 2,
+ MZ_RLE = 3,
+ MZ_FIXED = 4
+};
+
+/* Method */
+#define MZ_DEFLATED 8
+
+/* Heap allocation callbacks.
+Note that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */
+typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size);
+typedef void (*mz_free_func)(void *opaque, void *address);
+typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size);
+
+/* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */
+enum
+{
+ MZ_NO_COMPRESSION = 0,
+ MZ_BEST_SPEED = 1,
+ MZ_BEST_COMPRESSION = 9,
+ MZ_UBER_COMPRESSION = 10,
+ MZ_DEFAULT_LEVEL = 6,
+ MZ_DEFAULT_COMPRESSION = -1
+};
+
+#define MZ_VERSION "11.0.2"
+#define MZ_VERNUM 0xB002
+#define MZ_VER_MAJOR 11
+#define MZ_VER_MINOR 2
+#define MZ_VER_REVISION 0
+#define MZ_VER_SUBREVISION 0
+
+#ifndef MINIZ_NO_ZLIB_APIS
+
+/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */
+enum
+{
+ MZ_NO_FLUSH = 0,
+ MZ_PARTIAL_FLUSH = 1,
+ MZ_SYNC_FLUSH = 2,
+ MZ_FULL_FLUSH = 3,
+ MZ_FINISH = 4,
+ MZ_BLOCK = 5
+};
+
+/* Return status codes. MZ_PARAM_ERROR is non-standard. */
+enum
+{
+ MZ_OK = 0,
+ MZ_STREAM_END = 1,
+ MZ_NEED_DICT = 2,
+ MZ_ERRNO = -1,
+ MZ_STREAM_ERROR = -2,
+ MZ_DATA_ERROR = -3,
+ MZ_MEM_ERROR = -4,
+ MZ_BUF_ERROR = -5,
+ MZ_VERSION_ERROR = -6,
+ MZ_PARAM_ERROR = -10000
+};
+
+/* Window bits */
+#define MZ_DEFAULT_WINDOW_BITS 15
+
+struct mz_internal_state;
+
+/* Compression/decompression stream struct. */
+typedef struct mz_stream_s
+{
+ const unsigned char *next_in; /* pointer to next byte to read */
+ unsigned int avail_in; /* number of bytes available at next_in */
+ mz_ulong total_in; /* total number of bytes consumed so far */
+
+ unsigned char *next_out; /* pointer to next byte to write */
+ unsigned int avail_out; /* number of bytes that can be written to next_out */
+ mz_ulong total_out; /* total number of bytes produced so far */
+
+ char *msg; /* error msg (unused) */
+ struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */
+
+ mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */
+ mz_free_func zfree; /* optional heap free function (defaults to free) */
+ void *opaque; /* heap alloc function user pointer */
+
+ int data_type; /* data_type (unused) */
+ mz_ulong adler; /* adler32 of the source or uncompressed data */
+ mz_ulong reserved; /* not used */
+} mz_stream;
+
+typedef mz_stream *mz_streamp;
+
+/* Returns the version string of miniz.c. */
+MINIZ_EXPORT const char *mz_version(void);
+
+#ifndef MINIZ_NO_DEFLATE_APIS
+
+/* mz_deflateInit() initializes a compressor with default options: */
+/* Parameters: */
+/* pStream must point to an initialized mz_stream struct. */
+/* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */
+/* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */
+/* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */
+/* Return values: */
+/* MZ_OK on success. */
+/* MZ_STREAM_ERROR if the stream is bogus. */
+/* MZ_PARAM_ERROR if the input parameters are bogus. */
+/* MZ_MEM_ERROR on out of memory. */
+MINIZ_EXPORT int mz_deflateInit(mz_streamp pStream, int level);
+
+/* mz_deflateInit2() is like mz_deflate(), except with more control: */
+/* Additional parameters: */
+/* method must be MZ_DEFLATED */
+/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */
+/* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */
+MINIZ_EXPORT int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy);
+
+/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */
+MINIZ_EXPORT int mz_deflateReset(mz_streamp pStream);
+
+/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */
+/* Parameters: */
+/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */
+/* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */
+/* Return values: */
+/* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */
+/* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */
+/* MZ_STREAM_ERROR if the stream is bogus. */
+/* MZ_PARAM_ERROR if one of the parameters is invalid. */
+/* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */
+MINIZ_EXPORT int mz_deflate(mz_streamp pStream, int flush);
+
+/* mz_deflateEnd() deinitializes a compressor: */
+/* Return values: */
+/* MZ_OK on success. */
+/* MZ_STREAM_ERROR if the stream is bogus. */
+MINIZ_EXPORT int mz_deflateEnd(mz_streamp pStream);
+
+/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */
+MINIZ_EXPORT mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len);
+
+/* Single-call compression functions mz_compress() and mz_compress2(): */
+/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */
+MINIZ_EXPORT int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
+MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level);
+
+/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */
+MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len);
+
+#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/
+
+#ifndef MINIZ_NO_INFLATE_APIS
+
+/* Initializes a decompressor. */
+MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream);
+
+/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */
+/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */
+MINIZ_EXPORT int mz_inflateInit2(mz_streamp pStream, int window_bits);
+
+/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */
+MINIZ_EXPORT int mz_inflateReset(mz_streamp pStream);
+
+/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */
+/* Parameters: */
+/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */
+/* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */
+/* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */
+/* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */
+/* Return values: */
+/* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */
+/* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */
+/* MZ_STREAM_ERROR if the stream is bogus. */
+/* MZ_DATA_ERROR if the deflate stream is invalid. */
+/* MZ_PARAM_ERROR if one of the parameters is invalid. */
+/* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */
+/* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */
+MINIZ_EXPORT int mz_inflate(mz_streamp pStream, int flush);
+
+/* Deinitializes a decompressor. */
+MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream);
+
+/* Single-call decompression. */
+/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */
+MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
+MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len);
+#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/
+
+/* Returns a string description of the specified error code, or NULL if the error code is invalid. */
+MINIZ_EXPORT const char *mz_error(int err);
+
+/* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */
+/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */
+#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES
+typedef unsigned char Byte;
+typedef unsigned int uInt;
+typedef mz_ulong uLong;
+typedef Byte Bytef;
+typedef uInt uIntf;
+typedef char charf;
+typedef int intf;
+typedef void *voidpf;
+typedef uLong uLongf;
+typedef void *voidp;
+typedef void *const voidpc;
+#define Z_NULL 0
+#define Z_NO_FLUSH MZ_NO_FLUSH
+#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH
+#define Z_SYNC_FLUSH MZ_SYNC_FLUSH
+#define Z_FULL_FLUSH MZ_FULL_FLUSH
+#define Z_FINISH MZ_FINISH
+#define Z_BLOCK MZ_BLOCK
+#define Z_OK MZ_OK
+#define Z_STREAM_END MZ_STREAM_END
+#define Z_NEED_DICT MZ_NEED_DICT
+#define Z_ERRNO MZ_ERRNO
+#define Z_STREAM_ERROR MZ_STREAM_ERROR
+#define Z_DATA_ERROR MZ_DATA_ERROR
+#define Z_MEM_ERROR MZ_MEM_ERROR
+#define Z_BUF_ERROR MZ_BUF_ERROR
+#define Z_VERSION_ERROR MZ_VERSION_ERROR
+#define Z_PARAM_ERROR MZ_PARAM_ERROR
+#define Z_NO_COMPRESSION MZ_NO_COMPRESSION
+#define Z_BEST_SPEED MZ_BEST_SPEED
+#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION
+#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION
+#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY
+#define Z_FILTERED MZ_FILTERED
+#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY
+#define Z_RLE MZ_RLE
+#define Z_FIXED MZ_FIXED
+#define Z_DEFLATED MZ_DEFLATED
+#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS
+#define alloc_func mz_alloc_func
+#define free_func mz_free_func
+#define internal_state mz_internal_state
+#define z_stream mz_stream
+
+#ifndef MINIZ_NO_DEFLATE_APIS
+#define deflateInit mz_deflateInit
+#define deflateInit2 mz_deflateInit2
+#define deflateReset mz_deflateReset
+#define deflate mz_deflate
+#define deflateEnd mz_deflateEnd
+#define deflateBound mz_deflateBound
+#define compress mz_compress
+#define compress2 mz_compress2
+#define compressBound mz_compressBound
+#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/
+
+#ifndef MINIZ_NO_INFLATE_APIS
+#define inflateInit mz_inflateInit
+#define inflateInit2 mz_inflateInit2
+#define inflateReset mz_inflateReset
+#define inflate mz_inflate
+#define inflateEnd mz_inflateEnd
+#define uncompress mz_uncompress
+#define uncompress2 mz_uncompress2
+#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/
+
+#define crc32 mz_crc32
+#define adler32 mz_adler32
+#define MAX_WBITS 15
+#define MAX_MEM_LEVEL 9
+#define zError mz_error
+#define ZLIB_VERSION MZ_VERSION
+#define ZLIB_VERNUM MZ_VERNUM
+#define ZLIB_VER_MAJOR MZ_VER_MAJOR
+#define ZLIB_VER_MINOR MZ_VER_MINOR
+#define ZLIB_VER_REVISION MZ_VER_REVISION
+#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION
+#define zlibVersion mz_version
+#define zlib_version mz_version()
+#endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */
+
+#endif /* MINIZ_NO_ZLIB_APIS */
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+
+
+#pragma once
+#include
+#include
+#include
+#include
+
+
+
+/* ------------------- Types and macros */
+typedef unsigned char mz_uint8;
+typedef signed short mz_int16;
+typedef unsigned short mz_uint16;
+typedef unsigned int mz_uint32;
+typedef unsigned int mz_uint;
+typedef int64_t mz_int64;
+typedef uint64_t mz_uint64;
+typedef int mz_bool;
+
+#define MZ_FALSE (0)
+#define MZ_TRUE (1)
+
+/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */
+#ifdef _MSC_VER
+#define MZ_MACRO_END while (0, 0)
+#else
+#define MZ_MACRO_END while (0)
+#endif
+
+#ifdef MINIZ_NO_STDIO
+#define MZ_FILE void *
+#else
+#include
+#define MZ_FILE FILE
+#endif /* #ifdef MINIZ_NO_STDIO */
+
+#ifdef MINIZ_NO_TIME
+typedef struct mz_dummy_time_t_tag
+{
+ mz_uint32 m_dummy1;
+ mz_uint32 m_dummy2;
+} mz_dummy_time_t;
+#define MZ_TIME_T mz_dummy_time_t
+#else
+#define MZ_TIME_T time_t
+#endif
+
+#define MZ_ASSERT(x) assert(x)
+
+#ifdef MINIZ_NO_MALLOC
+#define MZ_MALLOC(x) NULL
+#define MZ_FREE(x) (void)x, ((void)0)
+#define MZ_REALLOC(p, x) NULL
+#else
+#define MZ_MALLOC(x) malloc(x)
+#define MZ_FREE(x) free(x)
+#define MZ_REALLOC(p, x) realloc(p, x)
+#endif
+
+#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj))
+#define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj))
+#define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj))
+
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
+#define MZ_READ_LE16(p) *((const mz_uint16 *)(p))
+#define MZ_READ_LE32(p) *((const mz_uint32 *)(p))
+#else
+#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U))
+#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U))
+#endif
+
+#define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U))
+
+#ifdef _MSC_VER
+#define MZ_FORCEINLINE __forceinline
+#elif defined(__GNUC__)
+#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__))
+#else
+#define MZ_FORCEINLINE inline
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size);
+extern MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address);
+extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size);
+
+#define MZ_UINT16_MAX (0xFFFFU)
+#define MZ_UINT32_MAX (0xFFFFFFFFU)
+
+#ifdef __cplusplus
+}
+#endif
+ #pragma once
+
+
+#ifndef MINIZ_NO_DEFLATE_APIS
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* ------------------- Low-level Compression API Definitions */
+
+/* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */
+#define TDEFL_LESS_MEMORY 0
+
+/* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */
+/* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */
+enum
+{
+ TDEFL_HUFFMAN_ONLY = 0,
+ TDEFL_DEFAULT_MAX_PROBES = 128,
+ TDEFL_MAX_PROBES_MASK = 0xFFF
+};
+
+/* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */
+/* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */
+/* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */
+/* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */
+/* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */
+/* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */
+/* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */
+/* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */
+/* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */
+enum
+{
+ TDEFL_WRITE_ZLIB_HEADER = 0x01000,
+ TDEFL_COMPUTE_ADLER32 = 0x02000,
+ TDEFL_GREEDY_PARSING_FLAG = 0x04000,
+ TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000,
+ TDEFL_RLE_MATCHES = 0x10000,
+ TDEFL_FILTER_MATCHES = 0x20000,
+ TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000,
+ TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000
+};
+
+/* High level compression functions: */
+/* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */
+/* On entry: */
+/* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */
+/* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */
+/* On return: */
+/* Function returns a pointer to the compressed data, or NULL on failure. */
+/* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */
+/* The caller must free() the returned block when it's no longer needed. */
+MINIZ_EXPORT void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
+
+/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */
+/* Returns 0 on failure. */
+MINIZ_EXPORT size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
+
+/* Compresses an image to a compressed PNG file in memory. */
+/* On entry: */
+/* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */
+/* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */
+/* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */
+/* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */
+/* On return: */
+/* Function returns a pointer to the compressed data, or NULL on failure. */
+/* *pLen_out will be set to the size of the PNG image file. */
+/* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */
+MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip);
+MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out);
+
+/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */
+typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser);
+
+/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */
+MINIZ_EXPORT mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
+
+enum
+{
+ TDEFL_MAX_HUFF_TABLES = 3,
+ TDEFL_MAX_HUFF_SYMBOLS_0 = 288,
+ TDEFL_MAX_HUFF_SYMBOLS_1 = 32,
+ TDEFL_MAX_HUFF_SYMBOLS_2 = 19,
+ TDEFL_LZ_DICT_SIZE = 32768,
+ TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1,
+ TDEFL_MIN_MATCH_LEN = 3,
+ TDEFL_MAX_MATCH_LEN = 258
+};
+
+/* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */
+#if TDEFL_LESS_MEMORY
+enum
+{
+ TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024,
+ TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10,
+ TDEFL_MAX_HUFF_SYMBOLS = 288,
+ TDEFL_LZ_HASH_BITS = 12,
+ TDEFL_LEVEL1_HASH_SIZE_MASK = 4095,
+ TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3,
+ TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS
+};
+#else
+enum
+{
+ TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024,
+ TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10,
+ TDEFL_MAX_HUFF_SYMBOLS = 288,
+ TDEFL_LZ_HASH_BITS = 15,
+ TDEFL_LEVEL1_HASH_SIZE_MASK = 4095,
+ TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3,
+ TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS
+};
+#endif
+
+/* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */
+typedef enum {
+ TDEFL_STATUS_BAD_PARAM = -2,
+ TDEFL_STATUS_PUT_BUF_FAILED = -1,
+ TDEFL_STATUS_OKAY = 0,
+ TDEFL_STATUS_DONE = 1
+} tdefl_status;
+
+/* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */
+typedef enum {
+ TDEFL_NO_FLUSH = 0,
+ TDEFL_SYNC_FLUSH = 2,
+ TDEFL_FULL_FLUSH = 3,
+ TDEFL_FINISH = 4
+} tdefl_flush;
+
+/* tdefl's compression state structure. */
+typedef struct
+{
+ tdefl_put_buf_func_ptr m_pPut_buf_func;
+ void *m_pPut_buf_user;
+ mz_uint m_flags, m_max_probes[2];
+ int m_greedy_parsing;
+ mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size;
+ mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end;
+ mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer;
+ mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish;
+ tdefl_status m_prev_return_status;
+ const void *m_pIn_buf;
+ void *m_pOut_buf;
+ size_t *m_pIn_buf_size, *m_pOut_buf_size;
+ tdefl_flush m_flush;
+ const mz_uint8 *m_pSrc;
+ size_t m_src_buf_left, m_out_buf_ofs;
+ mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1];
+ mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
+ mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
+ mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
+ mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE];
+ mz_uint16 m_next[TDEFL_LZ_DICT_SIZE];
+ mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE];
+ mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE];
+} tdefl_compressor;
+
+/* Initializes the compressor. */
+/* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */
+/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */
+/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */
+/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */
+MINIZ_EXPORT tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
+
+/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */
+MINIZ_EXPORT tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush);
+
+/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */
+/* tdefl_compress_buffer() always consumes the entire input buffer. */
+MINIZ_EXPORT tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush);
+
+MINIZ_EXPORT tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d);
+MINIZ_EXPORT mz_uint32 tdefl_get_adler32(tdefl_compressor *d);
+
+/* Create tdefl_compress() flags given zlib-style compression parameters. */
+/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */
+/* window_bits may be -15 (raw deflate) or 15 (zlib) */
+/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */
+MINIZ_EXPORT mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy);
+
+#ifndef MINIZ_NO_MALLOC
+/* Allocate the tdefl_compressor structure in C so that */
+/* non-C language bindings to tdefl_ API don't need to worry about */
+/* structure size and allocation mechanism. */
+MINIZ_EXPORT tdefl_compressor *tdefl_compressor_alloc(void);
+MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/
+ #pragma once
+
+/* ------------------- Low-level Decompression API Definitions */
+
+#ifndef MINIZ_NO_INFLATE_APIS
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* Decompression flags used by tinfl_decompress(). */
+/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */
+/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */
+/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */
+/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */
+enum
+{
+ TINFL_FLAG_PARSE_ZLIB_HEADER = 1,
+ TINFL_FLAG_HAS_MORE_INPUT = 2,
+ TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4,
+ TINFL_FLAG_COMPUTE_ADLER32 = 8
+};
+
+/* High level decompression functions: */
+/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */
+/* On entry: */
+/* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */
+/* On return: */
+/* Function returns a pointer to the decompressed data, or NULL on failure. */
+/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */
+/* The caller must call mz_free() on the returned block when it's no longer needed. */
+MINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
+
+/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */
+/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */
+#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1))
+MINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
+
+/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */
+/* Returns 1 on success or 0 on failure. */
+typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser);
+MINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
+
+struct tinfl_decompressor_tag;
+typedef struct tinfl_decompressor_tag tinfl_decompressor;
+
+#ifndef MINIZ_NO_MALLOC
+/* Allocate the tinfl_decompressor structure in C so that */
+/* non-C language bindings to tinfl_ API don't need to worry about */
+/* structure size and allocation mechanism. */
+MINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void);
+MINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp);
+#endif
+
+/* Max size of LZ dictionary. */
+#define TINFL_LZ_DICT_SIZE 32768
+
+/* Return status. */
+typedef enum {
+ /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */
+ /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */
+ /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */
+ TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4,
+
+ /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */
+ TINFL_STATUS_BAD_PARAM = -3,
+
+ /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */
+ TINFL_STATUS_ADLER32_MISMATCH = -2,
+
+ /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */
+ TINFL_STATUS_FAILED = -1,
+
+ /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */
+
+ /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */
+ /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */
+ TINFL_STATUS_DONE = 0,
+
+ /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */
+ /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */
+ /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */
+ TINFL_STATUS_NEEDS_MORE_INPUT = 1,
+
+ /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */
+ /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */
+ /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */
+ /* so I may need to add some code to address this. */
+ TINFL_STATUS_HAS_MORE_OUTPUT = 2
+} tinfl_status;
+
+/* Initializes the decompressor to its initial state. */
+#define tinfl_init(r) \
+ do \
+ { \
+ (r)->m_state = 0; \
+ } \
+ MZ_MACRO_END
+#define tinfl_get_adler32(r) (r)->m_check_adler32
+
+/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */
+/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */
+MINIZ_EXPORT tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags);
+
+/* Internal/private bits follow. */
+enum
+{
+ TINFL_MAX_HUFF_TABLES = 3,
+ TINFL_MAX_HUFF_SYMBOLS_0 = 288,
+ TINFL_MAX_HUFF_SYMBOLS_1 = 32,
+ TINFL_MAX_HUFF_SYMBOLS_2 = 19,
+ TINFL_FAST_LOOKUP_BITS = 10,
+ TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS
+};
+
+#if MINIZ_HAS_64BIT_REGISTERS
+#define TINFL_USE_64BIT_BITBUF 1
+#else
+#define TINFL_USE_64BIT_BITBUF 0
+#endif
+
+#if TINFL_USE_64BIT_BITBUF
+typedef mz_uint64 tinfl_bit_buf_t;
+#define TINFL_BITBUF_SIZE (64)
+#else
+typedef mz_uint32 tinfl_bit_buf_t;
+#define TINFL_BITBUF_SIZE (32)
+#endif
+
+struct tinfl_decompressor_tag
+{
+ mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES];
+ tinfl_bit_buf_t m_bit_buf;
+ size_t m_dist_from_out_buf_start;
+ mz_int16 m_look_up[TINFL_MAX_HUFF_TABLES][TINFL_FAST_LOOKUP_SIZE];
+ mz_int16 m_tree_0[TINFL_MAX_HUFF_SYMBOLS_0 * 2];
+ mz_int16 m_tree_1[TINFL_MAX_HUFF_SYMBOLS_1 * 2];
+ mz_int16 m_tree_2[TINFL_MAX_HUFF_SYMBOLS_2 * 2];
+ mz_uint8 m_code_size_0[TINFL_MAX_HUFF_SYMBOLS_0];
+ mz_uint8 m_code_size_1[TINFL_MAX_HUFF_SYMBOLS_1];
+ mz_uint8 m_code_size_2[TINFL_MAX_HUFF_SYMBOLS_2];
+ mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137];
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/
+
+#pragma once
+
+
+/* ------------------- ZIP archive reading/writing */
+
+#ifndef MINIZ_NO_ARCHIVE_APIS
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum
+{
+ /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */
+ MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024,
+ MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512,
+ MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512
+};
+
+typedef struct
+{
+ /* Central directory file index. */
+ mz_uint32 m_file_index;
+
+ /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */
+ mz_uint64 m_central_dir_ofs;
+
+ /* These fields are copied directly from the zip's central dir. */
+ mz_uint16 m_version_made_by;
+ mz_uint16 m_version_needed;
+ mz_uint16 m_bit_flag;
+ mz_uint16 m_method;
+
+ /* CRC-32 of uncompressed data. */
+ mz_uint32 m_crc32;
+
+ /* File's compressed size. */
+ mz_uint64 m_comp_size;
+
+ /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */
+ mz_uint64 m_uncomp_size;
+
+ /* Zip internal and external file attributes. */
+ mz_uint16 m_internal_attr;
+ mz_uint32 m_external_attr;
+
+ /* Entry's local header file offset in bytes. */
+ mz_uint64 m_local_header_ofs;
+
+ /* Size of comment in bytes. */
+ mz_uint32 m_comment_size;
+
+ /* MZ_TRUE if the entry appears to be a directory. */
+ mz_bool m_is_directory;
+
+ /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */
+ mz_bool m_is_encrypted;
+
+ /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */
+ mz_bool m_is_supported;
+
+ /* Filename. If string ends in '/' it's a subdirectory entry. */
+ /* Guaranteed to be zero terminated, may be truncated to fit. */
+ char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE];
+
+ /* Comment field. */
+ /* Guaranteed to be zero terminated, may be truncated to fit. */
+ char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE];
+
+#ifdef MINIZ_NO_TIME
+ MZ_TIME_T m_padding;
+#else
+ MZ_TIME_T m_time;
+#endif
+} mz_zip_archive_file_stat;
+
+typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n);
+typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n);
+typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque);
+
+struct mz_zip_internal_state_tag;
+typedef struct mz_zip_internal_state_tag mz_zip_internal_state;
+
+typedef enum {
+ MZ_ZIP_MODE_INVALID = 0,
+ MZ_ZIP_MODE_READING = 1,
+ MZ_ZIP_MODE_WRITING = 2,
+ MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3
+} mz_zip_mode;
+
+typedef enum {
+ MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100,
+ MZ_ZIP_FLAG_IGNORE_PATH = 0x0200,
+ MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400,
+ MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800,
+ MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */
+ MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */
+ MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */
+ MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000,
+ MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000,
+ /*After adding a compressed file, seek back
+ to local file header and set the correct sizes*/
+ MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE = 0x20000
+} mz_zip_flags;
+
+typedef enum {
+ MZ_ZIP_TYPE_INVALID = 0,
+ MZ_ZIP_TYPE_USER,
+ MZ_ZIP_TYPE_MEMORY,
+ MZ_ZIP_TYPE_HEAP,
+ MZ_ZIP_TYPE_FILE,
+ MZ_ZIP_TYPE_CFILE,
+ MZ_ZIP_TOTAL_TYPES
+} mz_zip_type;
+
+/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */
+typedef enum {
+ MZ_ZIP_NO_ERROR = 0,
+ MZ_ZIP_UNDEFINED_ERROR,
+ MZ_ZIP_TOO_MANY_FILES,
+ MZ_ZIP_FILE_TOO_LARGE,
+ MZ_ZIP_UNSUPPORTED_METHOD,
+ MZ_ZIP_UNSUPPORTED_ENCRYPTION,
+ MZ_ZIP_UNSUPPORTED_FEATURE,
+ MZ_ZIP_FAILED_FINDING_CENTRAL_DIR,
+ MZ_ZIP_NOT_AN_ARCHIVE,
+ MZ_ZIP_INVALID_HEADER_OR_CORRUPTED,
+ MZ_ZIP_UNSUPPORTED_MULTIDISK,
+ MZ_ZIP_DECOMPRESSION_FAILED,
+ MZ_ZIP_COMPRESSION_FAILED,
+ MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE,
+ MZ_ZIP_CRC_CHECK_FAILED,
+ MZ_ZIP_UNSUPPORTED_CDIR_SIZE,
+ MZ_ZIP_ALLOC_FAILED,
+ MZ_ZIP_FILE_OPEN_FAILED,
+ MZ_ZIP_FILE_CREATE_FAILED,
+ MZ_ZIP_FILE_WRITE_FAILED,
+ MZ_ZIP_FILE_READ_FAILED,
+ MZ_ZIP_FILE_CLOSE_FAILED,
+ MZ_ZIP_FILE_SEEK_FAILED,
+ MZ_ZIP_FILE_STAT_FAILED,
+ MZ_ZIP_INVALID_PARAMETER,
+ MZ_ZIP_INVALID_FILENAME,
+ MZ_ZIP_BUF_TOO_SMALL,
+ MZ_ZIP_INTERNAL_ERROR,
+ MZ_ZIP_FILE_NOT_FOUND,
+ MZ_ZIP_ARCHIVE_TOO_LARGE,
+ MZ_ZIP_VALIDATION_FAILED,
+ MZ_ZIP_WRITE_CALLBACK_FAILED,
+ MZ_ZIP_TOTAL_ERRORS
+} mz_zip_error;
+
+typedef struct
+{
+ mz_uint64 m_archive_size;
+ mz_uint64 m_central_directory_file_ofs;
+
+ /* We only support up to UINT32_MAX files in zip64 mode. */
+ mz_uint32 m_total_files;
+ mz_zip_mode m_zip_mode;
+ mz_zip_type m_zip_type;
+ mz_zip_error m_last_error;
+
+ mz_uint64 m_file_offset_alignment;
+
+ mz_alloc_func m_pAlloc;
+ mz_free_func m_pFree;
+ mz_realloc_func m_pRealloc;
+ void *m_pAlloc_opaque;
+
+ mz_file_read_func m_pRead;
+ mz_file_write_func m_pWrite;
+ mz_file_needs_keepalive m_pNeeds_keepalive;
+ void *m_pIO_opaque;
+
+ mz_zip_internal_state *m_pState;
+
+} mz_zip_archive;
+
+typedef struct
+{
+ mz_zip_archive *pZip;
+ mz_uint flags;
+
+ int status;
+
+ mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs;
+ mz_zip_archive_file_stat file_stat;
+ void *pRead_buf;
+ void *pWrite_buf;
+
+ size_t out_blk_remain;
+
+ tinfl_decompressor inflator;
+
+#ifdef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
+ mz_uint padding;
+#else
+ mz_uint file_crc32;
+#endif
+
+} mz_zip_reader_extract_iter_state;
+
+/* -------- ZIP reading */
+
+/* Inits a ZIP archive reader. */
+/* These functions read and validate the archive's central directory. */
+MINIZ_EXPORT mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags);
+
+MINIZ_EXPORT mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags);
+
+#ifndef MINIZ_NO_STDIO
+/* Read a archive from a disk file. */
+/* file_start_ofs is the file offset where the archive actually begins, or 0. */
+/* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */
+MINIZ_EXPORT mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags);
+MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size);
+
+/* Read an archive from an already opened FILE, beginning at the current file position. */
+/* The archive is assumed to be archive_size bytes long. If archive_size is 0, then the entire rest of the file is assumed to contain the archive. */
+/* The FILE will NOT be closed when mz_zip_reader_end() is called. */
+MINIZ_EXPORT mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags);
+#endif
+
+/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */
+MINIZ_EXPORT mz_bool mz_zip_reader_end(mz_zip_archive *pZip);
+
+/* -------- ZIP reading or writing */
+
+/* Clears a mz_zip_archive struct to all zeros. */
+/* Important: This must be done before passing the struct to any mz_zip functions. */
+MINIZ_EXPORT void mz_zip_zero_struct(mz_zip_archive *pZip);
+
+MINIZ_EXPORT mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip);
+MINIZ_EXPORT mz_zip_type mz_zip_get_type(mz_zip_archive *pZip);
+
+/* Returns the total number of files in the archive. */
+MINIZ_EXPORT mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip);
+
+MINIZ_EXPORT mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip);
+MINIZ_EXPORT mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip);
+MINIZ_EXPORT MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip);
+
+/* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */
+MINIZ_EXPORT size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n);
+
+/* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */
+/* Note that the m_last_error functionality is not thread safe. */
+MINIZ_EXPORT mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num);
+MINIZ_EXPORT mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip);
+MINIZ_EXPORT mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip);
+MINIZ_EXPORT mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip);
+MINIZ_EXPORT const char *mz_zip_get_error_string(mz_zip_error mz_err);
+
+/* MZ_TRUE if the archive file entry is a directory entry. */
+MINIZ_EXPORT mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index);
+
+/* MZ_TRUE if the file is encrypted/strong encrypted. */
+MINIZ_EXPORT mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index);
+
+/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */
+MINIZ_EXPORT mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index);
+
+/* Retrieves the filename of an archive file entry. */
+/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */
+MINIZ_EXPORT mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size);
+
+/* Attempts to locates a file in the archive's central directory. */
+/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */
+/* Returns -1 if the file cannot be found. */
+MINIZ_EXPORT int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
+MINIZ_EXPORT mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index);
+
+/* Returns detailed information about an archive file entry. */
+MINIZ_EXPORT mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat);
+
+/* MZ_TRUE if the file is in zip64 format. */
+/* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */
+MINIZ_EXPORT mz_bool mz_zip_is_zip64(mz_zip_archive *pZip);
+
+/* Returns the total central directory size in bytes. */
+/* The current max supported size is <= MZ_UINT32_MAX. */
+MINIZ_EXPORT size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip);
+
+/* Extracts a archive file to a memory buffer using no memory allocation. */
+/* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */
+MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
+MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
+
+/* Extracts a archive file to a memory buffer. */
+MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags);
+MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags);
+
+/* Extracts a archive file to a dynamically allocated heap buffer. */
+/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */
+/* Returns NULL and sets the last error on failure. */
+MINIZ_EXPORT void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags);
+MINIZ_EXPORT void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags);
+
+/* Extracts a archive file using a callback function to output the file's data. */
+MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
+MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
+
+/* Extract a file iteratively */
+MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags);
+MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags);
+MINIZ_EXPORT size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size);
+MINIZ_EXPORT mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState);
+
+#ifndef MINIZ_NO_STDIO
+/* Extracts a archive file to a disk file and sets its last accessed and modified times. */
+/* This function only extracts files, not archive directory records. */
+MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags);
+MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags);
+
+/* Extracts a archive file starting at the current position in the destination FILE stream. */
+MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags);
+MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags);
+#endif
+
+#if 0
+/* TODO */
+ typedef void *mz_zip_streaming_extract_state_ptr;
+ mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags);
+ mz_uint64 mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState);
+ mz_uint64 mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState);
+ mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, mz_uint64 new_ofs);
+ size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size);
+ mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState);
+#endif
+
+/* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */
+/* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */
+MINIZ_EXPORT mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags);
+
+/* Validates an entire archive by calling mz_zip_validate_file() on each file. */
+MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags);
+
+/* Misc utils/helpers, valid for ZIP reading or writing */
+MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr);
+#ifndef MINIZ_NO_STDIO
+MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr);
+#endif
+
+/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */
+MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip);
+
+/* -------- ZIP writing */
+
+#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
+
+/* Inits a ZIP archive writer. */
+/*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/
+/*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/
+MINIZ_EXPORT mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size);
+MINIZ_EXPORT mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags);
+
+MINIZ_EXPORT mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size);
+MINIZ_EXPORT mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags);
+
+#ifndef MINIZ_NO_STDIO
+MINIZ_EXPORT mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning);
+MINIZ_EXPORT mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags);
+MINIZ_EXPORT mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags);
+#endif
+
+/* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */
+/* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */
+/* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */
+/* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */
+/* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */
+/* the archive is finalized the file's central directory will be hosed. */
+MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename);
+MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags);
+
+/* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */
+/* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */
+/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */
+MINIZ_EXPORT mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags);
+
+/* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */
+/* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */
+MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,
+ mz_uint64 uncomp_size, mz_uint32 uncomp_crc32);
+
+MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,
+ mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len,
+ const char *user_extra_data_central, mz_uint user_extra_data_central_len);
+
+/* Adds the contents of a file to an archive. This function also records the disk file's modified time into the archive. */
+/* File data is supplied via a read callback function. User mz_zip_writer_add_(c)file to add a file directly.*/
+MINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size,
+ const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len,
+ const char *user_extra_data_central, mz_uint user_extra_data_central_len);
+
+
+#ifndef MINIZ_NO_STDIO
+/* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */
+/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */
+MINIZ_EXPORT mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
+
+/* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */
+MINIZ_EXPORT mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size,
+ const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len,
+ const char *user_extra_data_central, mz_uint user_extra_data_central_len);
+#endif
+
+/* Adds a file to an archive by fully cloning the data from another archive. */
+/* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */
+MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index);
+
+/* Finalizes the archive by writing the central directory records followed by the end of central directory record. */
+/* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */
+/* An archive must be manually finalized by calling this function for it to be valid. */
+MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip);
+
+/* Finalizes a heap archive, returning a pointer to the heap block and its size. */
+/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */
+MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize);
+
+/* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */
+/* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */
+MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip);
+
+/* -------- Misc. high-level helper functions: */
+
+/* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */
+/* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */
+/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */
+/* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */
+MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
+MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr);
+
+#ifndef MINIZ_NO_STDIO
+/* Reads a single file from an archive into a heap block. */
+/* If pComment is not NULL, only the file with the specified comment will be extracted. */
+/* Returns NULL on failure. */
+MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags);
+MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr);
+#endif
+
+#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MINIZ_NO_ARCHIVE_APIS */
diff --git a/source/engine/render.h b/source/engine/render.h
index f0e2dfa..f22150b 100644
--- a/source/engine/render.h
+++ b/source/engine/render.h
@@ -7,7 +7,6 @@
#define SOKOL_GLES3
#elif __WIN32
#define SOKOL_D3D11
- #define SOKOL_WIN32_FORCE_MAIN
#elif __APPLE__
#define SOKOL_METAL
#endif
diff --git a/source/engine/resources.c b/source/engine/resources.c
index 33a6df0..ab81a18 100644
--- a/source/engine/resources.c
+++ b/source/engine/resources.c
@@ -15,7 +15,7 @@
#include "font.h"
#include
-#include "cdb.h"
+#include "miniz.h"
#ifndef __EMSCRIPTEN__
#include
@@ -40,8 +40,8 @@ struct dirent *c_dirent = NULL;
char pathbuf[MAXPATH + 1];
-static struct cdb corecdb;
-static struct cdb game_cdb;
+static mz_zip_archive corecdb;
+static mz_zip_archive game_cdb;
int LOADED_GAME = 0;
uint8_t *gamebuf;
@@ -49,7 +49,7 @@ uint8_t *gamebuf;
static void response_cb(const sfetch_response_t *r)
{
if (r->fetched) {
- cdb_initf(&game_cdb, r->data.ptr, r->data.size);
+ mz_zip_reader_init_mem(&game_cdb, r->data.ptr, r->data.size,0);
LOADED_GAME = 1;
}
if (r->finished) {
@@ -78,7 +78,7 @@ void resources_init() {
}
});
- cdb_initf(&corecdb, core_cdb, core_cdb_len);
+ mz_zip_reader_init_mem(&corecdb, core_cdb, core_cdb_len, 0);
}
char *get_filename_from_path(char *path, int extension) {
@@ -160,6 +160,7 @@ char **ls(const char *path)
#else
void fill_extensions(char *paths, const char *path, const char *ext)
{};
+char **ls(const char *path) { return NULL; }
#endif
char *str_replace_ext(const char *s, const char *newext) {
@@ -172,32 +173,11 @@ char *str_replace_ext(const char *s, const char *newext) {
return ret;
}
-FILE *path_open(const char *tag, const char *fmt, ...) {
- va_list args;
- va_start(args, fmt);
- vsnprintf(pathbuf, MAXPATH+1, fmt, args);
- va_end(args);
-
- FILE *f = fopen(pathbuf, tag);
- return f;
-}
-
-void *cdb_slurp(struct cdb *cdb, const char *file, size_t *size)
-{
- unsigned vlen, vpos;
- vpos = cdb_datapos(cdb);
- vlen = cdb_datalen(cdb);
- char *data = malloc(vlen+1);
- cdb_read(cdb, data, vlen, vpos);
- if (size) *size = vlen;
- return data;
-}
-
int fexists(const char *path)
{
int len = strlen(path);
- if (cdb_find(&game_cdb, path, len)) return 1;
- else if (cdb_find(&corecdb, path, len)) return 1;
+ if (mz_zip_reader_locate_file(&game_cdb, path, NULL, 0) != -1) return 1;
+ else if (mz_zip_reader_locate_file(&corecdb, path, NULL, 0) != -1) return 1;
else if (!access(path, R_OK)) return 1;
return 0;
@@ -205,7 +185,6 @@ int fexists(const char *path)
void *os_slurp(const char *file, size_t *size)
{
- YughInfo("Slurping %s from the OS.\n", file);
FILE *f;
jump:
@@ -227,12 +206,13 @@ void *os_slurp(const char *file, size_t *size)
void *slurp_file(const char *filename, size_t *size)
{
+ void *ret;
if (!access(filename, R_OK))
return os_slurp(filename, size);
- else if (cdb_find(&game_cdb, filename, strlen(filename)))
- return cdb_slurp(&game_cdb, filename, size);
- else if (cdb_find(&corecdb, filename, strlen(filename)))
- return cdb_slurp(&corecdb, filename, size);
+ else if (ret = mz_zip_reader_extract_file_to_heap(&game_cdb, filename, size, 0))
+ return ret;
+ else if (ret = mz_zip_reader_extract_file_to_heap(&corecdb, filename, size, 0))
+ return ret;
return NULL;
}
@@ -311,58 +291,3 @@ int slurp_write(const char *txt, const char *filename, size_t len) {
fclose(f);
return 0;
}
-
-#ifndef __EMSCRIPTEN__
-static struct cdb_make cdbm;
-
-static const char *pack_ext[] = {".qoi", ".qoa", ".js", ".wav", ".mp3", ".png", ".sf2", ".midi", ".lvl", ".glsl", ".ttf", ".json", ".jso"};
-
-static int ftw_pack(const char *path, const struct stat *sb, int flag)
-{
- if (flag != FTW_F) return 0;
- int pack = 0;
- char *ext = strrchr(path, '.');
-
- if (!ext)
- return 0;
-
- for (int i = 0; i < 13; i++) {
- if (!strcmp(ext, pack_ext[i])) {
- pack = 1;
- break;
- }
- }
-
- if (!pack) return 0;
-
- size_t len;
- void *file = slurp_file(path, &len);
- cdb_make_add(&cdbm, &path[2], strlen(&path[2]), file, len);
-
- free(file);
-
- return 0;
-}
-
-void pack_engine(const char *fname)
-{
- int fd;
- char *key, *va;
- unsigned klen, vlen;
- fd = creat(fname, O_RDWR);
- if (fd == -1) {
- YughError("Couldn't make file at %s.", fname);
- return;
- }
- cdb_make_start(&cdbm, fd);
- ftw(".", ftw_pack, 20);
- cdb_make_finish(&cdbm);
- close(fd);
-}
-#else
-void pack_engine(const char *fname){
- YughError("Cannot pack engine on a web build.");
-}
-
-char **ls(const char *path) { return NULL; }
-#endif
diff --git a/source/engine/resources.h b/source/engine/resources.h
index b7cc893..a6dfba0 100644
--- a/source/engine/resources.h
+++ b/source/engine/resources.h
@@ -14,7 +14,6 @@ char *get_filename_from_path(char *path, int extension);
char *get_directory_from_path(char *path);
char *str_replace_ext(const char *s, const char *newext);
FILE *res_open(char *path, const char *tag);
-FILE *path_open(const char *tag, const char *fmt, ...);
char **ls(const char *path);
int cp(const char *p1, const char *p2);
int fexists(const char *path);
@@ -28,8 +27,6 @@ int slurp_write(const char *txt, const char *filename, size_t len);
char *seprint(char *fmt, ...);
-void pack_engine(const char *fname);
-
static inline void *stbarrdup(void *mem, size_t size, int len) {
void *out = NULL;
arrsetlen(out, len);
diff --git a/source/engine/script.c b/source/engine/script.c
index e8e5f01..9eb4ad4 100644
--- a/source/engine/script.c
+++ b/source/engine/script.c
@@ -73,11 +73,9 @@ JSValue script_eval(const char *file, const char *script)
void script_call_sym(JSValue sym, int argc, JSValue *argv) {
if (!JS_IsFunction(js, sym)) return;
- JSValue g = JS_GetGlobalObject(js);
- JSValue ret = JS_Call(js, sym, JS_GetGlobalObject(js), argc, argv);
+ JSValue ret = JS_Call(js, sym, JS_UNDEFINED, argc, argv);
js_print_exception(ret);
JS_FreeValue(js, ret);
- JS_FreeValue(js,g);
}
void out_memusage(const char *file)
diff --git a/source/engine/thirdparty/cgltf/.github/workflows/build.yml b/source/engine/thirdparty/cgltf/.github/workflows/build.yml
deleted file mode 100644
index 6e0bd7f..0000000
--- a/source/engine/thirdparty/cgltf/.github/workflows/build.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: build
-
-on: [push, pull_request]
-
-jobs:
- generic:
- strategy:
- matrix:
- os: [windows, ubuntu, macos]
- name: ${{matrix.os}}
- runs-on: ${{matrix.os}}-latest
- steps:
- - uses: actions/checkout@v1
- - name: test
- run: cd test && python test_all.py
diff --git a/source/engine/thirdparty/cgltf/fuzz/data/BadBasisU.gltf b/source/engine/thirdparty/cgltf/fuzz/data/BadBasisU.gltf
deleted file mode 100644
index e108e39..0000000
--- a/source/engine/thirdparty/cgltf/fuzz/data/BadBasisU.gltf
+++ /dev/null
@@ -1 +0,0 @@
-{"textures":[{"extensions":{"KHR_texture_basisu":{""}}:{""""},""}]}
\ No newline at end of file
diff --git a/source/engine/thirdparty/cgltf/fuzz/data/Box.glb b/source/engine/thirdparty/cgltf/fuzz/data/Box.glb
deleted file mode 100644
index 95ec886..0000000
Binary files a/source/engine/thirdparty/cgltf/fuzz/data/Box.glb and /dev/null differ
diff --git a/source/engine/thirdparty/cgltf/fuzz/data/TriangleWithoutIndices.gltf b/source/engine/thirdparty/cgltf/fuzz/data/TriangleWithoutIndices.gltf
deleted file mode 100644
index 06632b5..0000000
--- a/source/engine/thirdparty/cgltf/fuzz/data/TriangleWithoutIndices.gltf
+++ /dev/null
@@ -1,53 +0,0 @@
-{
- "scenes" : [
- {
- "nodes" : [ 0 ]
- }
- ],
-
- "nodes" : [
- {
- "mesh" : 0
- }
- ],
-
- "meshes" : [
- {
- "primitives" : [ {
- "attributes" : {
- "POSITION" : 0
- }
- } ]
- }
- ],
-
- "buffers" : [
- {
- "uri" : "data:application/octet-stream;base64,AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAA",
- "byteLength" : 36
- }
- ],
- "bufferViews" : [
- {
- "buffer" : 0,
- "byteOffset" : 0,
- "byteLength" : 36,
- "target" : 34962
- }
- ],
- "accessors" : [
- {
- "bufferView" : 0,
- "byteOffset" : 0,
- "componentType" : 5126,
- "count" : 3,
- "type" : "VEC3",
- "max" : [ 1.0, 1.0, 0.0 ],
- "min" : [ 0.0, 0.0, 0.0 ]
- }
- ],
-
- "asset" : {
- "version" : "2.0"
- }
-}
\ No newline at end of file
diff --git a/source/engine/thirdparty/cgltf/fuzz/data/WaterBottle.gltf b/source/engine/thirdparty/cgltf/fuzz/data/WaterBottle.gltf
deleted file mode 100644
index 9f23f3a..0000000
--- a/source/engine/thirdparty/cgltf/fuzz/data/WaterBottle.gltf
+++ /dev/null
@@ -1,172 +0,0 @@
-{
- "accessors": [
- {
- "bufferView": 0,
- "componentType": 5126,
- "count": 2549,
- "type": "VEC2"
- },
- {
- "bufferView": 1,
- "componentType": 5126,
- "count": 2549,
- "type": "VEC3"
- },
- {
- "bufferView": 2,
- "componentType": 5126,
- "count": 2549,
- "type": "VEC4"
- },
- {
- "bufferView": 3,
- "componentType": 5126,
- "count": 2549,
- "type": "VEC3",
- "max": [
- 0.05445001,
- 0.130220339,
- 0.0544500239
- ],
- "min": [
- -0.05445001,
- -0.130220339,
- -0.0544500239
- ]
- },
- {
- "bufferView": 4,
- "componentType": 5123,
- "count": 13530,
- "type": "SCALAR"
- }
- ],
- "asset": {
- "generator": "glTF Tools for Unity",
- "version": "2.0"
- },
- "bufferViews": [
- {
- "buffer": 0,
- "byteLength": 20392
- },
- {
- "buffer": 0,
- "byteOffset": 20392,
- "byteLength": 30588
- },
- {
- "buffer": 0,
- "byteOffset": 50980,
- "byteLength": 40784
- },
- {
- "buffer": 0,
- "byteOffset": 91764,
- "byteLength": 30588
- },
- {
- "buffer": 0,
- "byteOffset": 122352,
- "byteLength": 27060
- }
- ],
- "buffers": [
- {
- "uri": "WaterBottle.bin",
- "byteLength": 149412
- }
- ],
- "images": [
- {
- "uri": "WaterBottle_baseColor.png"
- },
- {
- "uri": "WaterBottle_occlusionRoughnessMetallic.png"
- },
- {
- "uri": "WaterBottle_normal.png"
- },
- {
- "uri": "WaterBottle_emissive.png"
- }
- ],
- "meshes": [
- {
- "primitives": [
- {
- "attributes": {
- "TEXCOORD_0": 0,
- "NORMAL": 1,
- "TANGENT": 2,
- "POSITION": 3
- },
- "indices": 4,
- "material": 0
- }
- ],
- "name": "WaterBottle"
- }
- ],
- "materials": [
- {
- "pbrMetallicRoughness": {
- "baseColorTexture": {
- "index": 0
- },
- "metallicRoughnessTexture": {
- "index": 1
- }
- },
- "normalTexture": {
- "index": 2
- },
- "occlusionTexture": {
- "index": 1
- },
- "emissiveFactor": [
- 1.0,
- 1.0,
- 1.0
- ],
- "emissiveTexture": {
- "index": 3
- },
- "name": "BottleMat"
- }
- ],
- "nodes": [
- {
- "mesh": 0,
- "rotation": [
- 0.0,
- 1.0,
- 0.0,
- 0.0
- ],
- "name": "WaterBottle"
- }
- ],
- "scene": 0,
- "scenes": [
- {
- "nodes": [
- 0
- ]
- }
- ],
- "textures": [
- {
- "source": 0
- },
- {
- "source": 1
- },
- {
- "source": 2
- },
- {
- "source": 3
- }
- ]
-}
\ No newline at end of file
diff --git a/source/engine/thirdparty/cgltf/fuzz/gltf.dict b/source/engine/thirdparty/cgltf/fuzz/gltf.dict
deleted file mode 100644
index f923c46..0000000
--- a/source/engine/thirdparty/cgltf/fuzz/gltf.dict
+++ /dev/null
@@ -1,224 +0,0 @@
-#
-# AFL dictionary for JSON
-# -----------------------
-#
-# Just the very basics.
-#
-# Inspired by a dictionary by Jakub Wilk
-#
-
-"0"
-",0"
-":0"
-"0:"
-"-1.2e+3"
-
-"true"
-"false"
-"null"
-
-"\"\""
-",\"\""
-":\"\""
-"\"\":"
-
-"{}"
-",{}"
-":{}"
-"{\"\":0}"
-"{{}}"
-
-"[]"
-",[]"
-":[]"
-"[0]"
-"[[]]"
-
-"''"
-"\\"
-"\\b"
-"\\f"
-"\\n"
-"\\r"
-"\\t"
-"\\u0000"
-"\\x00"
-"\\0"
-"\\uD800\\uDC00"
-"\\uDBFF\\uDFFF"
-
-"\"\":0"
-"//"
-"/**/"
-
-#
-# AFL dictionary for GLTF core
-# -----------------------
-
-"5120"
-"5121"
-"5122"
-"5123"
-"5125"
-"5126"
-"\"BLEND\""
-"\"CUBICSPLINE\""
-"\"LINEAR\""
-"\"MASK\""
-"\"MAT2\""
-"\"MAT3\""
-"\"MAT4\""
-"\"OPAQUE\""
-"\"SCALAR\""
-"\"STEP\""
-"\"VEC2\""
-"\"VEC3\""
-"\"VEC4\""
-"\"accessor\""
-"\"accessors\""
-"\"alphaCutoff\""
-"\"alphaMode\""
-"\"animations\""
-"\"aspectRatio\""
-"\"asset\""
-"\"attributes\""
-"\"baseColorFactor\""
-"\"baseColorTexture\""
-"\"bufferView\""
-"\"bufferViews\""
-"\"buffer\""
-"\"buffers\""
-"\"byteLength\""
-"\"byteOffset\""
-"\"byteStride\""
-"\"camera\""
-"\"cameras\""
-"\"channel\""
-"\"channels\""
-"\"children\""
-"\"componentType\""
-"\"copyright\""
-"\"count\""
-"\"doubleSided\""
-"\"emissiveFactor\""
-"\"emissiveTexture\""
-"\"extensionsRequired\""
-"\"extensionsUsed\""
-"\"extensions\""
-"\"extras\""
-"\"generator\""
-"\"image\""
-"\"images\""
-"\"index\""
-"\"indices\""
-"\"input\""
-"\"interpolation\""
-"\"inverseBindMatrices\""
-"\"joints\""
-"\"magFilter\""
-"\"material\""
-"\"materials\""
-"\"matrix\""
-"\"max\""
-"\"mesh\""
-"\"meshes\""
-"\"metallicFactor\""
-"\"metallicRoughnessTexture\""
-"\"mimeType\""
-"\"minFilter\""
-"\"minVersion\""
-"\"min\""
-"\"mode\""
-"\"name\""
-"\"node\""
-"\"nodes\""
-"\"normalTextureInfo\""
-"\"normalTexture\""
-"\"normalized\""
-"\"occlusionTextureInfo\""
-"\"occlusionTexture\""
-"\"orthographic\""
-"\"output\""
-"\"path\""
-"\"pbrMetallicRoughness\""
-"\"perspective\""
-"\"primitive\""
-"\"primitives\""
-"\"rotation\""
-"\"roughnessFactor\""
-"\"sampler\""
-"\"samplers\""
-"\"scale\""
-"\"scene\""
-"\"scenes\""
-"\"skeleton\""
-"\"skin\""
-"\"skins\""
-"\"source\""
-"\"sparse\""
-"\"strength\""
-"\"target\""
-"\"targets\""
-"\"texCoord\""
-"\"textureInfo\""
-"\"texture\""
-"\"textures\""
-"\"translation\""
-"\"type\""
-"\"uri\""
-"\"values\""
-"\"version\""
-"\"weights\""
-"\"wrapS\""
-"\"wrapT\""
-"\"xmag\""
-"\"yfov\""
-"\"ymag\""
-"\"zfar\""
-"\"znear\""
-
-#
-# AFL dictionary for GLTF extensions
-# -----------------------
-"\"KHR_materials_unlit\""
-"\"KHR_texture_basisu\""
-
-"\"KHR_materials_pbrSpecularGlossiness\""
-"\"diffuseFactor\""
-"\"diffuseTexture\""
-"\"specularFactor\""
-"\"glossinessFactor\""
-"\"specularGlossinessTexture\""
-
-"\"KHR_texture_transform\""
-"\"offset\""
-"\"rotation\""
-"\"scale\""
-"\"texCoord\""
-
-"\"KHR_lights_punctual\""
-"\"color\""
-"\"intensity\""
-"\"type\""
-"\"range\""
-"\"innerConeAngle\""
-"\"outerConeAngle\""
-
-"\"KHR_materials_transmission\""
-"\"transmissionFactor\""
-"\"transmissionTexture\""
-
-"\"KHR_materials_volume\""
-"\"thicknessFactor\""
-"\"thicknessTexture\""
-"\"attenuationColor\""
-"\"attenuationDistance\""
-
-"\"KHR_materials_sheen\""
-"\"sheenColorFactor\""
-"\"sheenColorTexture\""
-"\"sheenRoughnessFactor\""
-"\"sheenRoughnessTexture\""
-
-"\"KHR_materials_emissive_strength\""
-"\"emissiveStrength"\""
diff --git a/source/engine/thirdparty/cgltf/fuzz/main.c b/source/engine/thirdparty/cgltf/fuzz/main.c
deleted file mode 100644
index e3b75d1..0000000
--- a/source/engine/thirdparty/cgltf/fuzz/main.c
+++ /dev/null
@@ -1,22 +0,0 @@
-/* How to fuzz:
-
-clang main.c -O2 -g -fsanitize=address,fuzzer -o fuzz
-cp -r data temp
-./fuzz temp/ -dict=gltf.dict -jobs=12 -workers=12
-
-*/
-#define CGLTF_IMPLEMENTATION
-#include "../cgltf.h"
-
-int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
-{
- cgltf_options options = {cgltf_file_type_invalid};
- cgltf_data* data = NULL;
- cgltf_result res = cgltf_parse(&options, Data, Size, &data);
- if (res == cgltf_result_success)
- {
- cgltf_validate(data);
- cgltf_free(data);
- }
- return 0;
-}
diff --git a/source/engine/thirdparty/cgltf/test/CMakeLists.txt b/source/engine/thirdparty/cgltf/test/CMakeLists.txt
deleted file mode 100644
index aa31730..0000000
--- a/source/engine/thirdparty/cgltf/test/CMakeLists.txt
+++ /dev/null
@@ -1,68 +0,0 @@
-cmake_minimum_required( VERSION 2.8 )
-
-include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
-
-set( EXE_NAME cgltf_test )
-add_executable( ${EXE_NAME} main.c )
-set_property( TARGET ${EXE_NAME} PROPERTY C_STANDARD 99 )
-if(MSVC)
- target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
- add_definitions( -D_CRT_SECURE_NO_WARNINGS)
-else()
- target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
- target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
- target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
-endif()
-install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
-
-set( EXE_NAME test_conversion )
-add_executable( ${EXE_NAME} test_conversion.cpp )
-set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
-if(MSVC)
- target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
- add_definitions( -D_CRT_SECURE_NO_WARNINGS)
-else()
- target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
- target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
- target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
-endif()
-install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
-
-set( EXE_NAME test_write )
-add_executable( ${EXE_NAME} test_write.cpp )
-set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
-if(MSVC)
- target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
- add_definitions( -D_CRT_SECURE_NO_WARNINGS)
-else()
- target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
- target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
- target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
-endif()
-install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
-
-set( EXE_NAME test_math )
-add_executable( ${EXE_NAME} test_math.cpp )
-set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
-if(MSVC)
- target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
- add_definitions( -D_CRT_SECURE_NO_WARNINGS)
-else()
- target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
- target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
- target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
-endif()
-install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
-
-set( EXE_NAME test_strings )
-add_executable( ${EXE_NAME} test_strings.cpp )
-set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
-if(MSVC)
- target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
- add_definitions( -D_CRT_SECURE_NO_WARNINGS)
-else()
- target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
- target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
- target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
-endif()
-install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
diff --git a/source/engine/thirdparty/cgltf/test/main.c b/source/engine/thirdparty/cgltf/test/main.c
deleted file mode 100644
index 75543c1..0000000
--- a/source/engine/thirdparty/cgltf/test/main.c
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-#define CGLTF_IMPLEMENTATION
-#include "../cgltf.h"
-
-#include
-
-int main(int argc, char** argv)
-{
- if (argc < 2)
- {
- printf("err\n");
- return -1;
- }
-
- cgltf_options options;
- memset(&options, 0, sizeof(cgltf_options));
- cgltf_data* data = NULL;
- cgltf_result result = cgltf_parse_file(&options, argv[1], &data);
-
- if (result == cgltf_result_success)
- result = cgltf_load_buffers(&options, data, argv[1]);
-
- if (result == cgltf_result_success)
- result = cgltf_validate(data);
-
- printf("Result: %d\n", result);
-
- if (result == cgltf_result_success)
- {
- printf("Type: %u\n", data->file_type);
- printf("Meshes: %u\n", (unsigned)data->meshes_count);
- }
-
- cgltf_free(data);
-
- return result;
-}
diff --git a/source/engine/thirdparty/cgltf/test/test_all.py b/source/engine/thirdparty/cgltf/test/test_all.py
deleted file mode 100755
index ec9b9f0..0000000
--- a/source/engine/thirdparty/cgltf/test/test_all.py
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/usr/bin/env python
-
-import os, sys
-from sys import platform
-
-num_tested = 0
-num_errors = 0
-
-def get_executable_path(name):
- if platform == "win32":
- return "build\\Debug\\" + name
- else:
- return "build/" + name
-
-def is_ascii(s):
- return all(ord(c) < 128 for c in s)
-
-def collect_files(path, type, name):
- global num_tested
- global num_errors
- exe = get_executable_path(name)
- for the_file in os.listdir(path):
- file_path = os.path.join(os.path.normpath(path), the_file)
- if os.path.isfile(file_path):
- if the_file.endswith(type):
- num_tested = num_tested +1
- if is_ascii(file_path):
- print("### " + name + " " + file_path)
- result = os.system("{0} \"{1}\"".format(exe, file_path))
- print("### Result: " + str(result) + "\n")
- if result != 0:
- num_errors = num_errors + 1
- print("Error.")
- sys.exit(1)
- elif os.path.isdir(file_path):
- collect_files(file_path, type, name)
-
-if __name__ == "__main__":
- if not os.path.exists("build/"):
- os.makedirs("build/")
- os.chdir("build/")
- os.system("cmake ..")
- if os.system("cmake --build .") != 0:
- print("Unable to build.")
- exit(1)
- os.chdir("..")
- if not os.path.exists("glTF-Sample-Models/"):
- os.system("git init glTF-Sample-Models")
- os.chdir("glTF-Sample-Models")
- os.system("git remote add origin https://github.com/KhronosGroup/glTF-Sample-Models.git")
- os.system("git config core.sparsecheckout true")
- f = open(".git/info/sparse-checkout", "w+")
- f.write("2.0/*\n")
- f.close()
- os.system("git pull --depth=1 origin master")
- os.chdir("..")
- collect_files("glTF-Sample-Models/2.0/", ".glb", "cgltf_test")
- collect_files("glTF-Sample-Models/2.0/", ".gltf", "cgltf_test")
- collect_files("glTF-Sample-Models/2.0/", ".glb", "test_conversion")
- collect_files("glTF-Sample-Models/2.0/", ".gltf", "test_conversion")
- collect_files("glTF-Sample-Models/2.0/", ".gltf", "test_write")
-
- result = os.system(get_executable_path("test_math"))
- if result != 0:
- num_errors = num_errors + 1
- print("Error.")
- sys.exit(1)
-
- result = os.system(get_executable_path("test_strings"))
- if result != 0:
- num_errors = num_errors + 1
- print("Error.")
- sys.exit(1)
-
- print("Tested files: " + str(num_tested))
- print("Errors: " + str(num_errors))
diff --git a/source/engine/thirdparty/cgltf/test/test_conversion.cpp b/source/engine/thirdparty/cgltf/test/test_conversion.cpp
deleted file mode 100644
index e5217f3..0000000
--- a/source/engine/thirdparty/cgltf/test/test_conversion.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-#define CGLTF_IMPLEMENTATION
-#include "../cgltf.h"
-
-#include
-#include
-#include
-#include
-
-static bool is_near(cgltf_float a, cgltf_float b)
-{
- return std::abs(a - b) < 10 * std::numeric_limits::min();
-}
-
-int main(int argc, char** argv)
-{
- if (argc < 2)
- {
- printf("err\n");
- return -1;
- }
-
- cgltf_options options = {};
- cgltf_data* data = NULL;
- cgltf_result result = cgltf_parse_file(&options, argv[1], &data);
-
- if (result == cgltf_result_success)
- result = cgltf_load_buffers(&options, data, argv[1]);
-
- if (strstr(argv[1], "Draco"))
- {
- cgltf_free(data);
- return 0;
- }
-
- if (result != cgltf_result_success)
- return result;
-
- //const cgltf_accessor* blobs = data->accessors;
- cgltf_float element_float[16];
- cgltf_uint element_uint[4];
- for (cgltf_size blob_index = 0; blob_index < data->accessors_count; ++blob_index)
- {
- const cgltf_accessor* blob = data->accessors + blob_index;
- if (blob->is_sparse)
- {
- cgltf_size nfloats = cgltf_num_components(blob->type) * blob->count;
- cgltf_float* dense = (cgltf_float*) malloc(nfloats * sizeof(cgltf_float));
- if (cgltf_accessor_unpack_floats(blob, dense, nfloats) < nfloats) {
- printf("Unable to completely unpack a sparse accessor.\n");
- return -1;
- }
- free(dense);
- continue;
- }
- if (blob->has_max && blob->has_min)
- {
- cgltf_float min0 = std::numeric_limits::max();
- cgltf_float max0 = std::numeric_limits::lowest();
- for (cgltf_size index = 0; index < blob->count; index++)
- {
- cgltf_accessor_read_float(blob, index, element_float, 16);
- min0 = std::min(min0, element_float[0]);
- max0 = std::max(max0, element_float[0]);
- }
- if (!is_near(min0, blob->min[0]) || !is_near(max0, blob->max[0]))
- {
- printf("Computed [%f, %f] but expected [%f, %f]\n", min0, max0, blob->min[0], blob->max[0]);
- return -1;
- }
- }
- if (blob->has_max && blob->has_min && blob->component_type != cgltf_component_type_r_32f && blob->component_type != cgltf_component_type_invalid)
- {
- cgltf_uint min0 = std::numeric_limits::max();
- cgltf_uint max0 = std::numeric_limits::lowest();
- for ( cgltf_size index = 0; index < blob->count; index++ )
- {
- cgltf_accessor_read_uint( blob, index, element_uint, 4 );
- min0 = std::min( min0, element_uint[0] );
- max0 = std::max( max0, element_uint[0] );
- }
- if ( min0 != (unsigned int) blob->min[0] || max0 != (unsigned int) blob->max[0] )
- {
- printf( "Computed [%u, %u] but expected [%u, %u]\n", min0, max0, (unsigned int) blob->min[0], (unsigned int) blob->max[0] );
- return -1;
- }
- }
- }
-
- cgltf_free(data);
-
- return result;
-}
diff --git a/source/engine/thirdparty/cgltf/test/test_math.cpp b/source/engine/thirdparty/cgltf/test/test_math.cpp
deleted file mode 100644
index 84f9305..0000000
--- a/source/engine/thirdparty/cgltf/test/test_math.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-#define CGLTF_IMPLEMENTATION
-#include "../cgltf.h"
-
-// Performs matrix-vector multiplication, as in (4x4) * (4x1) = (4x1)
-static void transform(const cgltf_float matrix[16], const cgltf_float source[4], cgltf_float target[4]) {
- target[0] = matrix[0] * source[0] + matrix[4] * source[1] + matrix[ 8] * source[2] + matrix[12] * source[3];
- target[1] = matrix[1] * source[0] + matrix[5] * source[1] + matrix[ 9] * source[2] + matrix[13] * source[3];
- target[2] = matrix[2] * source[0] + matrix[6] * source[1] + matrix[10] * source[2] + matrix[14] * source[3];
- target[3] = matrix[3] * source[0] + matrix[7] * source[1] + matrix[11] * source[2] + matrix[15] * source[3];
-}
-
-static void set(cgltf_float target[3], float x, float y, float z) {
- target[0] = x;
- target[1] = y;
- target[2] = z;
-}
-
-static void check(cgltf_float target[3], float x, float y, float z) {
- if (target[0] != x || target[1] != y || target[2] != z) {
- fprintf(stderr, "Mismatch detected.\n");
- exit(1);
- }
-}
-
-int main(int, char**)
-{
- cgltf_node node = {};
-
- cgltf_float matrix[16];
- cgltf_float source[4] = {1, 2, 3, 1};
- cgltf_float target[4];
-
- set(node.scale, 1, 1, 1);
- set(node.translation, 1, 0, 0);
- cgltf_node_transform_local(&node, matrix);
- transform(matrix, source, target);
- check(target, 2, 2, 3);
-
- set(node.scale, 3, 1, 1);
- set(node.translation, 0, 0, 0);
- cgltf_node_transform_local(&node, matrix);
- transform(matrix, source, target);
- check(target, 3, 2, 3);
-
- set(node.scale, 1, 3, 1);
- set(node.translation, 1, 0, 0);
- cgltf_node_transform_local(&node, matrix);
- transform(matrix, source, target);
- check(target, 2, 6, 3);
-
- return 0;
-}
diff --git a/source/engine/thirdparty/cgltf/test/test_strings.cpp b/source/engine/thirdparty/cgltf/test/test_strings.cpp
deleted file mode 100644
index 3d63efc..0000000
--- a/source/engine/thirdparty/cgltf/test/test_strings.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-#define CGLTF_IMPLEMENTATION
-#include "../cgltf.h"
-
-#include
-
-static void check(const char* a, const char* b, cgltf_size size) {
- if (strcmp(a, b) != 0 || strlen(a) != size) {
- fprintf(stderr, "Mismatch detected.\n");
- exit(1);
- }
-}
-
-int main(int, char**)
-{
- char string[64];
- cgltf_size size;
-
- // cgltf_decode_string
- strcpy(string, "");
- size = cgltf_decode_string(string);
- check(string, "", size);
-
- strcpy(string, "nothing to replace");
- size = cgltf_decode_string(string);
- check(string, "nothing to replace", size);
-
- strcpy(string, "\\\" \\/ \\\\ \\b \\f \\r \\n \\t \\u0030");
- size = cgltf_decode_string(string);
- check(string, "\" / \\ \b \f \r \n \t 0", size);
-
- strcpy(string, "test \\u121b\\u130d\\u1294\\u1276\\u127d test");
- size = cgltf_decode_string(string);
- check(string, "test ማግኔቶች test", size);
-
- // cgltf_decode_uri
- strcpy(string, "");
- size = cgltf_decode_uri(string);
- check(string, "", size);
-
- strcpy(string, "nothing to replace");
- size = cgltf_decode_uri(string);
- check(string, "nothing to replace", size);
-
- strcpy(string, "%2F%D0%BA%D0%B8%D1%80%D0%B8%D0%BB%D0%BB%D0%B8%D1%86%D0%B0");
- size = cgltf_decode_uri(string);
- check(string, "/кириллица", size);
-
- strcpy(string, "test%20%E1%88%9B%E1%8C%8D%E1%8A%94%E1%89%B6%E1%89%BD%20test");
- size = cgltf_decode_uri(string);
- check(string, "test ማግኔቶች test", size);
-
- strcpy(string, "%%2F%X%AX%%2F%%");
- size = cgltf_decode_uri(string);
- check(string, "%/%X%AX%/%%", size);
-
- return 0;
-}
diff --git a/source/engine/thirdparty/cgltf/test/test_write.cpp b/source/engine/thirdparty/cgltf/test/test_write.cpp
deleted file mode 100644
index b7ee25d..0000000
--- a/source/engine/thirdparty/cgltf/test/test_write.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-#define CGLTF_IMPLEMENTATION
-#define CGLTF_WRITE_IMPLEMENTATION
-#include "../cgltf_write.h"
-
-#include
-#include
-#include
-#include
-
-int main(int argc, char** argv)
-{
- if (argc < 2)
- {
- printf("err\n");
- return -1;
- }
-
- cgltf_options options = {};
- cgltf_data* data0 = NULL;
- cgltf_result result = cgltf_parse_file(&options, argv[1], &data0);
-
- // Silently skip over files that are unreadable since this is a writing test.
- if (result != cgltf_result_success)
- {
- return cgltf_result_success;
- }
-
- result = cgltf_write_file(&options, "out.gltf", data0);
- if (result != cgltf_result_success)
- {
- return result;
- }
- cgltf_data* data1 = NULL;
- result = cgltf_parse_file(&options, "out.gltf", &data1);
- if (result != cgltf_result_success)
- {
- return result;
- }
- if (data0->meshes_count != data1->meshes_count) {
- return -1;
- }
- cgltf_free(data1);
- cgltf_free(data0);
- return cgltf_result_success;
-}
diff --git a/source/engine/thirdparty/dr_libs/.github/FUNDING.yml b/source/engine/thirdparty/dr_libs/.github/FUNDING.yml
deleted file mode 100644
index fb80301..0000000
--- a/source/engine/thirdparty/dr_libs/.github/FUNDING.yml
+++ /dev/null
@@ -1 +0,0 @@
-github: mackron
diff --git a/source/engine/thirdparty/dr_libs/.gitignore b/source/engine/thirdparty/dr_libs/.gitignore
deleted file mode 100644
index b31d733..0000000
--- a/source/engine/thirdparty/dr_libs/.gitignore
+++ /dev/null
@@ -1,12 +0,0 @@
-docs
-tests/testvectors/opus/*
-!tests/testvectors/opus/DO_NOT_DELETE.md
-tests/bin/*
-!tests/bin/DO_NOT_DELETE.md
-tests/build
-tests/flac/include
-tests/flac/lib
-tests/testvectors/flac/tests/*
-!tests/testvectors/flac/tests/DO_NOT_DELETE.md
-todo
-_private
\ No newline at end of file
diff --git a/source/engine/thirdparty/dr_libs/.gitmodules b/source/engine/thirdparty/dr_libs/.gitmodules
deleted file mode 100644
index c4d4547..0000000
--- a/source/engine/thirdparty/dr_libs/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "tests/external/miniaudio"]
- path = tests/external/miniaudio
- url = https://github.com/dr-soft/miniaudio.git
diff --git a/source/engine/thirdparty/dr_libs/old/dr.h b/source/engine/thirdparty/dr_libs/old/dr.h
deleted file mode 100644
index fb13f95..0000000
--- a/source/engine/thirdparty/dr_libs/old/dr.h
+++ /dev/null
@@ -1,4755 +0,0 @@
-// Public Domain. See "unlicense" statement at the end of this file.
-
-// USAGE
-//
-// This is a single-file library. To use it, do something like the following in one .c file.
-// #define DR_IMPLEMENTATION
-// #include "dr.h"
-//
-// You can then #include dr.h in other parts of the program as you would with any other header file.
-//
-//
-//
-// OPTIONS
-//
-// #define DR_UTIL_WIN32_USE_CRITICAL_SECTION_MUTEX
-// - Use the Win32 CRITICAL_SECTION API for mutex objects.
-
-#ifndef dr_util_h
-#define dr_util_h
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-// Disable MSVC compatibility if we're compiling with it.
-#if defined(_MSC_VER) || defined(__MINGW32__)
- #define DR_NO_MSVC_COMPAT
-#endif
-
-#if defined(_MSC_VER)
-#define DR_INLINE static __inline
-#else
-#define DR_INLINE static inline
-#endif
-
-
-#include
-#include
-#include
-#include
-
-#ifndef DR_NO_MSVC_COMPAT
-#include
-#endif
-
-#ifndef DR_SIZED_TYPES_DEFINED
-#define DR_SIZED_TYPES_DEFINED
-#if defined(_MSC_VER) && _MSC_VER < 1600
-typedef signed char dr_int8;
-typedef unsigned char dr_uint8;
-typedef signed short dr_int16;
-typedef unsigned short dr_uint16;
-typedef signed int dr_int32;
-typedef unsigned int dr_uint32;
-typedef signed __int64 dr_int64;
-typedef unsigned __int64 dr_uint64;
-#else
-#include
-typedef int8_t dr_int8;
-typedef uint8_t dr_uint8;
-typedef int16_t dr_int16;
-typedef uint16_t dr_uint16;
-typedef int32_t dr_int32;
-typedef uint32_t dr_uint32;
-typedef int64_t dr_int64;
-typedef uint64_t dr_uint64;
-#endif
-typedef dr_uint8 dr_bool8;
-typedef dr_uint32 dr_bool32;
-#define DR_TRUE 1
-#define DR_FALSE 0
-#endif
-
-
-#define STRINGIFY(x) #x
-#define TOSTRING(x) STRINGIFY(x)
-
-
-/////////////////////////////////////////////////////////
-// Annotations
-
-#ifndef IN
-#define IN
-#endif
-
-#ifndef OUT
-#define OUT
-#endif
-
-#ifndef UNUSED
-#define UNUSED(x) ((void)(x))
-#endif
-
-
-/////////////////////////////////////////////////////////
-// min/max/clamp
-
-#ifndef dr_min
-#define dr_min(x, y) (((x) < (y)) ? (x) : (y))
-#endif
-
-#ifndef dr_max
-#define dr_max(x, y) (((x) > (y)) ? (x) : (y))
-#endif
-
-#ifndef dr_clamp
-#define dr_clamp(x, low, high) (dr_max(low, dr_min(x, high)))
-#endif
-
-#ifndef dr_round_up
-#define dr_round_up(x, multiple) ((((x) + ((multiple) - 1)) / (multiple)) * (multiple))
-#endif
-
-#ifndef dr_round_up_signed
-#define dr_round_up_signed(x, multiple) ((((x) + (((x) >= 0)*((multiple) - 1))) / (multiple)) * (multiple))
-#endif
-
-DR_INLINE dr_uint32 dr_next_power_of_2(dr_uint32 value)
-{
- --value;
- value = (value >> 1) | value;
- value = (value >> 2) | value;
- value = (value >> 4) | value;
- value = (value >> 8) | value;
- value = (value >> 16) | value;
- return value + 1;
-}
-
-
-#define dr_abs(x) (((x) < 0) ? (-(x)) : (x))
-
-
-/////////////////////////////////////////////////////////
-// MSVC Compatibility
-
-// A basic implementation of MSVC's strcpy_s().
-int dr_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src);
-
-// A basic implementation of MSVC's strncpy_s().
-int dr_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count);
-
-// A basic implementation of MSVC's strcat_s().
-int dr_strcat_s(char* dst, size_t dstSizeInBytes, const char* src);
-
-// A basic implementation of MSVC's strncat_s()
-int dr_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count);
-
-// A basic implementation of MSVC's _atoi_s()
-int dr_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix);
-
-#ifndef DR_NO_MSVC_COMPAT
-#ifndef _TRUNCATE
-#define _TRUNCATE ((size_t)-1)
-#endif
-
-DR_INLINE int strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
-{
- return dr_strcpy_s(dst, dstSizeInBytes, src);
-}
-
-DR_INLINE int strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
-{
- return dr_strncpy_s(dst, dstSizeInBytes, src, count);
-}
-
-DR_INLINE int strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
-{
- return dr_strcat_s(dst, dstSizeInBytes, src);
-}
-
-DR_INLINE int strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
-{
- return dr_strncat_s(dst, dstSizeInBytes, src, count);
-}
-
-#ifndef __MINGW32__
-DR_INLINE int _stricmp(const char* string1, const char* string2)
-{
- return strcasecmp(string1, string2);
-}
-#endif
-
-DR_INLINE int _itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix)
-{
- return dr_itoa_s(value, dst, dstSizeInBytes, radix);
-}
-#endif
-
-
-/////////////////////////////////////////////////////////
-// String Helpers
-
-// Determines if the given character is whitespace.
-dr_bool32 dr_is_whitespace(dr_uint32 utf32);
-
-/// Removes every occurance of the given character from the given string.
-void dr_strrmchar(char* str, char c);
-
-/// Finds the first non-whitespace character in the given string.
-const char* dr_first_non_whitespace(const char* str);
-
-static inline const char* dr_ltrim(const char* str) { return dr_first_non_whitespace(str); }
-static const char* dr_rtrim(const char* str);
-
-/// Trims both the leading and trailing whitespace from the given string.
-void dr_trim(char* str);
-
-/// Finds the first occurance of a whitespace character in the given string.
-const char* dr_first_whitespace(const char* str);
-
-/// Finds the beginning of the next line.
-const char* dr_next_line(const char* str);
-
-/// Makes a copy of the first line of the given string.
-size_t dr_copy_line(const char* str, char* lineOut, size_t lineOutSize);
-
-// A slow string replace function. Free the returned string with free().
-char* dr_string_replace(const char* src, const char* query, const char* replacement);
-
-// Replaces an ASCII character with another in the given string.
-void dr_string_replace_ascii(char* src, char c, char replacement);
-
-
-/////////////////////////////////////////////////////////
-// Unicode Utilities
-
-/// Converts a UTF-32 character to UTF-16.
-///
-/// @param utf16 [in] A pointer to an array of at least two 16-bit values that will receive the UTF-16 character.
-///
-/// @return 2 if the returned character is a surrogate pair, 1 if it's a simple UTF-16 code point, or 0 if it's an invalid character.
-///
-/// @remarks
-/// It is assumed the is large enough to hold at least 2 unsigned shorts. will be padded with 0 for unused
-/// components.
-DR_INLINE int dr_utf32_to_utf16_ch(unsigned int utf32, unsigned short utf16[2])
-{
- if (utf16 == NULL) {
- return 0;
- }
-
- if (utf32 < 0xD800 || (utf32 >= 0xE000 && utf32 <= 0xFFFF))
- {
- utf16[0] = (unsigned short)utf32;
- utf16[1] = 0;
- return 1;
- }
- else
- {
- if (utf32 >= 0x10000 && utf32 <= 0x10FFFF)
- {
- utf16[0] = (unsigned short)(0xD7C0 + (unsigned short)(utf32 >> 10));
- utf16[1] = (unsigned short)(0xDC00 + (unsigned short)(utf32 & 0x3FF));
- return 2;
- }
- else
- {
- // Invalid.
- utf16[0] = 0;
- utf16[1] = 0;
- return 0;
- }
- }
-}
-
-/// Converts a UTF-16 character to UTF-32.
-DR_INLINE unsigned int dr_utf16_to_utf32_ch(unsigned short utf16[2])
-{
- if (utf16 == NULL) {
- return 0;
- }
-
- if (utf16[0] < 0xD800 || utf16[0] > 0xDFFF)
- {
- return utf16[0];
- }
- else
- {
- if ((utf16[0] & 0xFC00) == 0xD800 && (utf16[1] & 0xFC00) == 0xDC00)
- {
- return ((unsigned int)utf16[0] << 10) + utf16[1] - 0x35FDC00;
- }
- else
- {
- // Invalid.
- return 0;
- }
- }
-}
-
-/// Converts a UTF-16 surrogate pair to UTF-32.
-DR_INLINE unsigned int dr_utf16pair_to_utf32_ch(unsigned short utf160, unsigned short utf161)
-{
- unsigned short utf16[2];
- utf16[0] = utf160;
- utf16[1] = utf161;
- return dr_utf16_to_utf32_ch(utf16);
-}
-
-
-/////////////////////////////////////////////////////////
-// Aligned Allocations
-
-#ifndef DRUTIL_NO_ALIGNED_MALLOC
-DR_INLINE void* dr_aligned_malloc(size_t alignment, size_t size)
-{
-#if defined(_WIN32) || defined(_WIN64)
- return _aligned_malloc(size, alignment);
-#else
- void* pResult;
- if (posix_memalign(&pResult, alignment, size) == 0) {
- return pResult;
- }
-
- return 0;
-#endif
-}
-
-DR_INLINE void dr_aligned_free(void* ptr)
-{
-#if defined(_WIN32) || defined(_WIN64)
- _aligned_free(ptr);
-#else
- free(ptr);
-#endif
-}
-#endif // !DRUTIL_NO_ALIGNED_MALLOC
-
-
-
-/////////////////////////////////////////////////////////
-// Key/Value Pair Parsing
-
-typedef size_t (* dr_key_value_read_proc) (void* pUserData, void* pDataOut, size_t bytesToRead);
-typedef void (* dr_key_value_pair_proc) (void* pUserData, const char* key, const char* value);
-typedef void (* dr_key_value_error_proc)(void* pUserData, const char* message, unsigned int line);
-
-/// Parses a series of simple Key/Value pairs.
-///
-/// @remarks
-/// This function is suitable for parsing simple key/value config files.
-/// @par
-/// This function will never allocate memory on the heap. Because of this there is a minor restriction in the length of an individual
-/// key/value pair which is 4KB.
-/// @par
-/// Formatting rules are as follows:
-/// - The basic syntax for a key/value pair is [key][whitespace][value]. Example: MyProperty 1234
-/// - All key/value pairs must be declared on a single line, and a single line cannot contain more than a single key/value pair.
-/// - Comments begin with the '#' character and continue until the end of the line.
-/// - A key cannot contain spaces but are permitted in values.
-/// - The value will have any leading and trailing whitespace trimmed.
-/// @par
-/// If an error occurs, that line will be skipped and processing will continue.
-void dr_parse_key_value_pairs(dr_key_value_read_proc onRead, dr_key_value_pair_proc onPair, dr_key_value_error_proc onError, void* pUserData);
-
-// This will only return DR_FALSE if the file fails to open. It will still return DR_TRUE even if there are syntax error or whatnot.
-dr_bool32 dr_parse_key_value_pairs_from_file(const char* filePath, dr_key_value_pair_proc onPair, dr_key_value_error_proc onError, void* pUserData);
-
-
-
-/////////////////////////////////////////////////////////
-// Basic Tokenizer
-
-/// Retrieves the first token in the given string.
-///
-/// @remarks
-/// This function is suitable for doing a simple whitespace tokenization of a null-terminated string.
-/// @par
-/// The return value is a pointer to one character past the last character of the next token. You can use the return value to execute
-/// this function in a loop to parse an entire string.
-/// @par
-/// can be null. If the buffer is too small to contain the entire token it will be set to an empty string. The original
-/// input string combined with the return value can be used to reliably find the token.
-/// @par
-/// This will handle double-quoted strings, so a string such as "My \"Complex String\"" contains two tokens: "My" and "\"Complex String\"".
-/// @par
-/// This function has no dependencies.
-const char* dr_next_token(const char* tokens, char* tokenOut, size_t tokenOutSize);
-
-
-
-/////////////////////////////////////////////////////////
-// Known Folders
-
-/// Retrieves the path of the executable.
-///
-/// @remarks
-/// Currently only works on Windows and Linux. Other platforms will be added as they're needed.
-dr_bool32 dr_get_executable_path(char* pathOut, size_t pathOutSize);
-
-/// Retrieves the path of the directory containing the executable.
-///
-/// @remarks
-/// Currently only works on Windows and Linux. Other platforms will be added as they're needed.
-dr_bool32 dr_get_executable_directory_path(char* pathOut, size_t pathOutSize);
-
-/// Retrieves the path of the user's config directory.
-///
-/// @remarks
-/// On Windows this will typically be %APPDATA% and on Linux it will usually be ~/.config
-dr_bool32 dr_get_config_folder_path(char* pathOut, size_t pathOutSize);
-
-/// Retrieves the path of the user's log directory.
-///
-/// @remarks
-/// On Windows this will typically be %APPDATA% and on Linux it will usually be var/log
-dr_bool32 dr_get_log_folder_path(char* pathOut, size_t pathOutSize);
-
-/// Retrieves the current directory.
-const char* dr_get_current_directory(char* pathOut, size_t pathOutSize);
-
-/// Sets the current directory.
-dr_bool32 dr_set_current_directory(const char* path);
-
-
-
-/////////////////////////////////////////////////////////
-// Basic File Management
-
-// Callback function for file iteration.
-typedef dr_bool32 (* dr_iterate_files_proc)(const char* filePath, void* pUserData);
-
-
-// Helper for opening a stdio FILE.
-FILE* dr_fopen(const char* fileName, const char* openMode);
-
-// Helper for creating an empty file.
-dr_bool32 dr_create_empty_file(const char* fileName, dr_bool32 failIfExists);
-
-// Retrieves the file data of the given file. Free the returned pointer with dr_free_file_data().
-void* dr_open_and_read_file(const char* filePath, size_t* pFileSizeOut);
-
-// Retrieves the file data of the given file as a null terminated string. Free the returned pointer with dr_free_file_data(). The
-// returned file size is the length of the string not including the null terminator.
-char* dr_open_and_read_text_file(const char* filePath, size_t* pFileSizeOut);
-
-// Creates a new file with the given data.
-dr_bool32 dr_open_and_write_file(const char* filePath, const void* pData, size_t dataSize);
-
-// Creates a new file with the given string.
-dr_bool32 dr_open_and_write_text_file(const char* filePath, const char* text);
-
-// Frees the file data returned by dr_open_and_read_file().
-void dr_free_file_data(void* valueReturnedByOpenAndReadFile);
-
-// Determines whether or not the given file path is to a file.
-//
-// This will return DR_FALSE if the path points to a directory.
-dr_bool32 dr_file_exists(const char* filePath);
-
-// Determines whether or not the given file path points to a directory.
-//
-// This will return DR_FALSE if the path points to a file.
-dr_bool32 dr_directory_exists(const char* directoryPath);
-static inline dr_bool32 dr_is_directory(const char* directoryPath) { return dr_directory_exists(directoryPath); }
-
-// Moves a file.
-//
-// This uses rename() on POSIX platforms and MoveFileEx(oldPath, newPath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH) on windows platforms.
-dr_bool32 dr_move_file(const char* oldPath, const char* newPath);
-
-// Copies a file.
-dr_bool32 dr_copy_file(const char* srcPath, const char* dstPath, dr_bool32 failIfExists);
-
-// Determines if the given file is read only.
-dr_bool32 dr_is_file_read_only(const char* filePath);
-
-// Retrieves the last modified time of the file at the given path.
-dr_uint64 dr_get_file_modified_time(const char* filePath);
-
-// Deletes the file at the given path.
-//
-// This uses remove() on POSIX platforms and DeleteFile() on Windows platforms.
-dr_bool32 dr_delete_file(const char* filePath);
-
-// Cross-platform wrapper for creating a directory.
-dr_bool32 dr_mkdir(const char* directoryPath);
-
-// Recursively creates a directory.
-dr_bool32 dr_mkdir_recursive(const char* directoryPath);
-
-// Iterates over every file and folder of the given directory.
-dr_bool32 dr_iterate_files(const char* directory, dr_bool32 recursive, dr_iterate_files_proc proc, void* pUserData);
-
-
-/////////////////////////////////////////////////////////
-// DPI Awareness
-
-#if defined(_WIN32)
-/// Win32 Only: Makes the application DPI aware.
-void dr_win32_make_dpi_aware();
-
-/// Win32 Only: Retrieves the base DPI to use as a reference when calculating DPI scaling.
-void dr_win32_get_base_dpi(int* pDPIXOut, int* pDPIYOut);
-
-/// Win32 Only: Retrieves the system-wide DPI.
-void dr_win32_get_system_dpi(int* pDPIXOut, int* pDPIYOut);
-
-/// Win32 Only: Retrieves the actual DPI of the monitor at the given index.
-///
-/// @remarks
-/// If per-monitor DPI is not supported, the system wide DPI settings will be used instead.
-/// @par
-/// This runs in linear time.
-void dr_win32_get_monitor_dpi(int monitor, int* pDPIXOut, int* pDPIYOut);
-
-/// Win32 Only: Retrieves the number of monitors active at the time of calling.
-///
-/// @remarks
-/// This runs in linear time.
-int dr_win32_get_monitor_count();
-#endif
-
-
-
-/////////////////////////////////////////////////////////
-// Date / Time
-
-/// Retrieves a time_t as of the time the function was called.
-time_t dr_now();
-
-/// Formats a data/time string.
-void dr_datetime_short(time_t t, char* strOut, unsigned int strOutSize);
-
-// Returns a date string in YYYYMMDD format.
-void dr_date_YYYYMMDD(time_t t, char* strOut, unsigned int strOutSize);
-
-
-
-/////////////////////////////////////////////////////////
-// Command Line
-//
-// The command line functions below are just simple iteration functions. This command line system is good for
-// simple command lines, but probably not the best for programs requiring complex command line work.
-//
-// For argv style command lines, parse_cmdline() will run without any heap allocations. With a Win32 style
-// command line there will be one malloc() per call fo parse_cmdline(). This is the only function that will do
-// a malloc().
-//
-// Below is an example:
-//
-// dr_cmdline cmdline;
-// if (dr_init_cmdline(&cmdline, argc, argv)) {
-// dr_parse_cmdline(&cmdline, my_cmdline_handler, pMyUserData);
-// }
-//
-// void my_cmdline_handler(const char* key, const char* value, void* pUserData)
-// {
-// // Do something...
-// }
-//
-//
-// When parsing the command line, the first iteration will be the program path and the key will be "[path]".
-//
-// For segments such as "-abcd", the callback will be called for "a", "b", "c", "d" individually, with the
-// value set to NULL.
-//
-// For segments such as "--server", the callback will be called for "server", with the value set to NULL.
-//
-// For segments such as "-f file.txt", the callback will be called with the key set to "f" and the value set
-// to "file.txt".
-//
-// For segments such as "-f file1.txt file2.txt", the callback will be called twice, once for file1.txt and
-// again for file2.txt, with with the key set to "f" in both cases.
-//
-// For segments where there is no leading key, the values will be posted as annonymous (key set to NULL). An example
-// is "my_program.exe file1.txt file2.txt", in which case the first iteration will be the program path, the second iteration
-// will be "file1.txt", with the key set to NULL. The third iteration will be "file2.txt" with the key set to NULL.
-//
-// For segments such as "-abcd file.txt", "a", "b", "c", "d" will be sent with NULL values, and "file.txt" will be
-// posted with a NULL key.
-
-typedef struct dr_cmdline dr_cmdline;
-struct dr_cmdline
-{
- // argv style.
- int argc;
- char** argv;
-
- // Win32 style
- const char* win32;
-};
-
-typedef dr_bool32 dr_cmdline_parse_proc(const char* key, const char* value, void* pUserData);
-
-
-/// Initializes a command line object.
-dr_bool32 dr_init_cmdline(dr_cmdline* pCmdLine, int argc, char** argv);
-
-/// Initializes a command line object using a Win32 style command line.
-dr_bool32 dr_init_cmdline_win32(dr_cmdline* pCmdLine, const char* args);
-
-/// Parses the given command line.
-void dr_parse_cmdline(dr_cmdline* pCmdLine, dr_cmdline_parse_proc callback, void* pUserData);
-
-/// Helper for determining whether or not the given key exists.
-dr_bool32 dr_cmdline_key_exists(dr_cmdline* pCmdLine, const char* key);
-
-// Convers the given command line object to argc/argv style.
-//
-// Returns the argument count. Returns 0 if an error occurs. Free "argvOut" with dr_free_argv().
-int dr_cmdline_to_argv(dr_cmdline* pCmdLine, char*** argvOut);
-
-// Converts a WinMain style command line to argc/argv.
-//
-// Returns the argument count. Returns 0 if an error occurs. Free "argvOut" with dr_free_argv().
-int dr_winmain_to_argv(const char* cmdlineWinMain, char*** argvOut);
-
-// Frees the argc/argv command line that was generated by dr.h
-void dr_free_argv(char** argv);
-
-
-
-
-/////////////////////////////////////////////////////////
-// Threading
-
-/// Puts the calling thread to sleep for approximately the given number of milliseconds.
-///
-/// @remarks
-/// This is not 100% accurate and should be considered an approximation.
-void dr_sleep(unsigned int milliseconds);
-void dr_yield();
-
-/// Retrieves the number of logical cores on system.
-unsigned int dr_get_logical_processor_count();
-
-
-/// Thread.
-typedef void* dr_thread;
-typedef int (* dr_thread_entry_proc)(void* pData);
-
-/// Creates and begins executing a new thread.
-///
-/// @remarks
-/// This will not return until the thread has entered into it's entry point.
-/// @par
-/// Creating a thread should be considered an expensive operation. For high performance, you should create threads
-/// at load time and cache them.
-dr_thread dr_create_thread(dr_thread_entry_proc entryProc, void* pData);
-
-/// Deletes the given thread.
-///
-/// @remarks
-/// This does not actually exit the thread, but rather deletes the memory that was allocated for the thread
-/// object returned by dr_create_thread().
-/// @par
-/// It is usually best to wait for the thread to terminate naturally with dr_wait_thread() before calling
-/// this function, however it is still safe to do something like the following.
-/// @code
-/// dr_delete_thread(dr_create_thread(my_thread_proc, pData))
-/// @endcode
-void dr_delete_thread(dr_thread thread);
-
-/// Waits for the given thread to terminate.
-void dr_wait_thread(dr_thread thread);
-
-/// Helper function for waiting for a thread and then deleting the handle after it has terminated.
-void dr_wait_and_delete_thread(dr_thread thread);
-
-
-
-/// Mutex
-typedef void* dr_mutex;
-
-/// Creates a mutex object.
-///
-/// @remarks
-/// If an error occurs, 0 is returned. Otherwise a handle the size of a pointer is returned.
-dr_mutex dr_create_mutex();
-
-/// Deletes a mutex object.
-void dr_delete_mutex(dr_mutex mutex);
-
-/// Locks the given mutex.
-void dr_lock_mutex(dr_mutex mutex);
-
-/// Unlocks the given mutex.
-void dr_unlock_mutex(dr_mutex mutex);
-
-
-
-/// Semaphore
-typedef void* dr_semaphore;
-
-/// Creates a semaphore object.
-///
-/// @remarks
-/// If an error occurs, 0 is returned. Otherwise a handle the size of a pointer is returned.
-dr_semaphore dr_create_semaphore(int initialValue);
-
-/// Deletes the given semaphore.
-void dr_delete_semaphore(dr_semaphore semaphore);
-
-/// Waits on the given semaphore object and decrements it's counter by one upon returning.
-dr_bool32 dr_wait_semaphore(dr_semaphore semaphore);
-
-/// Releases the given semaphore and increments it's counter by one upon returning.
-dr_bool32 dr_release_semaphore(dr_semaphore semaphore);
-
-
-
-/////////////////////////////////////////////////////////
-// Timing
-
-typedef struct
-{
- dr_int64 counter;
-} dr_timer;
-
-// Initializes a high-resolution timer.
-void dr_timer_init(dr_timer* pTimer);
-
-// Ticks the timer and returns the number of seconds since the previous tick.
-//
-// The maximum return value is about 140 years or so.
-double dr_timer_tick(dr_timer* pTimer);
-
-
-
-
-/////////////////////////////////////////////////////////
-// Random
-
-// Generates a random double between 0 and 1. This is bassed of C standard rand().
-double dr_randd();
-
-// Generates a random float between 0 and 1. This is based off C standard rand().
-float dr_randf();
-
-
-
-/////////////////////////////////////////////////////////
-// User Accounts and Process Management
-
-// Retrieves the user name of the user running the application.
-size_t dr_get_username(char* usernameOut, size_t usernameOutSize);
-
-// Retrieves the ID of the current process.
-unsigned int dr_get_process_id();
-
-
-
-
-/////////////////////////////////////////////////////////
-// Miscellaneous Stuff.
-
-// Helper for clearing the given object to 0.
-#define dr_zero_object(pObject) memset(pObject, 0, sizeof(*pObject));
-
-// Converts an ASCII hex character to it's integral equivalent. Returns DR_FALSE if it's not a valid hex character.
-dr_bool32 dr_hex_char_to_uint(char ascii, unsigned int* out);
-
-
-/////////////////////////////////////////////////////////
-// C++ Specific
-
-#ifdef __cplusplus
-
-// Use this to prevent objects of the given class or struct from being copied. This is also useful for eliminating some
-// compiler warnings.
-//
-// Note for structs - this sets the access mode to private, so place this at the end of the declaration.
-#define NO_COPY(classname) \
- private: \
- classname(const classname &); \
- classname & operator=(const classname &);
-
-
-#ifndef DR_NO_MSVC_COMPAT
-extern "C++"
-{
-
-template
-int strcpy_s(char (&dst)[dstSizeInBytes], const char* src)
-{
- return strcpy_s(dst, dstSizeInBytes, src);
-}
-
-template
-int strncpy_s(char (&dst)[dstSizeInBytes], const char* src, size_t count)
-{
- return strncpy_s(dst, dstSizeInBytes, src, count);
-}
-
-template
-int strcat_s(char (&dst)[dstSizeInBytes], const char* src)
-{
- return strcat_s(dst, dstSizeInBytes, src);
-}
-
-template
-int strncat_s(char (&dst)[dstSizeInBytes], const char* src, size_t count)
-{
- return strncat_s(dst, dstSizeInBytes, src, count);
-}
-
-}
-#endif
-
-#endif
-
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif //dr_util_h
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// IMPLEMENTATION
-//
-///////////////////////////////////////////////////////////////////////////////
-
-#ifdef DR_IMPLEMENTATION
-#include
-#include
-#include
-#include // For memmove()
-#include
-
-#ifdef _WIN32
-#include
-#else
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#endif
-
-int dr_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
-{
- if (dst == 0) {
- return EINVAL;
- }
- if (dstSizeInBytes == 0) {
- return ERANGE;
- }
- if (src == 0) {
- dst[0] = '\0';
- return EINVAL;
- }
-
- size_t i;
- for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
- dst[i] = src[i];
- }
-
- if (i < dstSizeInBytes) {
- dst[i] = '\0';
- return 0;
- }
-
- dst[0] = '\0';
- return ERANGE;
-}
-
-int dr_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
-{
- if (dst == 0) {
- return EINVAL;
- }
- if (dstSizeInBytes == 0) {
- return ERANGE;
- }
- if (src == 0) {
- dst[0] = '\0';
- return EINVAL;
- }
-
- size_t maxcount = count;
- if (count == ((size_t)-1) || count >= dstSizeInBytes) { // -1 = _TRUNCATE
- maxcount = dstSizeInBytes - 1;
- }
-
- size_t i;
- for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
- dst[i] = src[i];
- }
-
- if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
- dst[i] = '\0';
- return 0;
- }
-
- dst[0] = '\0';
- return ERANGE;
-}
-
-int dr_strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
-{
- if (dst == 0) {
- return EINVAL;
- }
- if (dstSizeInBytes == 0) {
- return ERANGE;
- }
- if (src == 0) {
- dst[0] = '\0';
- return EINVAL;
- }
-
- char* dstorig = dst;
-
- while (dstSizeInBytes > 0 && dst[0] != '\0') {
- dst += 1;
- dstSizeInBytes -= 1;
- }
-
- if (dstSizeInBytes == 0) {
- return EINVAL; // Unterminated.
- }
-
-
- while (dstSizeInBytes > 0 && src[0] != '\0') {
- *dst++ = *src++;
- dstSizeInBytes -= 1;
- }
-
- if (dstSizeInBytes > 0) {
- dst[0] = '\0';
- } else {
- dstorig[0] = '\0';
- return ERANGE;
- }
-
- return 0;
-}
-
-int dr_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
-{
- if (dst == 0) {
- return EINVAL;
- }
- if (dstSizeInBytes == 0) {
- return ERANGE;
- }
- if (src == 0) {
- return EINVAL;
- }
-
- char* dstorig = dst;
-
- while (dstSizeInBytes > 0 && dst[0] != '\0') {
- dst += 1;
- dstSizeInBytes -= 1;
- }
-
- if (dstSizeInBytes == 0) {
- return EINVAL; // Unterminated.
- }
-
-
- if (count == ((size_t)-1)) { // _TRUNCATE
- count = dstSizeInBytes - 1;
- }
-
- while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0)
- {
- *dst++ = *src++;
- dstSizeInBytes -= 1;
- count -= 1;
- }
-
- if (dstSizeInBytes > 0) {
- dst[0] = '\0';
- } else {
- dstorig[0] = '\0';
- return ERANGE;
- }
-
- return 0;
-}
-
-int dr_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix)
-{
- if (dst == NULL || dstSizeInBytes == 0) {
- return EINVAL;
- }
- if (radix < 2 || radix > 36) {
- dst[0] = '\0';
- return EINVAL;
- }
-
- int sign = (value < 0 && radix == 10) ? -1 : 1; // The negative sign is only used when the base is 10.
-
- unsigned int valueU;
- if (value < 0) {
- valueU = -value;
- } else {
- valueU = value;
- }
-
- char* dstEnd = dst;
- do
- {
- int remainder = valueU % radix;
- if (remainder > 9) {
- *dstEnd = (char)((remainder - 10) + 'a');
- } else {
- *dstEnd = (char)(remainder + '0');
- }
-
- dstEnd += 1;
- dstSizeInBytes -= 1;
- valueU /= radix;
- } while (dstSizeInBytes > 0 && valueU > 0);
-
- if (dstSizeInBytes == 0) {
- dst[0] = '\0';
- return EINVAL; // Ran out of room in the output buffer.
- }
-
- if (sign < 0) {
- *dstEnd++ = '-';
- dstSizeInBytes -= 1;
- }
-
- if (dstSizeInBytes == 0) {
- dst[0] = '\0';
- return EINVAL; // Ran out of room in the output buffer.
- }
-
- *dstEnd = '\0';
-
-
- // At this point the string will be reversed.
- dstEnd -= 1;
- while (dst < dstEnd) {
- char temp = *dst;
- *dst = *dstEnd;
- *dstEnd = temp;
-
- dst += 1;
- dstEnd -= 1;
- }
-
- return 0;
-}
-
-/////////////////////////////////////////////////////////
-// String Helpers
-
-dr_bool32 dr_is_whitespace(dr_uint32 utf32)
-{
- return utf32 == ' ' || utf32 == '\t' || utf32 == '\n' || utf32 == '\v' || utf32 == '\f' || utf32 == '\r';
-}
-
-void dr_strrmchar(char* str, char c)
-{
- char* src = str;
- char* dst = str;
-
- while (src[0] != '\0')
- {
- dst[0] = src[0];
-
- if (dst[0] != c) {
- dst += 1;
- }
-
- src += 1;
- }
-
- dst[0] = '\0';
-}
-
-const char* dr_first_non_whitespace(const char* str)
-{
- if (str == NULL) {
- return NULL;
- }
-
- while (str[0] != '\0' && !(str[0] != ' ' && str[0] != '\t' && str[0] != '\n' && str[0] != '\v' && str[0] != '\f' && str[0] != '\r')) {
- str += 1;
- }
-
- return str;
-}
-
-const char* dr_first_whitespace(const char* str)
-{
- if (str == NULL) {
- return NULL;
- }
-
- while (str[0] != '\0' && (str[0] != ' ' && str[0] != '\t' && str[0] != '\n' && str[0] != '\v' && str[0] != '\f' && str[0] != '\r')) {
- str += 1;
- }
-
- return str;
-}
-
-const char* dr_rtrim(const char* str)
-{
- if (str == NULL) {
- return NULL;
- }
-
- const char* rstr = str;
- while (str[0] != '\0') {
- if (dr_is_whitespace(str[0])) {
- str += 1;
- continue;
- }
-
- str += 1;
- rstr = str;
- }
-
- return rstr;
-}
-
-void dr_trim(char* str)
-{
- if (str == NULL) {
- return;
- }
-
- const char* lstr = dr_ltrim(str);
- const char* rstr = dr_rtrim(lstr);
-
- if (lstr > str) {
- memmove(str, lstr, rstr-lstr);
- }
-
- str[rstr-lstr] = '\0';
-}
-
-const char* dr_next_line(const char* str)
-{
- if (str == NULL) {
- return NULL;
- }
-
- while (str[0] != '\0' && (str[0] != '\n' && !(str[0] == '\r' && str[1] == '\n'))) {
- str += 1;
- }
-
- if (str[0] == '\0') {
- return NULL;
- }
-
- if (str[0] == '\r') {
- return str + 2;
- }
-
- return str + 1;
-}
-
-size_t dr_copy_line(const char* str, char* lineOut, size_t lineOutSize)
-{
- if (str == NULL) {
- return 0;
- }
-
- if (str == NULL) {
- return 0;
- }
-
- size_t length = 0;
- while (lineOutSize > 0 && str[0] != '\0' && (str[0] != '\n' && !(str[0] == '\r' && str[1] == '\n'))) {
- *lineOut++ = *str++;
- lineOutSize -= 1;
- length += 1;
- }
-
- if (lineOutSize == 0) {
- return 0;
- }
-
- *lineOut = '\0';
- return length;
-}
-
-char* dr_string_replace(const char* src, const char* query, const char* replacement)
-{
- // This function could be improved, but it's good enough for now.
-
- if (src == NULL || query == NULL) {
- return NULL;
- }
-
- if (replacement == NULL) {
- replacement = "";
- }
-
- int queryLen = (int)strlen(query);
- int replacementLen = (int)strlen(replacement);
-
- size_t replacementCount = 0;
-
- const char* temp = src;
- for (;;) {
- temp = strstr(temp, query);
- if (temp == NULL) {
- break;
- }
-
- temp += queryLen;
- replacementCount += 1;
- }
-
-
- char* result = (char*)malloc(strlen(src) + (replacementLen - queryLen)*replacementCount + 1); // +1 for null terminator.
- if (result == NULL) {
- return NULL;
- }
-
- char* runningResult = result;
- for (size_t i = 0; i < replacementCount; ++i) {
- size_t len = strstr(src, query) - src;
- for (size_t j = 0; j < len; ++j) {
- runningResult[j] = src[j];
- }
-
- runningResult += len;
- for (int j = 0; j < replacementLen; ++j) {
- runningResult[j] = replacement[j];
- }
-
- runningResult += replacementLen;
- src += len + queryLen;
- }
-
- // The trailing part.
- strcpy_s(runningResult, strlen(src)+1, src);
- return result;
-}
-
-void dr_string_replace_ascii(char* src, char c, char replacement)
-{
- for (;;) {
- if (*src == '\0') {
- break;
- }
-
- if (*src == c) {
- *src = replacement;
- }
-
- src += 1;
- }
-}
-
-
-/////////////////////////////////////////////////////////
-// Key/Value Pair Parsing
-
-void dr_parse_key_value_pairs(dr_key_value_read_proc onRead, dr_key_value_pair_proc onPair, dr_key_value_error_proc onError, void* pUserData)
-{
- if (onRead == NULL) {
- return;
- }
-
- char pChunk[4096];
- size_t chunkSize = 0;
-
- unsigned int currentLine = 1;
-
- dr_bool32 moveToNextLineBeforeProcessing = DR_FALSE;
- dr_bool32 skipWhitespaceBeforeProcessing = DR_FALSE;
-
- // Just keep looping. We'll break from this loop when we have run out of data.
- for (;;)
- {
- // Start the iteration by reading as much data as we can.
- chunkSize = onRead(pUserData, pChunk, sizeof(pChunk));
- if (chunkSize == 0) {
- // No more data available.
- return;
- }
-
- char* pChunkEnd = pChunk + chunkSize;
- char* pC = pChunk; // Chunk pointer. This is as the chunk is processed.
-
- if (moveToNextLineBeforeProcessing)
- {
- move_to_next_line:
- while (pC < pChunkEnd && pC[0] != '\n') {
- pC += 1;
- }
-
- if (pC == pChunkEnd) {
- // Ran out of data. Load the next chunk and keep going.
- moveToNextLineBeforeProcessing = DR_TRUE;
- continue;
- }
-
- pC += 1; // pC[0] == '\n' - skip past the new line character.
- currentLine += 1;
- moveToNextLineBeforeProcessing = DR_FALSE;
- }
-
- if (skipWhitespaceBeforeProcessing)
- {
- while (pC < pChunkEnd && (pC[0] == ' ' || pC[0] == '\t' || pC[0] == '\r')) {
- pC += 1;
- }
-
- if (pC == pChunkEnd) {
- // Ran out of data.
- skipWhitespaceBeforeProcessing = DR_TRUE;
- continue;
- }
-
- skipWhitespaceBeforeProcessing = DR_FALSE;
- }
-
-
- // We loop character by character. When we run out of data, we start again.
- while (pC < pChunkEnd)
- {
- //// Key ////
-
- // Skip whitespace.
- while (pC < pChunkEnd && (pC[0] == ' ' || pC[0] == '\t' || pC[0] == '\r')) {
- pC += 1;
- }
-
- if (pC == pChunkEnd) {
- // Ran out of data.
- skipWhitespaceBeforeProcessing = DR_TRUE;
- continue;
- }
-
- if (pC[0] == '\n') {
- // Found the end of the line.
- pC += 1;
- currentLine += 1;
- continue;
- }
-
- if (pC[0] == '#') {
- // Found a comment. Move to the end of the line and continue.
- goto move_to_next_line;
- }
-
- char* pK = pC;
- while (pC < pChunkEnd && pC[0] != ' ' && pC[0] != '\t' && pC[0] != '\r' && pC[0] != '\n' && pC[0] != '#') {
- pC += 1;
- }
-
- if (pC == pChunkEnd)
- {
- // Ran out of data. We need to move what we have of the key to the start of the chunk buffer, and then read more data.
- if (chunkSize == sizeof(pChunk))
- {
- size_t lineSizeSoFar = pC - pK;
- memmove(pChunk, pK, lineSizeSoFar);
-
- chunkSize = lineSizeSoFar + onRead(pUserData, pChunk + lineSizeSoFar, sizeof(pChunk) - lineSizeSoFar);
- pChunkEnd = pChunk + chunkSize;
-
- pK = pChunk;
- pC = pChunk + lineSizeSoFar;
- while (pC < pChunkEnd && pC[0] != ' ' && pC[0] != '\t' && pC[0] != '\r' && pC[0] != '\n' && pC[0] != '#') {
- pC += 1;
- }
- }
-
- if (pC == pChunkEnd) {
- if (chunkSize == sizeof(pChunk)) {
- if (onError) {
- onError(pUserData, "Line is too long. A single line cannot exceed 4KB.", currentLine);
- }
-
- goto move_to_next_line;
- } else {
- // No more data. Just treat this one as a value-less key and return.
- if (onPair) {
- pC[0] = '\0';
- onPair(pUserData, pK, NULL);
- }
-
- return;
- }
- }
- }
-
- char* pKEnd = pC;
-
- //// Value ////
-
- // Skip whitespace.
- while (pC < pChunkEnd && (pC[0] == ' ' || pC[0] == '\t' || pC[0] == '\r')) {
- pC += 1;
- }
-
- if (pC == pChunkEnd)
- {
- // Ran out of data. We need to move what we have of the key to the start of the chunk buffer, and then read more data.
- if (chunkSize == sizeof(pChunk))
- {
- size_t lineSizeSoFar = pC - pK;
- memmove(pChunk, pK, lineSizeSoFar);
-
- chunkSize = lineSizeSoFar + onRead(pUserData, pChunk + lineSizeSoFar, sizeof(pChunk) - lineSizeSoFar);
- pChunkEnd = pChunk + chunkSize;
-
- pKEnd = pChunk + (pKEnd - pK);
- pK = pChunk;
- pC = pChunk + lineSizeSoFar;
- while (pC < pChunkEnd && (pC[0] == ' ' || pC[0] == '\t' || pC[0] == '\r')) {
- pC += 1;
- }
- }
-
- if (pC == pChunkEnd) {
- if (chunkSize == sizeof(pChunk)) {
- if (onError) {
- onError(pUserData, "Line is too long. A single line cannot exceed 4KB.", currentLine);
- }
-
- goto move_to_next_line;
- } else {
- // No more data. Just treat this one as a value-less key and return.
- if (onPair) {
- pKEnd[0] = '\0';
- onPair(pUserData, pK, NULL);
- }
-
- return;
- }
- }
- }
-
- if (pC[0] == '\n') {
- // Found the end of the line. Treat it as a value-less key.
- pKEnd[0] = '\0';
- if (onPair) {
- onPair(pUserData, pK, NULL);
- }
-
- pC += 1;
- currentLine += 1;
- continue;
- }
-
- if (pC[0] == '#') {
- // Found a comment. Treat is as a value-less key and move to the end of the line.
- pKEnd[0] = '\0';
- if (onPair) {
- onPair(pUserData, pK, NULL);
- }
-
- goto move_to_next_line;
- }
-
- char* pV = pC;
-
- // Find the last non-whitespace character.
- char* pVEnd = pC;
- while (pC < pChunkEnd && pC[0] != '\n' && pC[0] != '#') {
- if (pC[0] != ' ' && pC[0] != '\t' && pC[0] != '\r') {
- pVEnd = pC;
- }
-
- pC += 1;
- }
-
- if (pC == pChunkEnd)
- {
- // Ran out of data. We need to move what we have of the key to the start of the chunk buffer, and then read more data.
- if (chunkSize == sizeof(pChunk))
- {
- size_t lineSizeSoFar = pC - pK;
- memmove(pChunk, pK, lineSizeSoFar);
-
- chunkSize = lineSizeSoFar + onRead(pUserData, pChunk + lineSizeSoFar, sizeof(pChunk) - lineSizeSoFar);
- pChunkEnd = pChunk + chunkSize;
-
- pVEnd = pChunk + (pVEnd - pK);
- pKEnd = pChunk + (pKEnd - pK);
- pV = pChunk + (pV - pK);
- pK = pChunk;
- pC = pChunk + lineSizeSoFar;
- while (pC < pChunkEnd && pC[0] != '\n' && pC[0] != '#') {
- if (pC[0] != ' ' && pC[0] != '\t' && pC[0] != '\r') {
- pVEnd = pC;
- }
-
- pC += 1;
- }
- }
-
- if (pC == pChunkEnd) {
- if (chunkSize == sizeof(pChunk)) {
- if (onError) {
- onError(pUserData, "Line is too long. A single line cannot exceed 4KB.", currentLine);
- }
-
- goto move_to_next_line;
- }
- }
- }
-
-
- // Before null-terminating the value we first need to determine how we'll proceed after posting onPair.
- dr_bool32 wasOnNL = pVEnd[1] == '\n';
-
- pKEnd[0] = '\0';
- pVEnd[1] = '\0';
- if (onPair) {
- onPair(pUserData, pK, pV);
- }
-
- if (wasOnNL)
- {
- // Was sitting on a new-line character.
- pC += 1;
- currentLine += 1;
- continue;
- }
- else
- {
- // Was sitting on a comment - just to the next line.
- goto move_to_next_line;
- }
- }
- }
-}
-
-
-typedef struct
-{
- FILE* pFile;
- dr_key_value_pair_proc onPair;
- dr_key_value_error_proc onError;
- void* pOriginalUserData;
-} dr_parse_key_value_pairs_from_file_data;
-
-size_t dr_parse_key_value_pairs_from_file__on_read(void* pUserData, void* pDataOut, size_t bytesToRead)
-{
- dr_parse_key_value_pairs_from_file_data* pData = (dr_parse_key_value_pairs_from_file_data*)pUserData;
- assert(pData != NULL);
-
- return fread(pDataOut, 1, bytesToRead, pData->pFile);
-}
-
-void dr_parse_key_value_pairs_from_file__on_pair(void* pUserData, const char* key, const char* value)
-{
- dr_parse_key_value_pairs_from_file_data* pData = (dr_parse_key_value_pairs_from_file_data*)pUserData;
- assert(pData != NULL);
-
- pData->onPair(pData->pOriginalUserData, key, value);
-}
-
-void dr_parse_key_value_pairs_from_file__on_error(void* pUserData, const char* message, unsigned int line)
-{
- dr_parse_key_value_pairs_from_file_data* pData = (dr_parse_key_value_pairs_from_file_data*)pUserData;
- assert(pData != NULL);
-
- pData->onError(pData->pOriginalUserData, message, line);
-}
-
-dr_bool32 dr_parse_key_value_pairs_from_file(const char* filePath, dr_key_value_pair_proc onPair, dr_key_value_error_proc onError, void* pUserData)
-{
- dr_parse_key_value_pairs_from_file_data data;
- data.pFile = dr_fopen(filePath, "rb");
- if (data.pFile == NULL) {
- if (onError) onError(pUserData, "Could not open file.", 0);
- return DR_FALSE;
- }
-
- data.onPair = onPair;
- data.onError = onError;
- data.pOriginalUserData = pUserData;
- dr_parse_key_value_pairs(dr_parse_key_value_pairs_from_file__on_read, dr_parse_key_value_pairs_from_file__on_pair, dr_parse_key_value_pairs_from_file__on_error, &data);
-
- fclose(data.pFile);
- return DR_TRUE;
-}
-
-
-/////////////////////////////////////////////////////////
-// Basic Tokenizer
-
-const char* dr_next_token(const char* tokens, char* tokenOut, size_t tokenOutSize)
-{
- if (tokenOut) tokenOut[0] = '\0';
-
- if (tokens == NULL) {
- return NULL;
- }
-
- // Skip past leading whitespace.
- while (tokens[0] != '\0' && !(tokens[0] != ' ' && tokens[0] != '\t' && tokens[0] != '\n' && tokens[0] != '\v' && tokens[0] != '\f' && tokens[0] != '\r')) {
- tokens += 1;
- }
-
- if (tokens[0] == '\0') {
- return NULL;
- }
-
-
- const char* strBeg = tokens;
- const char* strEnd = strBeg;
-
- if (strEnd[0] == '\"')
- {
- // It's double-quoted - loop until the next unescaped quote character.
-
- // Skip past the first double-quote character.
- strBeg += 1;
- strEnd += 1;
-
- // Keep looping until the next unescaped double-quote character.
- char prevChar = '\0';
- while (strEnd[0] != '\0' && (strEnd[0] != '\"' || prevChar == '\\'))
- {
- prevChar = strEnd[0];
- strEnd += 1;
- }
- }
- else
- {
- // It's not double-quoted - just loop until the first whitespace.
- while (strEnd[0] != '\0' && (strEnd[0] != ' ' && strEnd[0] != '\t' && strEnd[0] != '\n' && strEnd[0] != '\v' && strEnd[0] != '\f' && strEnd[0] != '\r')) {
- strEnd += 1;
- }
- }
-
-
- // If the output buffer is large enough to hold the token, copy the token into it. When we copy the token we need to
- // ensure we don't include the escape character.
- //assert(strEnd >= strBeg);
-
- while (tokenOutSize > 1 && strBeg < strEnd)
- {
- if (strBeg[0] == '\\' && strBeg[1] == '\"' && strBeg < strEnd) {
- strBeg += 1;
- }
-
- *tokenOut++ = *strBeg++;
- tokenOutSize -= 1;
- }
-
- // Null-terminate.
- if (tokenOutSize > 0) {
- *tokenOut = '\0';
- }
-
-
- // Skip past the double-quote character before returning.
- if (strEnd[0] == '\"') {
- strEnd += 1;
- }
-
- return strEnd;
-}
-
-
-
-
-/////////////////////////////////////////////////////////
-// Known Folders
-
-#if defined(_WIN32)
-#if defined(_MSC_VER)
- #pragma warning(push)
- #pragma warning(disable:4091) // 'typedef ': ignored on left of 'tagGPFIDL_FLAGS' when no variable is declared
-#endif
-#include
-#if defined(_MSC_VER)
- #pragma warning(pop)
-#endif
-
-
-dr_bool32 dr_get_executable_path(char* pathOut, size_t pathOutSize)
-{
- if (pathOut == NULL || pathOutSize == 0) {
- return 0;
- }
-
- DWORD length = GetModuleFileNameA(NULL, pathOut, (DWORD)pathOutSize);
- if (length == 0) {
- pathOut[0] = '\0';
- return DR_FALSE;
- }
-
- // Force null termination.
- if (length == pathOutSize) {
- pathOut[length - 1] = '\0';
- }
-
- // Back slashes need to be normalized to forward.
- while (pathOut[0] != '\0') {
- if (pathOut[0] == '\\') {
- pathOut[0] = '/';
- }
-
- pathOut += 1;
- }
-
- return DR_TRUE;
-}
-
-dr_bool32 dr_get_config_folder_path(char* pathOut, size_t pathOutSize)
-{
- // The documentation for SHGetFolderPathA() says that the output path should be the size of MAX_PATH. We'll enforce
- // that just to be safe.
- if (pathOutSize >= MAX_PATH)
- {
- SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, pathOut);
- }
- else
- {
- char pathOutTemp[MAX_PATH];
- SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, pathOutTemp);
-
- if (strcpy_s(pathOut, pathOutSize, pathOutTemp) != 0) {
- return 0;
- }
- }
-
-
- // Back slashes need to be normalized to forward.
- while (pathOut[0] != '\0') {
- if (pathOut[0] == '\\') {
- pathOut[0] = '/';
- }
-
- pathOut += 1;
- }
-
- return 1;
-}
-
-dr_bool32 dr_get_log_folder_path(char* pathOut, size_t pathOutSize)
-{
- return dr_get_config_folder_path(pathOut, pathOutSize);
-}
-
-const char* dr_get_current_directory(char* pathOut, size_t pathOutSize)
-{
- DWORD result = GetCurrentDirectoryA((DWORD)pathOutSize, pathOut);
- if (result == 0) {
- return NULL;
- }
-
- return pathOut;
-}
-
-dr_bool32 dr_set_current_directory(const char* path)
-{
- return SetCurrentDirectoryA(path) != 0;
-}
-#else
-#include
-#include
-#include
-
-dr_bool32 dr_get_executable_path(char* pathOut, size_t pathOutSize)
-{
- if (pathOut == NULL || pathOutSize == 0) {
- return 0;
- }
-
- ssize_t length = readlink("/proc/self/exe", pathOut, pathOutSize);
- if (length == -1) {
- pathOut[0] = '\0';
- return DR_FALSE;
- }
-
- if ((size_t)length == pathOutSize) {
- pathOut[length - 1] = '\0';
- } else {
- pathOut[length] = '\0';
- }
-
- return DR_TRUE;
-}
-
-dr_bool32 dr_get_config_folder_path(char* pathOut, size_t pathOutSize)
-{
- const char* configdir = getenv("XDG_CONFIG_HOME");
- if (configdir != NULL)
- {
- return strcpy_s(pathOut, pathOutSize, configdir) == 0;
- }
- else
- {
- const char* homedir = getenv("HOME");
- if (homedir == NULL) {
- homedir = getpwuid(getuid())->pw_dir;
- }
-
- if (homedir != NULL)
- {
- if (strcpy_s(pathOut, pathOutSize, homedir) == 0)
- {
- size_t homedirLength = strlen(homedir);
- pathOut += homedirLength;
- pathOutSize -= homedirLength;
-
- if (pathOutSize > 0)
- {
- pathOut[0] = '/';
- pathOut += 1;
- pathOutSize -= 1;
-
- return strcpy_s(pathOut, pathOutSize, ".config") == 0;
- }
- }
- }
- }
-
- return 0;
-}
-
-dr_bool32 dr_get_log_folder_path(char* pathOut, size_t pathOutSize)
-{
- return strcpy_s(pathOut, pathOutSize, "var/log") == 0;
-}
-
-const char* dr_get_current_directory(char* pathOut, size_t pathOutSize)
-{
- return getcwd(pathOut, pathOutSize);
-}
-
-dr_bool32 dr_set_current_directory(const char* path)
-{
- return chdir(path) == 0;
-}
-#endif
-
-dr_bool32 dr_get_executable_directory_path(char* pathOut, size_t pathOutSize)
-{
- if (!dr_get_executable_path(pathOut, pathOutSize)) {
- return DR_FALSE;
- }
-
- // A null terminator needs to be placed at the last slash.
- char* lastSlash = pathOut;
- while (pathOut[0] != '\0') {
- if (pathOut[0] == '/' || pathOut[0] == '\\') {
- lastSlash = pathOut;
- }
- pathOut += 1;
- }
-
- lastSlash[0] = '\0';
- return DR_TRUE;
-}
-
-
-
-/////////////////////////////////////////////////////////
-// Basic File Management
-
-#ifndef _WIN32
-#include
-#include
-#include
-#endif
-
-FILE* dr_fopen(const char* filePath, const char* openMode)
-{
- FILE* pFile;
-#ifdef _MSC_VER
- if (fopen_s(&pFile, filePath, openMode) != 0) {
- return NULL;
- }
-#else
- pFile = fopen(filePath, openMode);
- if (pFile == NULL) {
- return NULL;
- }
-#endif
-
- return pFile;
-}
-
-dr_bool32 dr_create_empty_file(const char* fileName, dr_bool32 failIfExists)
-{
- if (fileName == NULL) {
- return DR_FALSE;
- }
-#ifdef _WIN32
- DWORD dwCreationDisposition;
- if (failIfExists) {
- dwCreationDisposition = CREATE_NEW;
- } else {
- dwCreationDisposition = CREATE_ALWAYS;
- }
-
- HANDLE hFile = CreateFileA(fileName, FILE_GENERIC_WRITE, 0, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
- if (hFile == INVALID_HANDLE_VALUE) {
- return DR_FALSE;
- }
-
- CloseHandle(hFile);
- return DR_TRUE;
-#else
- int flags = O_WRONLY | O_CREAT;
- if (failIfExists) {
- flags |= O_EXCL;
- } else {
- flags |= O_TRUNC;
- }
- int fd = open(fileName, flags, 0666);
- if (fd == -1) {
- return DR_FALSE;
- }
-
- close(fd);
- return DR_TRUE;
-#endif
-}
-
-static void* dr_open_and_read_file_with_extra_data(const char* filePath, size_t* pFileSizeOut, size_t extraBytes)
-{
- if (pFileSizeOut) *pFileSizeOut = 0; // For safety.
-
- if (filePath == NULL) {
- return NULL;
- }
-
- // TODO: Use 64-bit versions of the FILE APIs.
-
- FILE* pFile = dr_fopen(filePath, "rb");
- if (pFile == NULL) {
- return NULL;
- }
-
- fseek(pFile, 0, SEEK_END);
- dr_uint64 fileSize = ftell(pFile);
- fseek(pFile, 0, SEEK_SET);
-
- if (fileSize + extraBytes > SIZE_MAX) {
- fclose(pFile);
- return NULL; // File is too big.
- }
-
- void* pFileData = malloc((size_t)fileSize + extraBytes); // <-- Safe cast due to the check above.
- if (pFileData == NULL) {
- fclose(pFile);
- return NULL; // Failed to allocate memory for the file. Good chance the file is too big.
- }
-
- size_t bytesRead = fread(pFileData, 1, (size_t)fileSize, pFile);
- if (bytesRead != fileSize) {
- free(pFileData);
- fclose(pFile);
- return NULL; // Failed to read every byte from the file.
- }
-
- fclose(pFile);
-
- if (pFileSizeOut) *pFileSizeOut = (size_t)fileSize;
- return pFileData;
-}
-
-void* dr_open_and_read_file(const char* filePath, size_t* pFileSizeOut)
-{
- return dr_open_and_read_file_with_extra_data(filePath, pFileSizeOut, 0);
-}
-
-char* dr_open_and_read_text_file(const char* filePath, size_t* pFileSizeOut)
-{
- if (pFileSizeOut) *pFileSizeOut = 0; // For safety.
-
- size_t fileSize;
- char* pFileData = (char*)dr_open_and_read_file_with_extra_data(filePath, &fileSize, 1); // <-- 1 extra byte for the null terminator.
- if (pFileData == NULL) {
- return NULL;
- }
-
- pFileData[fileSize] = '\0';
-
- if (pFileSizeOut) *pFileSizeOut = fileSize;
- return pFileData;
-}
-
-dr_bool32 dr_open_and_write_file(const char* filePath, const void* pData, size_t dataSize)
-{
- if (filePath == NULL) {
- return DR_FALSE;
- }
-
- // TODO: Use 64-bit versions of the FILE APIs.
-
- FILE* pFile = dr_fopen(filePath, "wb");
- if (pFile == NULL) {
- return DR_FALSE;
- }
-
- if (pData != NULL && dataSize > 0) {
- fwrite(pData, 1, dataSize, pFile);
- }
-
- fclose(pFile);
- return DR_TRUE;
-}
-
-dr_bool32 dr_open_and_write_text_file(const char* filePath, const char* text)
-{
- if (text == NULL) {
- text = "";
- }
-
- return dr_open_and_write_file(filePath, text, strlen(text));
-}
-
-void dr_free_file_data(void* valueReturnedByOpenAndReadFile)
-{
- free(valueReturnedByOpenAndReadFile);
-}
-
-dr_bool32 dr_file_exists(const char* filePath)
-{
- if (filePath == NULL) {
- return DR_FALSE;
- }
-
-#if _WIN32
- DWORD attributes = GetFileAttributesA(filePath);
- return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
-#else
- struct stat info;
- if (stat(filePath, &info) != 0) {
- return DR_FALSE; // Likely the folder doesn't exist.
- }
-
- return (info.st_mode & S_IFDIR) == 0;
-#endif
-}
-
-dr_bool32 dr_directory_exists(const char* directoryPath)
-{
- if (directoryPath == NULL) {
- return DR_FALSE;
- }
-
-#if _WIN32
- DWORD attributes = GetFileAttributesA(directoryPath);
- return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
-#else
- struct stat info;
- if (stat(directoryPath, &info) != 0) {
- return DR_FALSE; // Likely the folder doesn't exist.
- }
-
- return (info.st_mode & S_IFDIR) != 0;
-#endif
-}
-
-dr_bool32 dr_move_file(const char* oldPath, const char* newPath)
-{
- if (oldPath == NULL || newPath == NULL) {
- return DR_FALSE;
- }
-
-#if _WIN32
- return MoveFileExA(oldPath, newPath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH) != 0;
-#else
- return rename(oldPath, newPath) == 0;
-#endif
-}
-
-dr_bool32 dr_copy_file(const char* srcPath, const char* dstPath, dr_bool32 failIfExists)
-{
- if (srcPath == NULL || dstPath == NULL) {
- return DR_FALSE;
- }
-
-#if _WIN32
- return CopyFileA(srcPath, dstPath, failIfExists) != 0;
-#else
- int fdSrc = open(srcPath, O_RDONLY, 0666);
- if (fdSrc == -1) {
- return DR_FALSE;
- }
-
- int fdDst = open(dstPath, O_WRONLY | O_TRUNC | O_CREAT | ((failIfExists) ? O_EXCL : 0), 0666);
- if (fdDst == -1) {
- close(fdSrc);
- return DR_FALSE;
- }
-
- dr_bool32 result = DR_TRUE;
- struct stat info;
- if (fstat(fdSrc, &info) == 0) {
- char buffer[BUFSIZ];
- int bytesRead;
- while ((bytesRead = read(fdSrc, buffer, sizeof(buffer))) > 0) {
- if (write(fdDst, buffer, bytesRead) != bytesRead) {
- result = DR_FALSE;
- break;
- }
- }
- } else {
- result = DR_FALSE;
- }
-
- close(fdDst);
- close(fdSrc);
-
- // Permissions.
- chmod(dstPath, info.st_mode & 07777);
-
- return result;
-#endif
-}
-
-dr_bool32 dr_is_file_read_only(const char* filePath)
-{
- if (filePath == NULL || filePath[0] == '\0') {
- return DR_FALSE;
- }
-
-#if _WIN32
- DWORD attributes = GetFileAttributesA(filePath);
- return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_READONLY) != 0;
-#else
- return access(filePath, W_OK) == -1;
-#endif
-}
-
-dr_uint64 dr_get_file_modified_time(const char* filePath)
-{
- if (filePath == NULL || filePath[0] == '\0') {
- return 0;
- }
-
-#if _WIN32
- HANDLE hFile = CreateFileA(filePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
- if (hFile == INVALID_HANDLE_VALUE) {
- return 0;
- }
-
- FILETIME fileTime;
- BOOL wasSuccessful = GetFileTime(hFile, NULL, NULL, &fileTime);
- CloseHandle(hFile);
-
- if (!wasSuccessful) {
- return 0;
- }
-
- ULARGE_INTEGER result;
- result.HighPart = fileTime.dwHighDateTime;
- result.LowPart = fileTime.dwLowDateTime;
- return result.QuadPart;
-#else
- struct stat info;
- if (stat(filePath, &info) != 0) {
- return 0;
- }
-
- return info.st_mtime;
-#endif
-}
-
-dr_bool32 dr_delete_file(const char* filePath)
-{
- if (filePath == NULL) {
- return DR_FALSE;
- }
-
-#if _WIN32
- return DeleteFileA(filePath) != 0;
-#else
- return remove(filePath) == 0;
-#endif
-}
-
-dr_bool32 dr_mkdir(const char* directoryPath)
-{
- if (directoryPath == NULL) {
- return DR_FALSE;
- }
-
-#if _WIN32
- return CreateDirectoryA(directoryPath, NULL) != 0;
-#else
- return mkdir(directoryPath, 0777) == 0;
-#endif
-}
-
-dr_bool32 dr_mkdir_recursive(const char* directoryPath)
-{
- if (directoryPath == NULL || directoryPath[0] == '\0') {
- return DR_FALSE;
- }
-
- // All we need to do is iterate over every segment in the path and try creating the directory.
- char runningPath[4096];
- memset(runningPath, 0, sizeof(runningPath));
-
- size_t i = 0;
- for (;;) {
- if (i >= sizeof(runningPath)-1) {
- return DR_FALSE; // Path is too long.
- }
-
- if (directoryPath[0] == '\0' || directoryPath[0] == '/' || directoryPath[0] == '\\') {
- if (runningPath[0] != '\0' && !(runningPath[1] == ':' && runningPath[2] == '\0')) { // <-- If the running path is empty, it means we're trying to create the root directory.
- if (!dr_directory_exists(runningPath)) {
- if (!dr_mkdir(runningPath)) {
- return DR_FALSE;
- }
- }
- }
-
- //printf("%s\n", runningPath);
- runningPath[i++] = '/';
- runningPath[i] = '\0';
-
- if (directoryPath[0] == '\0') {
- break;
- }
- } else {
- runningPath[i++] = directoryPath[0];
- }
-
- directoryPath += 1;
- }
-
- return DR_TRUE;
-}
-
-dr_bool32 dr_iterate_files(const char* directory, dr_bool32 recursive, dr_iterate_files_proc proc, void* pUserData)
-{
-#ifdef _WIN32
- char searchQuery[MAX_PATH];
- strcpy_s(searchQuery, sizeof(searchQuery), directory);
-
- unsigned int searchQueryLength = (unsigned int)strlen(searchQuery);
- if (searchQueryLength >= MAX_PATH - 3) {
- return DR_FALSE; // Path is too long.
- }
-
- searchQuery[searchQueryLength + 0] = '\\';
- searchQuery[searchQueryLength + 1] = '*';
- searchQuery[searchQueryLength + 2] = '\0';
-
- WIN32_FIND_DATAA ffd;
- HANDLE hFind = FindFirstFileA(searchQuery, &ffd);
- if (hFind == INVALID_HANDLE_VALUE) {
- return DR_FALSE; // Failed to begin search.
- }
-
- do
- {
- // Skip past "." and ".." directories.
- if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) {
- continue;
- }
-
- char filePath[MAX_PATH];
- strcpy_s(filePath, sizeof(filePath), directory);
- strcat_s(filePath, sizeof(filePath), "/");
- strcat_s(filePath, sizeof(filePath), ffd.cFileName);
-
- if (!proc(filePath, pUserData)) {
- return DR_FALSE;
- }
-
- if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
- if (recursive) {
- if (!dr_iterate_files(filePath, recursive, proc, pUserData)) {
- return DR_FALSE;
- }
- }
- }
-
- } while (FindNextFileA(hFind, &ffd));
-
- FindClose(hFind);
-#else
- DIR* dir = opendir(directory);
- if (dir == NULL) {
- return DR_FALSE;
- }
-
- struct dirent* info = NULL;
- while ((info = readdir(dir)) != NULL)
- {
- // Skip past "." and ".." directories.
- if (strcmp(info->d_name, ".") == 0 || strcmp(info->d_name, "..") == 0) {
- continue;
- }
-
- char filePath[4096];
- strcpy_s(filePath, sizeof(filePath), directory);
- strcat_s(filePath, sizeof(filePath), "/");
- strcat_s(filePath, sizeof(filePath), info->d_name);
-
- struct stat fileinfo;
- if (stat(filePath, &fileinfo) != 0) {
- continue;
- }
-
- if (!proc(filePath, pUserData)) {
- return DR_FALSE;
- }
-
- if (fileinfo.st_mode & S_IFDIR) {
- if (recursive) {
- if (!dr_iterate_files(filePath, recursive, proc, pUserData)) {
- return DR_FALSE;
- }
- }
- }
- }
-
- closedir(dir);
-#endif
-
- return DR_TRUE;
-}
-
-
-
-/////////////////////////////////////////////////////////
-// DPI Awareness
-
-#if defined(_WIN32) || defined(_WIN64)
-
-typedef enum PROCESS_DPI_AWARENESS {
- PROCESS_DPI_UNAWARE = 0,
- PROCESS_SYSTEM_DPI_AWARE = 1,
- PROCESS_PER_MONITOR_DPI_AWARE = 2
-} PROCESS_DPI_AWARENESS;
-
-typedef enum MONITOR_DPI_TYPE {
- MDT_EFFECTIVE_DPI = 0,
- MDT_ANGULAR_DPI = 1,
- MDT_RAW_DPI = 2,
- MDT_DEFAULT = MDT_EFFECTIVE_DPI
-} MONITOR_DPI_TYPE;
-
-typedef BOOL (__stdcall * PFN_SetProcessDPIAware) (void);
-typedef HRESULT (__stdcall * PFN_SetProcessDpiAwareness) (PROCESS_DPI_AWARENESS);
-typedef HRESULT (__stdcall * PFN_GetDpiForMonitor) (HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT *dpiX, UINT *dpiY);
-
-void dr_win32_make_dpi_aware()
-{
- dr_bool32 fallBackToDiscouragedAPI = DR_FALSE;
-
- // We can't call SetProcessDpiAwareness() directly because otherwise on versions of Windows < 8.1 we'll get an error at load time about
- // a missing DLL.
- HMODULE hSHCoreDLL = LoadLibraryW(L"shcore.dll");
- if (hSHCoreDLL != NULL)
- {
- PFN_SetProcessDpiAwareness _SetProcessDpiAwareness = (PFN_SetProcessDpiAwareness)GetProcAddress(hSHCoreDLL, "SetProcessDpiAwareness");
- if (_SetProcessDpiAwareness != NULL)
- {
- if (_SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE) != S_OK)
- {
- fallBackToDiscouragedAPI = DR_FALSE;
- }
- }
- else
- {
- fallBackToDiscouragedAPI = DR_FALSE;
- }
-
- FreeLibrary(hSHCoreDLL);
- }
- else
- {
- fallBackToDiscouragedAPI = DR_FALSE;
- }
-
-
- if (fallBackToDiscouragedAPI)
- {
- HMODULE hUser32DLL = LoadLibraryW(L"user32.dll");
- if (hUser32DLL != NULL)
- {
- PFN_SetProcessDPIAware _SetProcessDPIAware = (PFN_SetProcessDPIAware)GetProcAddress(hUser32DLL, "SetProcessDPIAware");
- if (_SetProcessDPIAware != NULL) {
- _SetProcessDPIAware();
- }
-
- FreeLibrary(hUser32DLL);
- }
- }
-}
-
-void dr_win32_get_base_dpi(int* pDPIXOut, int* pDPIYOut)
-{
- if (pDPIXOut != NULL) {
- *pDPIXOut = 96;
- }
-
- if (pDPIYOut != NULL) {
- *pDPIYOut = 96;
- }
-}
-
-void dr_win32_get_system_dpi(int* pDPIXOut, int* pDPIYOut)
-{
- if (pDPIXOut != NULL) {
- *pDPIXOut = GetDeviceCaps(GetDC(NULL), LOGPIXELSX);
- }
-
- if (pDPIYOut != NULL) {
- *pDPIYOut = GetDeviceCaps(GetDC(NULL), LOGPIXELSY);
- }
-}
-
-
-typedef struct
-{
- int monitorIndex;
- int i;
- int dpiX;
- int dpiY;
- PFN_GetDpiForMonitor _GetDpiForMonitor;
-
-} win32_get_monitor_dpi_data;
-
-static BOOL CALLBACK win32_get_monitor_dpi_callback(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
-{
- (void)hdcMonitor;
- (void)lprcMonitor;
-
- win32_get_monitor_dpi_data* pData = (win32_get_monitor_dpi_data*)dwData;
- if (pData->monitorIndex == pData->i)
- {
- UINT dpiX;
- UINT dpiY;
- if (pData->_GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY) == S_OK)
- {
- pData->dpiX = (int)dpiX;
- pData->dpiY = (int)dpiY;
- }
- else
- {
- dr_win32_get_system_dpi(&pData->dpiX, &pData->dpiY);
- }
-
- return FALSE; // Return DR_FALSE to terminate the enumerator.
- }
-
- pData->i += 1;
- return TRUE;
-}
-
-void dr_win32_get_monitor_dpi(int monitor, int* pDPIXOut, int* pDPIYOut)
-{
- // If multi-monitor DPI awareness is not supported we will need to fall back to system DPI.
- HMODULE hSHCoreDLL = LoadLibraryW(L"shcore.dll");
- if (hSHCoreDLL == NULL) {
- dr_win32_get_system_dpi(pDPIXOut, pDPIYOut);
- return;
- }
-
- PFN_GetDpiForMonitor _GetDpiForMonitor = (PFN_GetDpiForMonitor)GetProcAddress(hSHCoreDLL, "GetDpiForMonitor");
- if (_GetDpiForMonitor == NULL) {
- dr_win32_get_system_dpi(pDPIXOut, pDPIYOut);
- FreeLibrary(hSHCoreDLL);
- return;
- }
-
-
- win32_get_monitor_dpi_data data;
- data.monitorIndex = monitor;
- data.i = 0;
- data.dpiX = 0;
- data.dpiY = 0;
- data._GetDpiForMonitor = _GetDpiForMonitor;
- EnumDisplayMonitors(NULL, NULL, win32_get_monitor_dpi_callback, (LPARAM)&data);
-
- if (pDPIXOut) {
- *pDPIXOut = data.dpiX;
- }
-
- if (pDPIYOut) {
- *pDPIYOut = data.dpiY;
- }
-
-
- FreeLibrary(hSHCoreDLL);
-}
-
-
-static BOOL CALLBACK win32_get_monitor_count_callback(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
-{
- (void)hMonitor;
- (void)hdcMonitor;
- (void)lprcMonitor;
-
- int *count = (int*)dwData;
- (*count)++;
-
- return TRUE;
-}
-
-int dr_win32_get_monitor_count()
-{
- int count = 0;
- if (EnumDisplayMonitors(NULL, NULL, win32_get_monitor_count_callback, (LPARAM)&count)) {
- return count;
- }
-
- return 0;
-}
-#endif
-
-
-/////////////////////////////////////////////////////////
-// Date / Time
-
-time_t dr_now()
-{
- return time(NULL);
-}
-
-void dr_datetime_short(time_t t, char* strOut, unsigned int strOutSize)
-{
-#if defined(_MSC_VER)
- struct tm local;
- localtime_s(&local, &t);
- strftime(strOut, strOutSize, "%x %H:%M:%S", &local);
-#else
- struct tm *local = localtime(&t);
- strftime(strOut, strOutSize, "%x %H:%M:%S", local);
-#endif
-}
-
-void dr_date_YYYYMMDD(time_t t, char* strOut, unsigned int strOutSize)
-{
-#if defined(_MSC_VER)
- struct tm local;
- localtime_s(&local, &t);
- strftime(strOut, strOutSize, "%Y%m%d", &local);
-#else
- struct tm *local = localtime(&t);
- strftime(strOut, strOutSize, "%Y%m%d", local);
-#endif
-}
-
-
-
-/////////////////////////////////////////////////////////
-// Command Line
-
-typedef struct
-{
- dr_cmdline* pCmdLine;
- char* value;
-
- // Win32 style data.
- char* win32_payload;
- char* valueEnd;
-
- // argv style data.
- int iarg; // <-- This starts at -1 so that the first call to next() increments it to 0.
-
-} dr_cmdline_iterator;
-
-dr_cmdline_iterator dr_cmdline_begin(dr_cmdline* pCmdLine)
-{
- dr_cmdline_iterator i;
- i.pCmdLine = pCmdLine;
- i.value = NULL;
- i.win32_payload = NULL;
- i.valueEnd = NULL;
- i.iarg = -1;
-
- if (pCmdLine != NULL && pCmdLine->win32 != NULL) {
- // Win32 style
- size_t length = strlen(pCmdLine->win32);
- i.win32_payload = (char*)malloc(length + 2); // +2 for a double null terminator.
- strcpy_s(i.win32_payload, length + 2, pCmdLine->win32);
- i.win32_payload[length + 1] = '\0';
-
- i.valueEnd = i.win32_payload;
- }
-
- return i;
-}
-
-dr_bool32 dr_cmdline_next(dr_cmdline_iterator* i)
-{
- if (i != NULL && i->pCmdLine != NULL)
- {
- if (i->pCmdLine->win32 != NULL)
- {
- // Win32 style
- if (i->value == NULL) {
- i->value = i->win32_payload;
- i->valueEnd = i->value;
- } else {
- i->value = i->valueEnd + 1;
- }
-
-
- // Move to the start of the next argument.
- while (i->value[0] == ' ') {
- i->value += 1;
- }
-
-
- // If at this point we are sitting on the null terminator it means we have finished iterating.
- if (i->value[0] == '\0')
- {
- free(i->win32_payload);
- i->win32_payload = NULL;
- i->pCmdLine = NULL;
- i->value = NULL;
- i->valueEnd = NULL;
-
- return DR_FALSE;
- }
-
-
- // Move to the end of the token. If the argument begins with a double quote, we iterate until we find
- // the next unescaped double-quote.
- if (i->value[0] == '\"')
- {
- // Go to the last unescaped double-quote.
- i->value += 1;
- i->valueEnd = i->value + 1;
-
- while (i->valueEnd[0] != '\0' && i->valueEnd[0] != '\"')
- {
- if (i->valueEnd[0] == '\\') {
- i->valueEnd += 1;
-
- if (i->valueEnd[0] == '\0') {
- break;
- }
- }
-
- i->valueEnd += 1;
- }
- i->valueEnd[0] = '\0';
- }
- else
- {
- // Go to the next space.
- i->valueEnd = i->value + 1;
-
- while (i->valueEnd[0] != '\0' && i->valueEnd[0] != ' ')
- {
- i->valueEnd += 1;
- }
- i->valueEnd[0] = '\0';
- }
-
- return DR_TRUE;
- }
- else
- {
- // argv style
- i->iarg += 1;
- if (i->iarg < i->pCmdLine->argc)
- {
- i->value = i->pCmdLine->argv[i->iarg];
- return DR_TRUE;
- }
- else
- {
- i->value = NULL;
- return DR_FALSE;
- }
- }
- }
-
- return DR_FALSE;
-}
-
-
-dr_bool32 dr_init_cmdline(dr_cmdline* pCmdLine, int argc, char** argv)
-{
- if (pCmdLine == NULL) {
- return DR_FALSE;
- }
-
- pCmdLine->argc = argc;
- pCmdLine->argv = argv;
- pCmdLine->win32 = NULL;
-
- return DR_TRUE;
-}
-
-dr_bool32 dr_init_cmdline_win32(dr_cmdline* pCmdLine, const char* args)
-{
- if (pCmdLine == NULL) {
- return DR_FALSE;
- }
-
- pCmdLine->argc = 0;
- pCmdLine->argv = NULL;
- pCmdLine->win32 = args;
-
- return DR_TRUE;
-}
-
-void dr_parse_cmdline(dr_cmdline* pCmdLine, dr_cmdline_parse_proc callback, void* pUserData)
-{
- if (pCmdLine == NULL || callback == NULL) {
- return;
- }
-
-
- char pTemp[2] = {0};
-
- char* pKey = NULL;
- char* pVal = NULL;
-
- dr_cmdline_iterator arg = dr_cmdline_begin(pCmdLine);
- if (dr_cmdline_next(&arg))
- {
- if (!callback("[path]", arg.value, pUserData)) {
- return;
- }
- }
-
- while (dr_cmdline_next(&arg))
- {
- if (arg.value[0] == '-')
- {
- // key
-
- // If the key is non-null, but the value IS null, it means we hit a key with no value in which case it will not yet have been posted.
- if (pKey != NULL && pVal == NULL)
- {
- if (!callback(pKey, pVal, pUserData)) {
- return;
- }
-
- pKey = NULL;
- }
- else
- {
- // Need to ensure the key and value are reset before doing any further processing.
- pKey = NULL;
- pVal = NULL;
- }
-
-
-
- if (arg.value[1] == '-')
- {
- // --argument style
- pKey = arg.value + 2;
- }
- else
- {
- // -a -b -c -d or -abcd style
- if (arg.value[1] != '\0')
- {
- if (arg.value[2] == '\0')
- {
- // -a -b -c -d style
- pTemp[0] = arg.value[1];
- pKey = pTemp;
- pVal = NULL;
- }
- else
- {
- // -abcd style.
- int i = 1;
- while (arg.value[i] != '\0')
- {
- pTemp[0] = arg.value[i];
-
- if (!callback(pTemp, NULL, pUserData)) {
- return;
- }
-
- pKey = NULL;
- pVal = NULL;
-
- i += 1;
- }
- }
- }
- }
- }
- else
- {
- // value
-
- pVal = arg.value;
- if (!callback(pKey, pVal, pUserData)) {
- return;
- }
- }
- }
-
-
- // There may be a key without a value that needs posting.
- if (pKey != NULL && pVal == NULL) {
- callback(pKey, pVal, pUserData);
- }
-}
-
-typedef struct
-{
- dr_bool32 exists;
- const char* key;
-} dr_cmdline_key_exists_data;
-
-dr_bool32 dr_cmdline_key_exists_callback(const char* key, const char* value, void* pUserData)
-{
- (void)value;
-
- dr_cmdline_key_exists_data* pData = (dr_cmdline_key_exists_data*)pUserData;
- assert(pData != NULL);
-
- if (key != NULL && strcmp(pData->key, key) == 0) {
- pData->exists = DR_TRUE;
- return DR_FALSE;
- }
-
- return DR_TRUE;
-}
-
-dr_bool32 dr_cmdline_key_exists(dr_cmdline* pCmdLine, const char* key)
-{
- dr_cmdline_key_exists_data data;
- data.exists = DR_FALSE;
- data.key = key;
- dr_parse_cmdline(pCmdLine, dr_cmdline_key_exists_callback, &data);
-
- return data.exists;
-}
-
-int dr_cmdline_to_argv(dr_cmdline* pCmdLine, char*** argvOut)
-{
- if (argvOut == NULL) return 0;
- *argvOut = NULL; // Safety.
-
- int argc = 0;
- char** argv = NULL;
- size_t cmdlineLen = 0;
-
- // The command line is parsed in 2 passes. The first pass simple calculates the required sizes of each buffer. The second
- // pass fills those buffers with actual data.
-
- // First pass.
- dr_cmdline_iterator arg = dr_cmdline_begin(pCmdLine);
- while (dr_cmdline_next(&arg)) {
- cmdlineLen += strlen(arg.value) + 1; // +1 for null terminator.
- argc += 1;
- }
-
- if (argc == 0) {
- return 0;
- }
-
-
- // The entire data for the command line is stored in a single buffer.
- char* data = (char*)malloc((argc * sizeof(char**)) + (cmdlineLen * sizeof(char)));
- if (data == NULL) {
- return 0; // Ran out of memory.
- }
-
- argv = (char**)data;
- char* cmdlineStr = data + (argc * sizeof(char**));
-
-
-
- // Second pass.
- argc = 0;
- cmdlineLen = 0;
-
- arg = dr_cmdline_begin(pCmdLine);
- while (dr_cmdline_next(&arg)) {
- argv[argc] = cmdlineStr + cmdlineLen;
-
- int i = 0;
- while (arg.value[i] != '\0') {
- argv[argc][i] = arg.value[i];
- i += 1;
- }
- argv[argc][i] = '\0';
-
-
- cmdlineLen += strlen(arg.value) + 1; // +1 for null terminator.
- argc += 1;
- }
-
-
- *argvOut = argv;
- return argc;
-}
-
-int dr_winmain_to_argv(const char* cmdlineWinMain, char*** argvOut)
-{
- dr_cmdline cmdline;
- if (!dr_init_cmdline_win32(&cmdline, cmdlineWinMain)) {
- return 0;
- }
-
- return dr_cmdline_to_argv(&cmdline, argvOut);
-}
-
-void dr_free_argv(char** argv)
-{
- if (argv == NULL) {
- return;
- }
-
- free(argv);
-}
-
-
-
-/////////////////////////////////////////////////////////
-// Threading
-
-#if defined(_WIN32)
-void dr_sleep(unsigned int milliseconds)
-{
- Sleep((DWORD)milliseconds);
-}
-
-void dr_yield()
-{
- SwitchToThread();
-}
-
-unsigned int dr_get_logical_processor_count()
-{
- SYSTEM_INFO sysinfo;
- GetSystemInfo(&sysinfo);
-
- return (unsigned int)sysinfo.dwNumberOfProcessors;
-}
-
-
-typedef struct
-{
- /// The Win32 thread handle.
- HANDLE hThread;
-
- /// The entry point.
- dr_thread_entry_proc entryProc;
-
- /// The user data to pass to the thread's entry point.
- void* pData;
-
- /// Set to DR_TRUE by the entry function. We use this to wait for the entry function to start.
- dr_bool32 isInEntryProc;
-
-} dr_thread_win32;
-
-static DWORD WINAPI dr_thread_entry_proc_win32(LPVOID pUserData)
-{
- dr_thread_win32* pThreadWin32 = (dr_thread_win32*)pUserData;
- assert(pThreadWin32 != NULL);
-
- void* pEntryProcData = pThreadWin32->pData;
- dr_thread_entry_proc entryProc = pThreadWin32->entryProc;
- assert(entryProc != NULL);
-
- pThreadWin32->isInEntryProc = DR_TRUE;
-
- return (DWORD)entryProc(pEntryProcData);
-}
-
-dr_thread dr_create_thread(dr_thread_entry_proc entryProc, void* pData)
-{
- if (entryProc == NULL) {
- return NULL;
- }
-
- dr_thread_win32* pThreadWin32 = (dr_thread_win32*)malloc(sizeof(*pThreadWin32));
- if (pThreadWin32 != NULL)
- {
- pThreadWin32->entryProc = entryProc;
- pThreadWin32->pData = pData;
- pThreadWin32->isInEntryProc = DR_FALSE;
-
- pThreadWin32->hThread = CreateThread(NULL, 0, dr_thread_entry_proc_win32, pThreadWin32, 0, NULL);
- if (pThreadWin32 == NULL) {
- free(pThreadWin32);
- return NULL;
- }
-
- // Wait for the new thread to enter into it's entry point before returning. We need to do this so we can safely
- // support something like dr_delete_thread(dr_create_thread(my_thread_proc, pData)).
- //
- // On Win32 there are times when this can get stuck in an infinite loop - I expect it's something to do with some
- // bad scheduling by the OS. This can be "fixed" by sleeping for a bit.
- while (!pThreadWin32->isInEntryProc) { dr_sleep(0); }
- }
-
- return (dr_thread)pThreadWin32;
-}
-
-void dr_delete_thread(dr_thread thread)
-{
- dr_thread_win32* pThreadWin32 = (dr_thread_win32*)thread;
- if (pThreadWin32 != NULL)
- {
- CloseHandle(pThreadWin32->hThread);
- }
-
- free(pThreadWin32);
-}
-
-void dr_wait_thread(dr_thread thread)
-{
- dr_thread_win32* pThreadWin32 = (dr_thread_win32*)thread;
- if (pThreadWin32 != NULL)
- {
- WaitForSingleObject(pThreadWin32->hThread, INFINITE);
- }
-}
-
-void dr_wait_and_delete_thread(dr_thread thread)
-{
- dr_wait_thread(thread);
- dr_delete_thread(thread);
-}
-
-
-#ifdef DR_UTIL_WIN32_USE_CRITICAL_SECTION_MUTEX
-dr_mutex dr_create_mutex()
-{
- dr_mutex mutex = malloc(sizeof(CRITICAL_SECTION));
- if (mutex != NULL)
- {
- InitializeCriticalSection(mutex);
- }
-
- return mutex;
-}
-
-void dr_delete_mutex(dr_mutex mutex)
-{
- DeleteCriticalSection(mutex);
- free(mutex);
-}
-
-void dr_lock_mutex(dr_mutex mutex)
-{
- EnterCriticalSection(mutex);
-}
-
-void dr_unlock_mutex(dr_mutex mutex)
-{
- LeaveCriticalSection(mutex);
-}
-#else
-dr_mutex dr_create_mutex()
-{
- return CreateEventA(NULL, FALSE, TRUE, NULL);
-}
-
-void dr_delete_mutex(dr_mutex mutex)
-{
- CloseHandle((HANDLE)mutex);
-}
-
-void dr_lock_mutex(dr_mutex mutex)
-{
- WaitForSingleObject((HANDLE)mutex, INFINITE);
-}
-
-void dr_unlock_mutex(dr_mutex mutex)
-{
- SetEvent((HANDLE)mutex);
-}
-#endif
-
-
-dr_semaphore dr_create_semaphore(int initialValue)
-{
- return (void*)CreateSemaphoreA(NULL, initialValue, LONG_MAX, NULL);
-}
-
-void dr_delete_semaphore(dr_semaphore semaphore)
-{
- CloseHandle(semaphore);
-}
-
-dr_bool32 dr_wait_semaphore(dr_semaphore semaphore)
-{
- return WaitForSingleObject((HANDLE)semaphore, INFINITE) == WAIT_OBJECT_0;
-}
-
-dr_bool32 dr_release_semaphore(dr_semaphore semaphore)
-{
- return ReleaseSemaphore((HANDLE)semaphore, 1, NULL) != 0;
-}
-#else
-void dr_sleep(unsigned int milliseconds)
-{
- usleep(milliseconds * 1000); // <-- usleep is in microseconds.
-}
-
-void dr_yield()
-{
- sched_yield();
-}
-
-unsigned int dr_get_logical_processor_count()
-{
- return (unsigned int)sysconf(_SC_NPROCESSORS_ONLN);
-}
-
-
-typedef struct
-{
- /// The Win32 thread handle.
- pthread_t pthread;
-
- /// The entry point.
- dr_thread_entry_proc entryProc;
-
- /// The user data to pass to the thread's entry point.
- void* pData;
-
- /// Set to DR_TRUE by the entry function. We use this to wait for the entry function to start.
- dr_bool32 isInEntryProc;
-
-} dr_thread_posix;
-
-static void* dr_thread_entry_proc_posix(void* pDataIn)
-{
- dr_thread_posix* pThreadPosix = (dr_thread_posix*)pDataIn;
- assert(pThreadPosix != NULL);
-
- void* pEntryProcData = pThreadPosix->pData;
- dr_thread_entry_proc entryProc = pThreadPosix->entryProc;
- assert(entryProc != NULL);
-
- pThreadPosix->isInEntryProc = DR_TRUE;
-
- return (void*)(size_t)entryProc(pEntryProcData);
-}
-
-dr_thread dr_create_thread(dr_thread_entry_proc entryProc, void* pData)
-{
- if (entryProc == NULL) {
- return NULL;
- }
-
- dr_thread_posix* pThreadPosix = (dr_thread_posix*)malloc(sizeof(*pThreadPosix));
- if (pThreadPosix != NULL)
- {
- pThreadPosix->entryProc = entryProc;
- pThreadPosix->pData = pData;
- pThreadPosix->isInEntryProc = DR_FALSE;
-
- if (pthread_create(&pThreadPosix->pthread, NULL, dr_thread_entry_proc_posix, pThreadPosix) != 0) {
- free(pThreadPosix);
- return NULL;
- }
-
- // Wait for the new thread to enter into it's entry point before returning. We need to do this so we can safely
- // support something like dr_delete_thread(dr_create_thread(my_thread_proc, pData)).
- while (!pThreadPosix->isInEntryProc) {}
- }
-
- return (dr_thread)pThreadPosix;
-}
-
-void dr_delete_thread(dr_thread thread)
-{
- free(thread);
-}
-
-void dr_wait_thread(dr_thread thread)
-{
- dr_thread_posix* pThreadPosix = (dr_thread_posix*)thread;
- if (pThreadPosix != NULL)
- {
- pthread_join(pThreadPosix->pthread, NULL);
- }
-}
-
-
-
-dr_mutex dr_create_mutex()
-{
- pthread_mutex_t* mutex = (pthread_mutex_t*)malloc(sizeof(*mutex));
- if (pthread_mutex_init(mutex, NULL) != 0) {
- free(mutex);
- mutex = NULL;
- }
-
- return mutex;
-}
-
-void dr_delete_mutex(dr_mutex mutex)
-{
- pthread_mutex_destroy((pthread_mutex_t*)mutex);
-}
-
-void dr_lock_mutex(dr_mutex mutex)
-{
- pthread_mutex_lock((pthread_mutex_t*)mutex);
-}
-
-void dr_unlock_mutex(dr_mutex mutex)
-{
- pthread_mutex_unlock((pthread_mutex_t*)mutex);
-}
-
-
-
-dr_semaphore dr_create_semaphore(int initialValue)
-{
- sem_t* semaphore = (sem_t*)malloc(sizeof(*semaphore));
- if (sem_init(semaphore, 0, (unsigned int)initialValue) == -1) {
- free(semaphore);
- semaphore = NULL;
- }
-
- return semaphore;
-}
-
-void dr_delete_semaphore(dr_semaphore semaphore)
-{
- sem_close((sem_t*)semaphore);
-}
-
-dr_bool32 dr_wait_semaphore(dr_semaphore semaphore)
-{
- return sem_wait((sem_t*)semaphore) != -1;
-}
-
-dr_bool32 dr_release_semaphore(dr_semaphore semaphore)
-{
- return sem_post((sem_t*)semaphore) != -1;
-}
-#endif
-
-
-
-/////////////////////////////////////////////////////////
-// Timing
-
-// macOS does not have clock_gettime on OS X < 10.12
-#ifdef __MACH__
-#include
-#ifndef MAC_OS_X_VERSION_10_12
-#define MAC_OS_X_VERSION_10_12 101200
-#endif
-#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12
-#include
-#define CLOCK_REALTIME 0
-#define CLOCK_MONOTONIC 0
-int clock_gettime(int clk_id, struct timespec* t)
-{
- mach_timebase_info_data_t timebase;
- mach_timebase_info(&timebase);
- dr_uint64 time;
- time = mach_absolute_time();
- double nseconds = ((double)time * (double)timebase.numer) / ((double)timebase.denom);
- double seconds = ((double)time * (double)timebase.numer) / ((double)timebase.denom * 1e9);
- t->tv_sec = seconds;
- t->tv_nsec = nseconds;
- return 0;
-}
-#endif
-#endif
-
-#ifdef _WIN32
-static LARGE_INTEGER g_DRTimerFrequency = {{0}};
-void dr_timer_init(dr_timer* pTimer)
-{
- if (g_DRTimerFrequency.QuadPart == 0) {
- QueryPerformanceFrequency(&g_DRTimerFrequency);
- }
-
- LARGE_INTEGER counter;
- QueryPerformanceCounter(&counter);
- pTimer->counter = (dr_uint64)counter.QuadPart;
-}
-
-double dr_timer_tick(dr_timer* pTimer)
-{
- LARGE_INTEGER counter;
- if (!QueryPerformanceCounter(&counter)) {
- return 0;
- }
-
- dr_uint64 newTimeCounter = counter.QuadPart;
- dr_uint64 oldTimeCounter = pTimer->counter;
-
- pTimer->counter = newTimeCounter;
-
- return (newTimeCounter - oldTimeCounter) / (double)g_DRTimerFrequency.QuadPart;
-}
-#else
-void dr_timer_init(dr_timer* pTimer)
-{
- struct timespec newTime;
- clock_gettime(CLOCK_MONOTONIC, &newTime);
-
- pTimer->counter = (newTime.tv_sec * 1000000000LL) + newTime.tv_nsec;
-}
-
-double dr_timer_tick(dr_timer* pTimer)
-{
- struct timespec newTime;
- clock_gettime(CLOCK_MONOTONIC, &newTime);
-
- dr_uint64 newTimeCounter = (newTime.tv_sec * 1000000000LL) + newTime.tv_nsec;
- dr_uint64 oldTimeCounter = pTimer->counter;
-
- pTimer->counter = newTimeCounter;
-
- return (newTimeCounter - oldTimeCounter) / 1000000000.0;
-}
-#endif
-
-
-/////////////////////////////////////////////////////////
-// Random
-
-double dr_randd()
-{
- return (double)rand() / (double)RAND_MAX;
-}
-
-float dr_randf()
-{
- return (float)dr_randd();
-}
-
-
-/////////////////////////////////////////////////////////
-// User Accounts and Process Management
-
-size_t dr_get_username(char* usernameOut, size_t usernameOutSize)
-{
- if (usernameOut != NULL && usernameOutSize > 0) {
- usernameOut[0] = '\0';
- }
-
-#ifdef _WIN32
- DWORD dwSize = (DWORD)usernameOutSize;
- if (!GetUserNameA(usernameOut, &dwSize)) {
- return 0;
- }
-
- return dwSize;
-#else
- struct passwd *pw = getpwuid(geteuid());
- if (pw == NULL) {
- return 0;
- }
-
- if (usernameOut != NULL) {
- strcpy_s(usernameOut, usernameOutSize, pw->pw_name);
- }
-
- return strlen(pw->pw_name);
-#endif
-}
-
-unsigned int dr_get_process_id()
-{
-#ifdef _WIN32
- return GetProcessId(GetCurrentProcess());
-#else
- return (unsigned int)getpid();
-#endif
-}
-
-
-
-/////////////////////////////////////////////////////////
-// Miscellaneous Stuff.
-
-dr_bool32 dr_hex_char_to_uint(char ascii, unsigned int* out)
-{
- if (ascii >= '0' && ascii <= '9') {
- if (out) *out = ascii - '0';
- return DR_TRUE;
- }
-
- if (ascii >= 'A' && ascii <= 'F') {
- if (out) *out = 10 + (ascii - 'A');
- return DR_TRUE;
- }
-
- if (ascii >= 'a' && ascii <= 'f') {
- if (out) *out = 10 + (ascii - 'a');
- return DR_TRUE;
- }
-
- if (out) *out = 0;
- return DR_FALSE;
-}
-
-#endif //DR_IMPLEMENTATION
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-//
-// dr_path
-//
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-#ifndef dr_path_h
-#define dr_path_h
-
-#include
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-// Structure representing a section of a path.
-typedef struct
-{
- size_t offset;
- size_t length;
-
-} drpath_segment;
-
-// Structure used for iterating over a path while at the same time providing useful and easy-to-use information about the iteration.
-typedef struct drpath_iterator
-{
- const char* path;
- drpath_segment segment;
-
-} drpath_iterator;
-
-
-
-/// Compares a section of two strings for equality.
-///
-/// @param s0Path [in] The first path.
-/// @param s0 [in] The segment of the first path to compare.
-/// @param s1Path [in] The second path.
-/// @param s1 [in] The segment of the second path to compare.
-///
-/// @return DR_TRUE if the strings are equal; DR_FALSE otherwise.
-dr_bool32 drpath_segments_equal(const char* s0Path, const drpath_segment s0, const char* s1Path, const drpath_segment s1);
-
-
-/// Creates an iterator for iterating over each segment in a path.
-///
-/// @param path [in] The path whose segments are being iterated.
-///
-/// @return True if at least one segment is found; DR_FALSE if it's an empty path.
-dr_bool32 drpath_first(const char* path, drpath_iterator* i);
-
-/// Creates an iterator beginning at the last segment.
-dr_bool32 drpath_last(const char* path, drpath_iterator* i);
-
-/// Goes to the next segment in the path as per the given iterator.
-///
-/// @param i [in] A pointer to the iterator to increment.
-///
-/// @return True if the iterator contains a valid value. Use this to determine when to terminate iteration.
-dr_bool32 drpath_next(drpath_iterator* i);
-
-/// Goes to the previous segment in the path.
-///
-/// @param i [in] A pointer to the iterator to decrement.
-///
-/// @return DR_TRUE if the iterator contains a valid value. Use this to determine when to terminate iteration.
-dr_bool32 drpath_prev(drpath_iterator* i);
-
-/// Determines if the given iterator is at the end.
-///
-/// @param i [in] The iterator to check.
-dr_bool32 drpath_at_end(drpath_iterator i);
-
-/// Determines if the given iterator is at the start.
-///
-/// @param i [in] The iterator to check.
-dr_bool32 drpath_at_start(drpath_iterator i);
-
-/// Compares the string values of two iterators for equality.
-///
-/// @param i0 [in] The first iterator to compare.
-/// @param i1 [in] The second iterator to compare.
-///
-/// @return DR_TRUE if the strings are equal; DR_FALSE otherwise.
-dr_bool32 drpath_iterators_equal(const drpath_iterator i0, const drpath_iterator i1);
-
-
-/// Determines whether or not the given iterator refers to the root segment of a path.
-dr_bool32 drpath_is_root_segment(const drpath_iterator i);
-
-/// Determines whether or not the given iterator refers to a Linux style root directory ("/")
-dr_bool32 drpath_is_linux_style_root_segment(const drpath_iterator i);
-
-/// Determines whether or not the given iterator refers to a Windows style root directory.
-dr_bool32 drpath_is_win32_style_root_segment(const drpath_iterator i);
-
-
-/// Converts the slashes in the given path to forward slashes.
-///
-/// @param path [in] The path whose slashes are being converted.
-void drpath_to_forward_slashes(char* path);
-
-/// Converts the slashes in the given path to back slashes.
-///
-/// @param path [in] The path whose slashes are being converted.
-void drpath_to_backslashes(char* path);
-
-
-/// Determines whether or not the given path is a decendant of another.
-///
-/// @param descendantAbsolutePath [in] The absolute path of the descendant.
-/// @param parentAbsolutePath [in] The absolute path of the parent.
-///
-/// @remarks
-/// As an example, "C:/My/Folder" is a descendant of "C:/".
-/// @par
-/// If either path contains "." or "..", clean it with drpath_clean() before calling this.
-dr_bool32 drpath_is_descendant(const char* descendantAbsolutePath, const char* parentAbsolutePath);
-
-/// Determines whether or not the given path is a direct child of another.
-///
-/// @param childAbsolutePath [in] The absolute of the child.
-/// @param parentAbsolutePath [in] The absolute path of the parent.
-///
-/// @remarks
-/// As an example, "C:/My/Folder" is NOT a child of "C:/" - it is a descendant. Alternatively, "C:/My" IS a child of "C:/".
-/// @par
-/// If either path contains "." or "..", clean it with drpath_clean() before calling this.
-dr_bool32 drpath_is_child(const char* childAbsolutePath, const char* parentAbsolutePath);
-
-
-/// Modifies the given path by transforming it into it's base path.
-///
-/// Returns , for convenience.
-char* drpath_base_path(char* path);
-
-/// Retrieves the base path from the given path, not including the trailing slash.
-///
-/// @param path [in] The full path.
-/// @param baseOut [out] A pointer to the buffer that will receive the base path.
-/// @param baseSizeInBytes [in] The size in bytes of the buffer that will receive the base directory.
-///
-/// @remarks
-/// As an example, when "path" is "C:/MyFolder/MyFile", the output will be "C:/MyFolder". Note that there is no trailing slash.
-/// @par
-/// If "path" is something like "/MyFolder", the return value will be an empty string.
-void drpath_copy_base_path(const char* path, char* baseOut, size_t baseSizeInBytes);
-
-/// Finds the file name portion of the path.
-///
-/// @param path [in] The path to search.
-///
-/// @return A pointer to the beginning of the string containing the file name. If this is non-null, it will be an offset of "path".
-///
-/// @remarks
-/// A path with a trailing slash will return an empty string.
-/// @par
-/// The return value is just an offset of "path".
-const char* drpath_file_name(const char* path);
-
-/// Copies the file name into the given buffer.
-const char* drpath_copy_file_name(const char* path, char* fileNameOut, size_t fileNameSizeInBytes);
-
-/// Finds the file extension of the given file path.
-///
-/// @param path [in] The path to search.
-///
-/// @return A pointer to the beginning of the string containing the file's extension.
-///
-/// @remarks
-/// A path with a trailing slash will return an empty string.
-/// @par
-/// The return value is just an offset of "path".
-/// @par
-/// On a path such as "filename.ext1.ext2" the returned string will be "ext2".
-const char* drpath_extension(const char* path);
-
-
-/// Checks whether or not the two paths are equal.
-///
-/// @param path1 [in] The first path.
-/// @param path2 [in] The second path.
-///
-/// @return DR_TRUE if the paths are equal, DR_FALSE otherwise.
-///
-/// @remarks
-/// This is case-sensitive.
-/// @par
-/// This is more than just a string comparison. Rather, this splits the path and compares each segment. The path "C:/My/Folder" is considered
-/// equal to to "C:\\My\\Folder".
-dr_bool32 drpath_equal(const char* path1, const char* path2);
-
-/// Checks if the extension of the given path is equal to the given extension.
-///
-/// @remarks
-/// By default this is NOT case-sensitive, however if the standard library is disable, it is case-sensitive.
-dr_bool32 drpath_extension_equal(const char* path, const char* extension);
-
-
-/// Determines whether or not the given path is relative.
-///
-/// @param path [in] The path to check.
-dr_bool32 drpath_is_relative(const char* path);
-
-/// Determines whether or not the given path is absolute.
-///
-/// @param path [in] The path to check.
-dr_bool32 drpath_is_absolute(const char* path);
-
-
-/// Appends two paths together, ensuring there is not double up on the last slash.
-///
-/// @param base [in, out] The base path that is being appended to.
-/// @param baseBufferSizeInBytes [in] The size of the buffer pointed to by "base", in bytes.
-/// @param other [in] The other path segment.
-///
-/// @remarks
-/// This assumes both paths are well formed and "other" is a relative path.
-dr_bool32 drpath_append(char* base, size_t baseBufferSizeInBytes, const char* other);
-
-/// Appends an iterator object to the given base path.
-dr_bool32 drpath_append_iterator(char* base, size_t baseBufferSizeInBytes, drpath_iterator i);
-
-/// Appends an extension to the given path.
-dr_bool32 drpath_append_extension(char* base, size_t baseBufferSizeInBytes, const char* extension);
-
-/// Appends two paths together, and copyies them to a separate buffer.
-///
-/// @param dst [out] The destination buffer.
-/// @param dstSizeInBytes [in] The size of the buffer pointed to by "dst", in bytes.
-/// @param base [in] The base directory.
-/// @param other [in] The relative path to append to "base".
-///
-/// @return DR_TRUE if the paths were appended successfully; DR_FALSE otherwise.
-///
-/// @remarks
-/// This assumes both paths are well formed and "other" is a relative path.
-dr_bool32 drpath_copy_and_append(char* dst, size_t dstSizeInBytes, const char* base, const char* other);
-
-/// Appends a base path and an iterator together, and copyies them to a separate buffer.
-///
-/// @param dst [out] The destination buffer.
-/// @param dstSizeInBytes [in] The size of the buffer pointed to by "dst", in bytes.
-/// @param base [in] The base directory.
-/// @param i [in] The iterator to append.
-///
-/// @return DR_TRUE if the paths were appended successfully; DR_FALSE otherwise.
-///
-/// @remarks
-/// This assumes both paths are well formed and "i" is a valid iterator.
-dr_bool32 drpath_copy_and_append_iterator(char* dst, size_t dstSizeInBytes, const char* base, drpath_iterator i);
-
-/// Appends an extension to the given base path and copies them to a separate buffer.
-/// @param dst [out] The destination buffer.
-/// @param dstSizeInBytes [in] The size of the buffer pointed to by "dst", in bytes.
-/// @param base [in] The base directory.
-/// @param extension [in] The relative path to append to "base".
-///
-/// @return DR_TRUE if the paths were appended successfully; DR_FALSE otherwise.
-dr_bool32 drpath_copy_and_append_extension(char* dst, size_t dstSizeInBytes, const char* base, const char* extension);
-
-
-/// Cleans the path and resolves the ".." and "." segments.
-///
-/// @param path [in] The path to clean.
-/// @param pathOut [out] A pointer to the buffer that will receive the path.
-/// @param pathOutSizeInBytes [in] The size of the buffer pointed to by pathOut, in bytes.
-///
-/// @return The number of bytes written to the output buffer, including the null terminator.
-///
-/// @remarks
-/// The output path will never be longer than the input path.
-/// @par
-/// The output buffer should never overlap with the input path.
-/// @par
-/// As an example, the path "my/messy/../path" will result in "my/path"
-/// @par
-/// The path "my/messy/../../../path" (note how there are too many ".." segments) will return "path" (the extra ".." segments will be dropped.)
-/// @par
-/// If an error occurs, such as an invalid input path, 0 will be returned.
-size_t drpath_clean(const char* path, char* pathOut, size_t pathOutSizeInBytes);
-
-/// Appends one path to the other and then cleans it.
-size_t drpath_append_and_clean(char* dst, size_t dstSizeInBytes, const char* base, const char* other);
-
-
-/// Removes the extension from the given path.
-///
-/// @remarks
-/// If the given path does not have an extension, 1 will be returned, but the string will be left unmodified.
-dr_bool32 drpath_remove_extension(char* path);
-
-/// Creates a copy of the given string and removes the extension.
-dr_bool32 drpath_copy_and_remove_extension(char* dst, size_t dstSizeInBytes, const char* path);
-
-
-/// Removes the last segment from the given path.
-dr_bool32 drpath_remove_file_name(char* path);
-
-/// Creates a copy of the given string and removes the extension.
-dr_bool32 drpath_copy_and_remove_file_name(char* dst, size_t dstSizeInBytes, const char* path);
-
-
-/// Converts an absolute path to a relative path.
-///
-/// @return DR_TRUE if the conversion was successful; DR_FALSE if there was an error.
-///
-/// @remarks
-/// This will normalize every slash to forward slashes.
-dr_bool32 drpath_to_relative(const char* absolutePathToMakeRelative, const char* absolutePathToMakeRelativeTo, char* relativePathOut, size_t relativePathOutSizeInBytes);
-
-/// Converts a relative path to an absolute path based on a base path.
-///
-/// @return DR_TRUE if the conversion was successful; DR_FALSE if there was an error.
-///
-/// @remarks
-/// This is equivalent to an append followed by a clean. Slashes will be normalized to forward slashes.
-dr_bool32 drpath_to_absolute(const char* relativePathToMakeAbsolute, const char* basePath, char* absolutePathOut, size_t absolutePathOutSizeInBytes);
-
-
-#ifdef __cplusplus
-}
-#endif
-#endif //dr_path_h
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// IMPLEMENTATION
-//
-///////////////////////////////////////////////////////////////////////////////
-#ifdef DR_IMPLEMENTATION
-#include
-#include
-#include
-
-dr_bool32 drpath_first(const char* path, drpath_iterator* i)
-{
- if (i == 0) return DR_FALSE;
- i->path = path;
- i->segment.offset = 0;
- i->segment.length = 0;
-
- if (path == 0 || path[0] == '\0') {
- return DR_FALSE;
- }
-
- while (i->path[i->segment.length] != '\0' && (i->path[i->segment.length] != '/' && i->path[i->segment.length] != '\\')) {
- i->segment.length += 1;
- }
-
- return DR_TRUE;
-}
-
-dr_bool32 drpath_last(const char* path, drpath_iterator* i)
-{
- if (i == 0) return DR_FALSE;
- i->path = path;
- i->segment.offset = 0;
- i->segment.length = 0;
-
- if (path == 0 || path[0] == '\0') {
- return DR_FALSE;
- }
-
- i->path = path;
- i->segment.offset = strlen(path);
- i->segment.length = 0;
-
- return drpath_prev(i);
-}
-
-dr_bool32 drpath_next(drpath_iterator* i)
-{
- if (i == NULL || i->path == NULL) {
- return DR_FALSE;
- }
-
- i->segment.offset = i->segment.offset + i->segment.length;
- i->segment.length = 0;
-
- while (i->path[i->segment.offset] != '\0' && (i->path[i->segment.offset] == '/' || i->path[i->segment.offset] == '\\')) {
- i->segment.offset += 1;
- }
-
- if (i->path[i->segment.offset] == '\0') {
- return DR_FALSE;
- }
-
-
- while (i->path[i->segment.offset + i->segment.length] != '\0' && (i->path[i->segment.offset + i->segment.length] != '/' && i->path[i->segment.offset + i->segment.length] != '\\')) {
- i->segment.length += 1;
- }
-
- return DR_TRUE;
-}
-
-dr_bool32 drpath_prev(drpath_iterator* i)
-{
- if (i == NULL || i->path == NULL || i->segment.offset == 0) {
- return DR_FALSE;
- }
-
- i->segment.length = 0;
-
- do
- {
- i->segment.offset -= 1;
- } while (i->segment.offset > 0 && (i->path[i->segment.offset] == '/' || i->path[i->segment.offset] == '\\'));
-
- if (i->segment.offset == 0) {
- if (i->path[i->segment.offset] == '/' || i->path[i->segment.offset] == '\\') {
- i->segment.length = 0;
- return DR_TRUE;
- }
-
- return DR_FALSE;
- }
-
-
- size_t offsetEnd = i->segment.offset + 1;
- while (i->segment.offset > 0 && (i->path[i->segment.offset] != '/' && i->path[i->segment.offset] != '\\')) {
- i->segment.offset -= 1;
- }
-
- if (i->path[i->segment.offset] == '/' || i->path[i->segment.offset] == '\\') {
- i->segment.offset += 1;
- }
-
-
- i->segment.length = offsetEnd - i->segment.offset;
-
- return DR_TRUE;
-}
-
-dr_bool32 drpath_at_end(drpath_iterator i)
-{
- return i.path == 0 || i.path[i.segment.offset] == '\0';
-}
-
-dr_bool32 drpath_at_start(drpath_iterator i)
-{
- return i.path != 0 && i.segment.offset == 0;
-}
-
-dr_bool32 drpath_iterators_equal(const drpath_iterator i0, const drpath_iterator i1)
-{
- return drpath_segments_equal(i0.path, i0.segment, i1.path, i1.segment);
-}
-
-dr_bool32 drpath_segments_equal(const char* s0Path, const drpath_segment s0, const char* s1Path, const drpath_segment s1)
-{
- if (s0Path == NULL || s1Path == NULL) {
- return DR_FALSE;
- }
-
- if (s0.length != s1.length) {
- return DR_FALSE;
- }
-
- return strncmp(s0Path + s0.offset, s1Path + s1.offset, s0.length) == 0;
-}
-
-
-dr_bool32 drpath_is_root_segment(const drpath_iterator i)
-{
- return drpath_is_linux_style_root_segment(i) || drpath_is_win32_style_root_segment(i);
-}
-
-dr_bool32 drpath_is_linux_style_root_segment(const drpath_iterator i)
-{
- if (i.path == NULL) {
- return DR_FALSE;
- }
-
- if (i.segment.offset == 0 && i.segment.length == 0) {
- return DR_TRUE; // "/" style root.
- }
-
- return DR_FALSE;
-}
-
-dr_bool32 drpath_is_win32_style_root_segment(const drpath_iterator i)
-{
- if (i.path == NULL) {
- return DR_FALSE;
- }
-
- if (i.segment.offset == 0 && i.segment.length == 2) {
- if (((i.path[0] >= 'a' && i.path[0] <= 'z') || (i.path[0] >= 'A' && i.path[0] <= 'Z')) && i.path[1] == ':') {
- return DR_TRUE;
- }
- }
-
- return DR_FALSE;
-}
-
-
-void drpath_to_forward_slashes(char* path)
-{
- if (path == NULL) {
- return;
- }
-
- while (path[0] != '\0') {
- if (path[0] == '\\') {
- path[0] = '/';
- }
-
- path += 1;
- }
-}
-
-void drpath_to_backslashes(char* path)
-{
- if (path == NULL) {
- return;
- }
-
- while (path[0] != '\0') {
- if (path[0] == '/') {
- path[0] = '\\';
- }
-
- path += 1;
- }
-}
-
-
-dr_bool32 drpath_is_descendant(const char* descendantAbsolutePath, const char* parentAbsolutePath)
-{
- drpath_iterator iChild;
- if (!drpath_first(descendantAbsolutePath, &iChild)) {
- return DR_FALSE; // The descendant is an empty string which makes it impossible for it to be a descendant.
- }
-
- drpath_iterator iParent;
- if (drpath_first(parentAbsolutePath, &iParent))
- {
- do
- {
- // If the segment is different, the paths are different and thus it is not a descendant.
- if (!drpath_iterators_equal(iParent, iChild)) {
- return DR_FALSE;
- }
-
- if (!drpath_next(&iChild)) {
- return DR_FALSE; // The descendant is shorter which means it's impossible for it to be a descendant.
- }
-
- } while (drpath_next(&iParent));
- }
-
- return DR_TRUE;
-}
-
-dr_bool32 drpath_is_child(const char* childAbsolutePath, const char* parentAbsolutePath)
-{
- drpath_iterator iChild;
- if (!drpath_first(childAbsolutePath, &iChild)) {
- return DR_FALSE; // The descendant is an empty string which makes it impossible for it to be a descendant.
- }
-
- drpath_iterator iParent;
- if (drpath_first(parentAbsolutePath, &iParent))
- {
- do
- {
- // If the segment is different, the paths are different and thus it is not a descendant.
- if (!drpath_iterators_equal(iParent, iChild)) {
- return DR_FALSE;
- }
-
- if (!drpath_next(&iChild)) {
- return DR_FALSE; // The descendant is shorter which means it's impossible for it to be a descendant.
- }
-
- } while (drpath_next(&iParent));
- }
-
- // At this point we have finished iteration of the parent, which should be shorter one. We now do one more iterations of
- // the child to ensure it is indeed a direct child and not a deeper descendant.
- return !drpath_next(&iChild);
-}
-
-char* drpath_base_path(char* path)
-{
- if (path == NULL) {
- return NULL;
- }
-
- char* pathorig = path;
- char* baseend = path;
-
- // We just loop through the path until we find the last slash.
- while (path[0] != '\0') {
- if (path[0] == '/' || path[0] == '\\') {
- baseend = path;
- }
-
- path += 1;
- }
-
-
- // Now we just loop backwards until we hit the first non-slash.
- while (baseend > path) {
- if (baseend[0] != '/' && baseend[0] != '\\') {
- break;
- }
-
- baseend -= 1;
- }
-
-
- // We just put a null terminator on the end.
- baseend[0] = '\0';
-
- return pathorig;
-}
-
-void drpath_copy_base_path(const char* path, char* baseOut, size_t baseSizeInBytes)
-{
- if (path == NULL || baseOut == NULL || baseSizeInBytes == 0) {
- return;
- }
-
- strcpy_s(baseOut, baseSizeInBytes, path);
- drpath_base_path(baseOut);
-}
-
-const char* drpath_file_name(const char* path)
-{
- if (path == NULL) {
- return NULL;
- }
-
- const char* fileName = path;
-
- // We just loop through the path until we find the last slash.
- while (path[0] != '\0') {
- if (path[0] == '/' || path[0] == '\\') {
- fileName = path;
- }
-
- path += 1;
- }
-
- // At this point the file name is sitting on a slash, so just move forward.
- while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
- fileName += 1;
- }
-
- return fileName;
-}
-
-const char* drpath_copy_file_name(const char* path, char* fileNameOut, size_t fileNameSizeInBytes)
-{
- const char* fileName = drpath_file_name(path);
- if (fileName != NULL) {
- strcpy_s(fileNameOut, fileNameSizeInBytes, fileName);
- }
-
- return fileName;
-}
-
-const char* drpath_extension(const char* path)
-{
- if (path == NULL) {
- return NULL;
- }
-
- const char* extension = drpath_file_name(path);
- const char* lastoccurance = 0;
-
- // Just find the last '.' and return.
- while (extension[0] != '\0')
- {
- if (extension[0] == '.') {
- extension += 1;
- lastoccurance = extension;
- }
-
- extension += 1;
- }
-
- return (lastoccurance != 0) ? lastoccurance : extension;
-}
-
-
-dr_bool32 drpath_equal(const char* path1, const char* path2)
-{
- if (path1 == NULL || path2 == NULL) {
- return DR_FALSE;
- }
-
- if (path1 == path2 || (path1[0] == '\0' && path2[0] == '\0')) {
- return DR_TRUE; // Two empty paths are treated as the same.
- }
-
- drpath_iterator iPath1;
- drpath_iterator iPath2;
- if (drpath_first(path1, &iPath1) && drpath_first(path2, &iPath2))
- {
- dr_bool32 isPath1Valid;
- dr_bool32 isPath2Valid;
-
- do
- {
- if (!drpath_iterators_equal(iPath1, iPath2)) {
- return DR_FALSE;
- }
-
- isPath1Valid = drpath_next(&iPath1);
- isPath2Valid = drpath_next(&iPath2);
-
- } while (isPath1Valid && isPath2Valid);
-
- // At this point either iPath1 and/or iPath2 have finished iterating. If both of them are at the end, the two paths are equal.
- return isPath1Valid == isPath2Valid && iPath1.path[iPath1.segment.offset] == '\0' && iPath2.path[iPath2.segment.offset] == '\0';
- }
-
- return DR_FALSE;
-}
-
-dr_bool32 drpath_extension_equal(const char* path, const char* extension)
-{
- if (path == NULL || extension == NULL) {
- return DR_FALSE;
- }
-
- const char* ext1 = extension;
- const char* ext2 = drpath_extension(path);
-
-#ifdef _MSC_VER
- return _stricmp(ext1, ext2) == 0;
-#else
- return strcasecmp(ext1, ext2) == 0;
-#endif
-}
-
-
-
-dr_bool32 drpath_is_relative(const char* path)
-{
- if (path == NULL) {
- return DR_FALSE;
- }
-
- drpath_iterator seg;
- if (drpath_first(path, &seg)) {
- return !drpath_is_root_segment(seg);
- }
-
- // We'll get here if the path is empty. We consider this to be a relative path.
- return DR_TRUE;
-}
-
-dr_bool32 drpath_is_absolute(const char* path)
-{
- return !drpath_is_relative(path);
-}
-
-
-dr_bool32 drpath_append(char* base, size_t baseBufferSizeInBytes, const char* other)
-{
- if (base == NULL || other == NULL) {
- return DR_FALSE;
- }
-
- size_t path1Length = strlen(base);
- size_t path2Length = strlen(other);
-
- if (path1Length >= baseBufferSizeInBytes) {
- return DR_FALSE;
- }
-
-
- // Slash.
- if (path1Length > 0 && base[path1Length - 1] != '/' && base[path1Length - 1] != '\\') {
- base[path1Length] = '/';
- path1Length += 1;
- }
-
- // Other part.
- if (path1Length + path2Length >= baseBufferSizeInBytes) {
- path2Length = baseBufferSizeInBytes - path1Length - 1; // -1 for the null terminator.
- }
-
- strncpy_s(base + path1Length, baseBufferSizeInBytes - path1Length, other, path2Length);
-
- return DR_TRUE;
-}
-
-dr_bool32 drpath_append_iterator(char* base, size_t baseBufferSizeInBytes, drpath_iterator i)
-{
- if (base == NULL) {
- return DR_FALSE;
- }
-
- size_t path1Length = strlen(base);
- size_t path2Length = i.segment.length;
-
- if (path1Length >= baseBufferSizeInBytes) {
- return DR_FALSE;
- }
-
- if (drpath_is_linux_style_root_segment(i)) {
- if (baseBufferSizeInBytes > 1) {
- base[0] = '/';
- base[1] = '\0';
- return DR_TRUE;
- }
- }
-
- // Slash.
- if (path1Length > 0 && base[path1Length - 1] != '/' && base[path1Length - 1] != '\\') {
- base[path1Length] = '/';
- path1Length += 1;
- }
-
- // Other part.
- if (path1Length + path2Length >= baseBufferSizeInBytes) {
- path2Length = baseBufferSizeInBytes - path1Length - 1; // -1 for the null terminator.
- }
-
- strncpy_s(base + path1Length, baseBufferSizeInBytes - path1Length, i.path + i.segment.offset, path2Length);
-
- return DR_TRUE;
-}
-
-dr_bool32 drpath_append_extension(char* base, size_t baseBufferSizeInBytes, const char* extension)
-{
- if (base == NULL || extension == NULL) {
- return DR_FALSE;
- }
-
- size_t baseLength = strlen(base);
- size_t extLength = strlen(extension);
-
- if (baseLength >= baseBufferSizeInBytes) {
- return DR_FALSE;
- }
-
- base[baseLength] = '.';
- baseLength += 1;
-
- if (baseLength + extLength >= baseBufferSizeInBytes) {
- extLength = baseBufferSizeInBytes - baseLength - 1; // -1 for the null terminator.
- }
-
- strncpy_s(base + baseLength, baseBufferSizeInBytes - baseLength, extension, extLength);
-
- return DR_TRUE;
-}
-
-dr_bool32 drpath_copy_and_append(char* dst, size_t dstSizeInBytes, const char* base, const char* other)
-{
- if (dst == NULL || dstSizeInBytes == 0) {
- return DR_FALSE;
- }
-
- strcpy_s(dst, dstSizeInBytes, base);
- return drpath_append(dst, dstSizeInBytes, other);
-}
-
-dr_bool32 drpath_copy_and_append_iterator(char* dst, size_t dstSizeInBytes, const char* base, drpath_iterator i)
-{
- if (dst == NULL || dstSizeInBytes == 0) {
- return DR_FALSE;
- }
-
- strcpy_s(dst, dstSizeInBytes, base);
- return drpath_append_iterator(dst, dstSizeInBytes, i);
-}
-
-dr_bool32 drpath_copy_and_append_extension(char* dst, size_t dstSizeInBytes, const char* base, const char* extension)
-{
- if (dst == NULL || dstSizeInBytes == 0) {
- return DR_FALSE;
- }
-
- strcpy_s(dst, dstSizeInBytes, base);
- return drpath_append_extension(dst, dstSizeInBytes, extension);
-}
-
-
-
-// This function recursively cleans a path which is defined as a chain of iterators. This function works backwards, which means at the time of calling this
-// function, each iterator in the chain should be placed at the end of the path.
-//
-// This does not write the null terminator, nor a leading slash for absolute paths.
-size_t _drpath_clean_trywrite(drpath_iterator* iterators, unsigned int iteratorCount, char* pathOut, size_t pathOutSizeInBytes, unsigned int ignoreCounter)
-{
- if (iteratorCount == 0) {
- return 0;
- }
-
- drpath_iterator isegment = iterators[iteratorCount - 1];
-
-
- // If this segment is a ".", we ignore it. If it is a ".." we ignore it and increment "ignoreCount".
- int ignoreThisSegment = ignoreCounter > 0 && isegment.segment.length > 0;
-
- if (isegment.segment.length == 1 && isegment.path[isegment.segment.offset] == '.')
- {
- // "."
- ignoreThisSegment = 1;
- }
- else
- {
- if (isegment.segment.length == 2 && isegment.path[isegment.segment.offset] == '.' && isegment.path[isegment.segment.offset + 1] == '.')
- {
- // ".."
- ignoreThisSegment = 1;
- ignoreCounter += 1;
- }
- else
- {
- // It's a regular segment, so decrement the ignore counter.
- if (ignoreCounter > 0) {
- ignoreCounter -= 1;
- }
- }
- }
-
-
- // The previous segment needs to be written before we can write this one.
- size_t bytesWritten = 0;
-
- drpath_iterator prev = isegment;
- if (!drpath_prev(&prev))
- {
- if (iteratorCount > 1)
- {
- iteratorCount -= 1;
- prev = iterators[iteratorCount - 1];
- }
- else
- {
- prev.path = NULL;
- prev.segment.offset = 0;
- prev.segment.length = 0;
- }
- }
-
- if (prev.segment.length > 0)
- {
- iterators[iteratorCount - 1] = prev;
- bytesWritten = _drpath_clean_trywrite(iterators, iteratorCount, pathOut, pathOutSizeInBytes, ignoreCounter);
- }
-
-
- if (!ignoreThisSegment)
- {
- if (pathOutSizeInBytes > 0)
- {
- pathOut += bytesWritten;
- pathOutSizeInBytes -= bytesWritten;
-
- if (bytesWritten > 0)
- {
- pathOut[0] = '/';
- bytesWritten += 1;
-
- pathOut += 1;
- pathOutSizeInBytes -= 1;
- }
-
- if (pathOutSizeInBytes >= isegment.segment.length)
- {
- strncpy_s(pathOut, pathOutSizeInBytes, isegment.path + isegment.segment.offset, isegment.segment.length);
- bytesWritten += isegment.segment.length;
- }
- else
- {
- strncpy_s(pathOut, pathOutSizeInBytes, isegment.path + isegment.segment.offset, pathOutSizeInBytes);
- bytesWritten += pathOutSizeInBytes;
- }
- }
- }
-
- return bytesWritten;
-}
-
-size_t drpath_clean(const char* path, char* pathOut, size_t pathOutSizeInBytes)
-{
- if (path == NULL) {
- return 0;
- }
-
- drpath_iterator last;
- if (drpath_last(path, &last))
- {
- size_t bytesWritten = 0;
- if (path[0] == '/') {
- if (pathOut != NULL && pathOutSizeInBytes > 1) {
- pathOut[0] = '/';
- bytesWritten = 1;
- }
- }
-
- bytesWritten += _drpath_clean_trywrite(&last, 1, pathOut + bytesWritten, pathOutSizeInBytes - bytesWritten - 1, 0); // -1 to ensure there is enough room for a null terminator later on.
- if (pathOutSizeInBytes > bytesWritten) {
- pathOut[bytesWritten] = '\0';
- }
-
- return bytesWritten + 1;
- }
-
- return 0;
-}
-
-size_t drpath_append_and_clean(char* dst, size_t dstSizeInBytes, const char* base, const char* other)
-{
- if (base == NULL || other == NULL) {
- return 0;
- }
-
- drpath_iterator last[2] = {
- {NULL, {0, 0}},
- {NULL, {0, 0}}
- };
-
- dr_bool32 isPathEmpty0 = !drpath_last(base, last + 0);
- dr_bool32 isPathEmpty1 = !drpath_last(other, last + 1);
-
- int iteratorCount = !isPathEmpty0 + !isPathEmpty1;
- if (iteratorCount == 0) {
- return 0; // Both input strings are empty.
- }
-
- size_t bytesWritten = 0;
- if (base[0] == '/') {
- if (dst != NULL && dstSizeInBytes > 1) {
- dst[0] = '/';
- bytesWritten = 1;
- }
- }
-
- bytesWritten += _drpath_clean_trywrite(last, 2, dst + bytesWritten, dstSizeInBytes - bytesWritten - 1, 0); // -1 to ensure there is enough room for a null terminator later on.
- if (dstSizeInBytes > bytesWritten) {
- dst[bytesWritten] = '\0';
- }
-
- return bytesWritten + 1;
-}
-
-
-dr_bool32 drpath_remove_extension(char* path)
-{
- if (path == NULL) {
- return DR_FALSE;
- }
-
- const char* extension = drpath_extension(path);
- if (extension != NULL) {
- extension -= 1;
- path[(extension - path)] = '\0';
- }
-
- return DR_TRUE;
-}
-
-dr_bool32 drpath_copy_and_remove_extension(char* dst, size_t dstSizeInBytes, const char* path)
-{
- if (dst == NULL || dstSizeInBytes == 0 || path == NULL) {
- return DR_FALSE;
- }
-
- const char* extension = drpath_extension(path);
- if (extension != NULL) {
- extension -= 1;
- }
-
- strncpy_s(dst, dstSizeInBytes, path, (size_t)(extension - path));
- return DR_TRUE;
-}
-
-
-dr_bool32 drpath_remove_file_name(char* path)
-{
- if (path == NULL) {
- return DR_FALSE;
- }
-
-
- // We just create an iterator that starts at the last segment. We then move back one and place a null terminator at the end of
- // that segment. That will ensure the resulting path is not left with a slash.
- drpath_iterator iLast;
- if (!drpath_last(path, &iLast)) {
- return DR_FALSE; // The path is empty.
- }
-
- if (drpath_is_root_segment(iLast)) {
- return DR_FALSE;
- }
-
-
- drpath_iterator iSecondLast = iLast;
- if (drpath_prev(&iSecondLast))
- {
- // There is a segment before it so we just place a null terminator at the end, but only if it's not the root of a Linux-style absolute path.
- if (drpath_is_linux_style_root_segment(iSecondLast)) {
- path[iLast.segment.offset] = '\0';
- } else {
- path[iSecondLast.segment.offset + iSecondLast.segment.length] = '\0';
- }
- }
- else
- {
- // This is already the last segment, so just place a null terminator at the beginning of the string.
- path[0] = '\0';
- }
-
- return DR_TRUE;
-}
-
-dr_bool32 drpath_copy_and_remove_file_name(char* dst, size_t dstSizeInBytes, const char* path)
-{
- if (dst == NULL) {
- return DR_FALSE;
- }
- if (dstSizeInBytes == 0) {
- return DR_FALSE;
- }
- if (path == NULL) {
- dst[0] = '\0';
- return DR_FALSE;
- }
-
-
- // We just create an iterator that starts at the last segment. We then move back one and place a null terminator at the end of
- // that segment. That will ensure the resulting path is not left with a slash.
- drpath_iterator iLast;
- if (!drpath_last(path, &iLast)) {
- dst[0] = '\0';
- return DR_FALSE; // The path is empty.
- }
-
- if (drpath_is_linux_style_root_segment(iLast)) {
- if (dstSizeInBytes > 1) {
- dst[0] = '/'; dst[1] = '\0';
- return DR_FALSE;
- } else {
- dst[0] = '\0';
- return DR_FALSE;
- }
- }
-
- if (drpath_is_win32_style_root_segment(iLast)) {
- return strncpy_s(dst, dstSizeInBytes, path + iLast.segment.offset, iLast.segment.length) == 0;
- }
-
-
- drpath_iterator iSecondLast = iLast;
- if (drpath_prev(&iSecondLast))
- {
- // There is a segment before it so we just place a null terminator at the end, but only if it's not the root of a Linux-style absolute path.
- if (drpath_is_linux_style_root_segment(iSecondLast)) {
- return strcpy_s(dst, dstSizeInBytes, "/") == 0;
- } else {
- return strncpy_s(dst, dstSizeInBytes, path, iSecondLast.segment.offset + iSecondLast.segment.length) == 0;
- }
- }
- else
- {
- // This is already the last segment, so just place a null terminator at the beginning of the string.
- dst[0] = '\0';
- return DR_TRUE;
- }
-}
-
-
-dr_bool32 drpath_to_relative(const char* absolutePathToMakeRelative, const char* absolutePathToMakeRelativeTo, char* relativePathOut, size_t relativePathOutSizeInBytes)
-{
- // We do this in two phases. The first phase just iterates past each segment of both the path to convert and the
- // base path until we find two that are not equal. The second phase just adds the appropriate ".." segments.
-
- if (relativePathOut == 0) {
- return DR_FALSE;
- }
- if (relativePathOutSizeInBytes == 0) {
- return DR_FALSE;
- }
-
- if (!drpath_is_absolute(absolutePathToMakeRelative) || !drpath_is_absolute(absolutePathToMakeRelativeTo)) {
- return DR_FALSE;
- }
-
-
- drpath_iterator iPath;
- drpath_iterator iBase;
- dr_bool32 isPathEmpty = !drpath_first(absolutePathToMakeRelative, &iPath);
- dr_bool32 isBaseEmpty = !drpath_first(absolutePathToMakeRelativeTo, &iBase);
-
- if (isPathEmpty && isBaseEmpty)
- {
- // Looks like both paths are empty.
- relativePathOut[0] = '\0';
- return DR_FALSE;
- }
-
-
- // Phase 1: Get past the common section.
- int isPathAtEnd = 0;
- int isBaseAtEnd = 0;
- while (!isPathAtEnd && !isBaseAtEnd && drpath_iterators_equal(iPath, iBase))
- {
- isPathAtEnd = !drpath_next(&iPath);
- isBaseAtEnd = !drpath_next(&iBase);
- }
-
- if (iPath.segment.offset == 0)
- {
- // The path is not relative to the base path.
- relativePathOut[0] = '\0';
- return DR_FALSE;
- }
-
-
- // Phase 2: Append ".." segments - one for each remaining segment in the base path.
- char* pDst = relativePathOut;
- size_t bytesAvailable = relativePathOutSizeInBytes;
-
- if (!drpath_at_end(iBase))
- {
- do
- {
- if (pDst != relativePathOut)
- {
- if (bytesAvailable <= 3)
- {
- // Ran out of room.
- relativePathOut[0] = '\0';
- return DR_FALSE;
- }
-
- pDst[0] = '/';
- pDst[1] = '.';
- pDst[2] = '.';
-
- pDst += 3;
- bytesAvailable -= 3;
- }
- else
- {
- // It's the first segment, so we need to ensure we don't lead with a slash.
- if (bytesAvailable <= 2)
- {
- // Ran out of room.
- relativePathOut[0] = '\0';
- return DR_FALSE;
- }
-
- pDst[0] = '.';
- pDst[1] = '.';
-
- pDst += 2;
- bytesAvailable -= 2;
- }
- } while (drpath_next(&iBase));
- }
-
-
- // Now we just append whatever is left of the main path. We want the path to be clean, so we append segment-by-segment.
- if (!drpath_at_end(iPath))
- {
- do
- {
- // Leading slash, if required.
- if (pDst != relativePathOut)
- {
- if (bytesAvailable <= 1)
- {
- // Ran out of room.
- relativePathOut[0] = '\0';
- return DR_FALSE;
- }
-
- pDst[0] = '/';
-
- pDst += 1;
- bytesAvailable -= 1;
- }
-
-
- if (bytesAvailable <= iPath.segment.length)
- {
- // Ran out of room.
- relativePathOut[0] = '\0';
- return DR_FALSE;
- }
-
- if (strncpy_s(pDst, bytesAvailable, iPath.path + iPath.segment.offset, iPath.segment.length) != 0)
- {
- // Error copying the string. Probably ran out of room in the output buffer.
- relativePathOut[0] = '\0';
- return DR_FALSE;
- }
-
- pDst += iPath.segment.length;
- bytesAvailable -= iPath.segment.length;
-
-
- } while (drpath_next(&iPath));
- }
-
-
- // Always null terminate.
- //assert(bytesAvailable > 0);
- pDst[0] = '\0';
-
- return DR_TRUE;
-}
-
-dr_bool32 drpath_to_absolute(const char* relativePathToMakeAbsolute, const char* basePath, char* absolutePathOut, size_t absolutePathOutSizeInBytes)
-{
- return drpath_append_and_clean(absolutePathOut, absolutePathOutSizeInBytes, basePath, relativePathToMakeAbsolute) != 0;
-}
-#endif //DR_IMPLEMENTATION
-
-
-
-/*
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-For more information, please refer to
-*/
diff --git a/source/engine/thirdparty/dr_libs/old/dr_2d.h b/source/engine/thirdparty/dr_libs/old/dr_2d.h
deleted file mode 100644
index 36d1a40..0000000
--- a/source/engine/thirdparty/dr_libs/old/dr_2d.h
+++ /dev/null
@@ -1,3417 +0,0 @@
-// Public Domain. See "unlicense" statement at the end of this file.
-
-// ABOUT
-//
-// dr_2d is a simple library for drawing simple 2D graphics.
-//
-//
-//
-// USAGE
-//
-// This is a single-file library. To use it, do something like the following in one .c file.
-// #define DR_2D_IMPLEMENTATION
-// #include "dr_2d.h"
-//
-// You can then #include dr_2d.h in other parts of the program as you would with any other header file.
-//
-//
-//
-// QUICK NOTES
-//
-// - Drawing must be done inside a dr2d_begin_draw() and dr2d_end_draw() pair. Rationale: 1) required for compatibility
-// with GDI's BeginPaint() and EndPaint() APIs; 2) gives implementations opportunity to save and restore state, such as
-// OpenGL state and whatnot.
-// - This library is not thread safe.
-//
-//
-//
-// OPTIONS
-//
-// #define DR2D_NO_GDI
-// Excludes the GDI back-end.
-//
-// #define DR2D_NO_CAIRO
-// Excludes the Cairo back-end.
-//
-//
-//
-// TODO
-// - Document resource management.
-
-#ifndef dr_2d_h
-#define dr_2d_h
-
-#include
-#include
-
-#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN64)
-#include
-
-// No Cairo on Win32 builds.
-#ifndef DR2D_NO_CAIRO
-#define DR2D_NO_CAIRO
-#endif
-#else
-// No GDI on non-Win32 builds.
-#ifndef DR2D_NO_GDI
-#define DR2D_NO_GDI
-#endif
-#endif
-
-#ifndef DR2D_MAX_FONT_FAMILY_LENGTH
-#define DR2D_MAX_FONT_FAMILY_LENGTH 128
-#endif
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-/////////////////////////////////////////////////////////////////
-//
-// CORE 2D API
-//
-/////////////////////////////////////////////////////////////////
-
-typedef unsigned char dr2d_byte;
-
-typedef struct dr2d_context dr2d_context;
-typedef struct dr2d_surface dr2d_surface;
-typedef struct dr2d_font dr2d_font;
-typedef struct dr2d_image dr2d_image;
-typedef struct dr2d_color dr2d_color;
-typedef struct dr2d_font_metrics dr2d_font_metrics;
-typedef struct dr2d_glyph_metrics dr2d_glyph_metrics;
-typedef struct dr2d_drawing_callbacks dr2d_drawing_callbacks;
-
-
-/// Structure representing an RGBA color. Color components are specified in the range of 0 - 255.
-struct dr2d_color
-{
- dr2d_byte r;
- dr2d_byte g;
- dr2d_byte b;
- dr2d_byte a;
-};
-
-struct dr2d_font_metrics
-{
- int ascent;
- int descent;
- int lineHeight;
- int spaceWidth;
-};
-
-struct dr2d_glyph_metrics
-{
- int width;
- int height;
- int originX;
- int originY;
- int advanceX;
- int advanceY;
-};
-
-typedef enum
-{
- dr2d_font_weight_medium = 0,
- dr2d_font_weight_thin,
- dr2d_font_weight_extra_light,
- dr2d_font_weight_light,
- dr2d_font_weight_semi_light,
- dr2d_font_weight_book,
- dr2d_font_weight_semi_bold,
- dr2d_font_weight_bold,
- dr2d_font_weight_extra_bold,
- dr2d_font_weight_heavy,
- dr2d_font_weight_extra_heavy,
-
- dr2d_font_weight_normal = dr2d_font_weight_medium,
- dr2d_font_weight_default = dr2d_font_weight_medium
-
-} dr2d_font_weight;
-
-typedef enum
-{
- dr2d_font_slant_none = 0,
- dr2d_font_slant_italic,
- dr2d_font_slant_oblique
-
-} dr2d_font_slant;
-
-typedef enum
-{
- dr2d_image_format_rgba8,
- dr2d_image_format_bgra8,
- dr2d_image_format_argb8,
-} dr2d_image_format;
-
-
-#define DR2D_IMAGE_DRAW_BACKGROUND (1 << 0)
-#define DR2D_IMAGE_HINT_NO_ALPHA (1 << 1)
-
-#define DR2D_READ (1 << 0)
-#define DR2D_WRITE (1 << 1)
-
-#define DR2D_FONT_NO_CLEARTYPE (1 << 0)
-
-typedef struct
-{
- /// The destination position on the x axis. This is ignored if the DR2D_IMAGE_ALIGN_CENTER option is set.
- float dstX;
-
- /// The destination position on the y axis. This is ignored if the DR2D_IMAGE_ALIGN_CENTER option is set.
- float dstY;
-
- /// The destination width.
- float dstWidth;
-
- /// The destination height.
- float dstHeight;
-
-
- /// The source offset on the x axis.
- float srcX;
-
- /// The source offset on the y axis.
- float srcY;
-
- /// The source width.
- float srcWidth;
-
- /// The source height.
- float srcHeight;
-
-
- /// The foreground tint color. This is not applied to the background color, and the alpha component is ignored.
- dr2d_color foregroundTint;
-
- /// The background color. Only used if the DR2D_IMAGE_DRAW_BACKGROUND option is set.
- dr2d_color backgroundColor;
-
-
- /// Flags for controlling how the image should be drawn.
- unsigned int options;
-
-} dr2d_draw_image_args;
-
-
-typedef bool (* dr2d_on_create_context_proc) (dr2d_context* pContext, const void* pUserData);
-typedef void (* dr2d_on_delete_context_proc) (dr2d_context* pContext);
-typedef bool (* dr2d_on_create_surface_proc) (dr2d_surface* pSurface, float width, float height);
-typedef void (* dr2d_on_delete_surface_proc) (dr2d_surface* pSurface);
-typedef bool (* dr2d_on_create_font_proc) (dr2d_font* pFont);
-typedef void (* dr2d_on_delete_font_proc) (dr2d_font* pFont);
-typedef bool (* dr2d_on_create_image_proc) (dr2d_image* pImage, unsigned int stride, const void* pData);
-typedef void (* dr2d_on_delete_image_proc) (dr2d_image* pImage);
-typedef void (* dr2d_begin_draw_proc) (dr2d_surface* pSurface);
-typedef void (* dr2d_end_draw_proc) (dr2d_surface* pSurface);
-typedef void (* dr2d_clear_proc) (dr2d_surface* pSurface, dr2d_color color);
-typedef void (* dr2d_draw_rect_proc) (dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color);
-typedef void (* dr2d_draw_rect_outline_proc) (dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float outlineWidth);
-typedef void (* dr2d_draw_rect_with_outline_proc) (dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float outlineWidth, dr2d_color outlineColor);
-typedef void (* dr2d_draw_round_rect_proc) (dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float width);
-typedef void (* dr2d_draw_round_rect_outline_proc) (dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float width, float outlineWidth);
-typedef void (* dr2d_draw_round_rect_with_outline_proc) (dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float width, float outlineWidth, dr2d_color outlineColor);
-typedef void (* dr2d_draw_text_proc) (dr2d_surface* pSurface, dr2d_font* pFont, const char* text, size_t textSizeInBytes, float posX, float posY, dr2d_color color, dr2d_color backgroundColor);
-typedef void (* dr2d_draw_image_proc) (dr2d_surface* pSurface, dr2d_image* pImage, dr2d_draw_image_args* pArgs);
-typedef void (* dr2d_set_clip_proc) (dr2d_surface* pSurface, float left, float top, float right, float bottom);
-typedef void (* dr2d_get_clip_proc) (dr2d_surface* pSurface, float* pLeftOut, float* pTopOut, float* pRightOut, float* pBottomOut);
-typedef dr2d_image_format (* dr2d_get_optimal_image_format_proc) (dr2d_context* pContext);
-typedef void* (* dr2d_map_image_data_proc) (dr2d_image* pImage, unsigned int accessFlags);
-typedef void (* dr2d_unmap_image_data_proc) (dr2d_image* pImage);
-typedef bool (* dr2d_get_font_metrics_proc) (dr2d_font* pFont, dr2d_font_metrics* pMetricsOut);
-typedef bool (* dr2d_get_glyph_metrics_proc) (dr2d_font* pFont, unsigned int utf32, dr2d_glyph_metrics* pMetricsOut);
-typedef bool (* dr2d_measure_string_proc) (dr2d_font* pFont, const char* text, size_t textSizeInBytes, float* pWidthOut, float* pHeightOut);
-typedef bool (* dr2d_get_text_cursor_position_from_point_proc)(dr2d_font* pFont, const char* text, size_t textSizeInBytes, float maxWidth, float inputPosX, float* pTextCursorPosXOut, size_t* pCharacterIndexOut);
-typedef bool (* dr2d_get_text_cursor_position_from_char_proc) (dr2d_font* pFont, const char* text, size_t characterIndex, float* pTextCursorPosXOut);
-
-
-struct dr2d_drawing_callbacks
-{
- dr2d_on_create_context_proc on_create_context;
- dr2d_on_delete_context_proc on_delete_context;
- dr2d_on_create_surface_proc on_create_surface;
- dr2d_on_delete_surface_proc on_delete_surface;
- dr2d_on_create_font_proc on_create_font;
- dr2d_on_delete_font_proc on_delete_font;
- dr2d_on_create_image_proc on_create_image;
- dr2d_on_delete_image_proc on_delete_image;
-
- dr2d_begin_draw_proc begin_draw;
- dr2d_end_draw_proc end_draw;
- dr2d_clear_proc clear;
- dr2d_draw_rect_proc draw_rect;
- dr2d_draw_rect_outline_proc draw_rect_outline;
- dr2d_draw_rect_with_outline_proc draw_rect_with_outline;
- dr2d_draw_round_rect_proc draw_round_rect;
- dr2d_draw_round_rect_outline_proc draw_round_rect_outline;
- dr2d_draw_round_rect_with_outline_proc draw_round_rect_with_outline;
- dr2d_draw_text_proc draw_text;
- dr2d_draw_image_proc draw_image;
- dr2d_set_clip_proc set_clip;
- dr2d_get_clip_proc get_clip;
-
- dr2d_get_optimal_image_format_proc get_optimal_image_format;
- dr2d_map_image_data_proc map_image_data;
- dr2d_unmap_image_data_proc unmap_image_data;
-
- dr2d_get_font_metrics_proc get_font_metrics;
- dr2d_get_glyph_metrics_proc get_glyph_metrics;
- dr2d_measure_string_proc measure_string;
- dr2d_get_text_cursor_position_from_point_proc get_text_cursor_position_from_point;
- dr2d_get_text_cursor_position_from_char_proc get_text_cursor_position_from_char;
-};
-
-struct dr2d_image
-{
- /// A pointer to the context that owns the image.
- dr2d_context* pContext;
-
- /// The width of the image.
- unsigned int width;
-
- /// The height of the image.
- unsigned int height;
-
- /// The format of the image data.
- dr2d_image_format format;
-
- /// Whether or not the image's data is already mapped.
- bool isMapped;
-
- /// The extra bytes. The size of this buffer is equal to pContext->imageExtraBytes.
- dr2d_byte pExtraData[1];
-};
-
-struct dr2d_font
-{
- /// A pointer to the context that owns the font.
- dr2d_context* pContext;
-
- /// The font family.
- char family[DR2D_MAX_FONT_FAMILY_LENGTH];
-
- /// The size of the font.
- unsigned int size;
-
- /// The font's weight.
- dr2d_font_weight weight;
-
- /// The font's slant.
- dr2d_font_slant slant;
-
- /// The font's rotation, in degrees.
- float rotation;
-
- /// Flags. Can be a combination of the following.
- /// DR2D_FONT_NO_CLEARTYPE
- unsigned int flags;
-
- /// The extra bytes. The size of this buffer is equal to pContext->fontExtraBytes.
- dr2d_byte pExtraData[1];
-};
-
-struct dr2d_surface
-{
- /// A pointer to the context that owns the surface.
- dr2d_context* pContext;
-
- /// The width of the surface.
- float width;
-
- /// The height of the surface.
- float height;
-
- /// The extra bytes. The size of this buffer is equal to pContext->surfaceExtraBytes.
- dr2d_byte pExtraData[1];
-};
-
-struct dr2d_context
-{
- /// The drawing callbacks.
- dr2d_drawing_callbacks drawingCallbacks;
-
- /// The number of extra bytes to allocate for each image.
- size_t imageExtraBytes;
-
- /// The number of extra bytes to allocate for each font.
- size_t fontExtraBytes;
-
- /// The number of extra bytes to allocate for each surface.
- size_t surfaceExtraBytes;
-
- /// The number of extra bytes to allocate for the context.
- size_t contextExtraBytes;
-
- /// The extra bytes.
- dr2d_byte pExtraData[1];
-};
-
-
-
-/// Creats a context.
-dr2d_context* dr2d_create_context(dr2d_drawing_callbacks drawingCallbacks, size_t contextExtraBytes, size_t surfaceExtraBytes, size_t fontExtraBytes, size_t imageExtraBytes, const void* pUserData);
-
-/// Deletes the given context.
-void dr2d_delete_context(dr2d_context* pContext);
-
-/// Retrieves a pointer to the given context's extra data buffer.
-void* dr2d_get_context_extra_data(dr2d_context* pContext);
-
-
-/// Creates a surface.
-dr2d_surface* dr2d_create_surface(dr2d_context* pContext, float width, float height);
-
-/// Deletes the given surface.
-void dr2d_delete_surface(dr2d_surface* pSurface);
-
-/// Retrieves the width of the surface.
-float dr2d_get_surface_width(const dr2d_surface* pSurface);
-
-/// Retrieves the height of the surface.
-float dr2d_get_surface_height(const dr2d_surface* pSurface);
-
-/// Retrieves a pointer to the given surface's extra data buffer.
-void* dr2d_get_surface_extra_data(dr2d_surface* pSurface);
-
-
-
-//// Drawing ////
-
-/// Marks the beginning of a paint operation.
-void dr2d_begin_draw(dr2d_surface* pSurface);
-
-/// Marks the end of a paint operation.
-void dr2d_end_draw(dr2d_surface* pSurface);
-
-/// Clears the given surface with the given color.
-void dr2d_clear(dr2d_surface* pSurface, dr2d_color color);
-
-/// Draws a filled rectangle without an outline.
-void dr2d_draw_rect(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color);
-
-/// Draws the outline of the given rectangle.
-void dr2d_draw_rect_outline(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float outlineWidth);
-
-/// Draws a filled rectangle with an outline.
-void dr2d_draw_rect_with_outline(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float outlineWidth, dr2d_color outlineColor);
-
-/// Draws a filled rectangle without an outline with rounded corners.
-void dr2d_draw_round_rect(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius);
-
-/// Draws the outline of the given rectangle with rounded corners.
-void dr2d_draw_round_rect_outline(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius, float outlineWidth);
-
-/// Draws a filled rectangle with an outline.
-void dr2d_draw_round_rect_with_outline(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius, float outlineWidth, dr2d_color outlineColor);
-
-/// Draws a run of text.
-void dr2d_draw_text(dr2d_surface* pSurface, dr2d_font* pFont, const char* text, size_t textSizeInBytes, float posX, float posY, dr2d_color color, dr2d_color backgroundColor);
-
-/// Draws an image.
-void dr2d_draw_image(dr2d_surface* pSurface, dr2d_image* pImage, dr2d_draw_image_args* pArgs);
-
-/// Sets the clipping rectangle.
-void dr2d_set_clip(dr2d_surface* pSurface, float left, float top, float right, float bottom);
-
-/// Retrieves the clipping rectangle.
-void dr2d_get_clip(dr2d_surface* pSurface, float* pLeftOut, float* pTopOut, float* pRightOut, float* pBottomOut);
-
-
-/// Creates a font that can be passed to dr2d_draw_text().
-dr2d_font* dr2d_create_font(dr2d_context* pContext, const char* family, unsigned int size, dr2d_font_weight weight, dr2d_font_slant slant, float rotation, unsigned int flags);
-
-/// Deletes a font that was previously created with dr2d_create_font()
-void dr2d_delete_font(dr2d_font* pFont);
-
-/// Retrieves a pointer to the given font's extra data buffer.
-void* dr2d_get_font_extra_data(dr2d_font* pFont);
-
-/// Retrieves the size of the given font.
-unsigned int dr2d_get_font_size(dr2d_font* pFont);
-
-/// Retrieves the metrics of the given font.
-bool dr2d_get_font_metrics(dr2d_font* pFont, dr2d_font_metrics* pMetricsOut);
-
-/// Retrieves the metrics of the glyph for the given character when rendered with the given font.
-bool dr2d_get_glyph_metrics(dr2d_font* pFont, unsigned int utf32, dr2d_glyph_metrics* pMetricsOut);
-
-/// Retrieves the dimensions of the given string when drawn with the given font.
-bool dr2d_measure_string(dr2d_font* pFont, const char* text, size_t textSizeInBytes, float* pWidthOut, float* pHeightOut);
-
-/// Retrieves the position to place a text cursor based on the given point for the given string when drawn with the given font.
-bool dr2d_get_text_cursor_position_from_point(dr2d_font* pFont, const char* text, size_t textSizeInBytes, float maxWidth, float inputPosX, float* pTextCursorPosXOut, size_t* pCharacterIndexOut);
-
-/// Retrieves the position to palce a text cursor based on the character at the given index for the given string when drawn with the given font.
-bool dr2d_get_text_cursor_position_from_char(dr2d_font* pFont, const char* text, size_t characterIndex, float* pTextCursorPosXOut);
-
-
-/// Creates an image that can be passed to dr2d_draw_image().
-///
-/// @remarks
-/// The dimensions and format of an image are immutable. If these need to change, then the image needs to be deleted and re-created.
-/// @par
-/// If pData is NULL, the default image data is undefined.
-/// @par
-/// Use dr2d_map_image_data() and dr2d_unmap_image_data() to update or retrieve image data.
-dr2d_image* dr2d_create_image(dr2d_context* pContext, unsigned int width, unsigned int height, dr2d_image_format format, unsigned int stride, const void* pData);
-
-/// Deletes the given image.
-void dr2d_delete_image(dr2d_image* pImage);
-
-/// Retrieves a pointer to the given image's extra data buffer.
-void* dr2d_get_image_extra_data(dr2d_image* pImage);
-
-/// Retrieves the size of the given image.
-void dr2d_get_image_size(dr2d_image* pImage, unsigned int* pWidthOut, unsigned int* pHeightOut);
-
-/// Retrieves a pointer to a buffer representing the given image's data.
-///
-/// Call dr2d_unmap_image_data() when you are done with this function.
-///
-/// Use this function to access an image's data. The returned pointer does not necessarilly point to the image's actual data, so when
-/// writing to this pointer, nothing is guaranteed to be updated until dr2d_unmap_image_data() is called.
-///
-/// The returned data will contain the image data at the time of the mapping.
-///
-/// This will fail if the image's data is already mapped.
-void* dr2d_map_image_data(dr2d_image* pImage, unsigned int accessFlags);
-
-/// Unmaps the given image's data.
-///
-/// A flush is done at this point to ensure the actual underlying image data is updated.
-void dr2d_unmap_image_data(dr2d_image* pImage);
-
-/// Retrieves the optimal image format for the given context. This depends on the backend.
-dr2d_image_format dr2d_get_optimal_image_format(dr2d_context* pContext);
-
-
-/////////////////////////////////////////////////////////////////
-//
-// UTILITY API
-//
-/////////////////////////////////////////////////////////////////
-
-/// Creates a color object from a set of RGBA color components.
-dr2d_color dr2d_rgba(dr2d_byte r, dr2d_byte g, dr2d_byte b, dr2d_byte a);
-
-/// Creates a fully opaque color object from a set of RGB color components.
-dr2d_color dr2d_rgb(dr2d_byte r, dr2d_byte g, dr2d_byte b);
-
-
-
-
-/////////////////////////////////////////////////////////////////
-//
-// WINDOWS GDI 2D API
-//
-// When using GDI as the rendering back-end you will usually want to only call drawing functions in response to a WM_PAINT message.
-//
-/////////////////////////////////////////////////////////////////
-#ifndef DR2D_NO_GDI
-
-/// Creates a 2D context with GDI as the backend and, optionally, a custom HDC.
-///
-/// hDC [in, optional] The main device context. Can be null.
-dr2d_context* dr2d_create_context_gdi(HDC hDC);
-
-/// Creates a surface that draws directly to the given window.
-///
-/// @remarks
-/// When using this kind of surface, the internal HBITMAP is not used.
-dr2d_surface* dr2d_create_surface_gdi_HWND(dr2d_context* pContext, HWND hWnd);
-dr2d_surface* dr2d_create_surface_gdi_HDC(dr2d_context* pContext, HDC hDC);
-
-/// Retrieves the internal HDC that we have been rendering to for the given surface.
-///
-/// @remarks
-/// This assumes the given surface was created from a context that was created from dr2d_create_context_gdi()
-HDC dr2d_get_HDC(dr2d_surface* pSurface);
-
-/// Retrieves the internal HBITMAP object that we have been rendering to.
-///
-/// @remarks
-/// This assumes the given surface was created from a context that was created from dr2d_create_context_gdi().
-HBITMAP dr2d_get_HBITMAP(dr2d_surface* pSurface);
-
-/// Retrieves the internal HFONT object from the given dr2d_font object.
-HFONT dr2d_get_HFONT(dr2d_font* pFont);
-
-#endif // GDI
-
-
-
-/////////////////////////////////////////////////////////////////
-//
-// CAIRO 2D API
-//
-/////////////////////////////////////////////////////////////////
-#ifndef DR2D_NO_CAIRO
-#include
-
-/// Creates a 2D context with Cairo as the backend.
-dr2d_context* dr2d_create_context_cairo();
-
-/// Creates a surface that draws directly to the given cairo context.
-dr2d_surface* dr2d_create_surface_cairo(dr2d_context* pContext, cairo_t* cr);
-
-/// Retrieves the internal cairo_surface_t object from the given surface.
-///
-/// @remarks
-/// This assumes the given surface was created from a context that was created with dr2d_create_context_cairo().
-cairo_surface_t* dr2d_get_cairo_surface_t(dr2d_surface* pSurface);
-
-/// Retrieves the internal cairo_t object from the given surface.
-cairo_t* dr2d_get_cairo_t(dr2d_surface* pSurface);
-
-#endif // Cairo
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif //dr_2d_h
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// IMPLEMENTATION
-//
-///////////////////////////////////////////////////////////////////////////////
-#ifdef DR_2D_IMPLEMENTATION
-#include
-#include
-#include
-#include
-#include
-
-#ifndef DR2D_PRIVATE
-#define DR2D_PRIVATE static
-#endif
-
-static int dr2d_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
-{
-#ifdef _WIN32
- return strcpy_s(dst, dstSizeInBytes, src);
-#else
- if (dst == 0) {
- return EINVAL;
- }
- if (dstSizeInBytes == 0) {
- return ERANGE;
- }
- if (src == 0) {
- dst[0] = '\0';
- return EINVAL;
- }
-
- size_t i;
- for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
- dst[i] = src[i];
- }
-
- if (i < dstSizeInBytes) {
- dst[i] = '\0';
- return 0;
- }
-
- dst[0] = '\0';
- return ERANGE;
-#endif
-}
-
-
-dr2d_context* dr2d_create_context(dr2d_drawing_callbacks drawingCallbacks, size_t contextExtraBytes, size_t surfaceExtraBytes, size_t fontExtraBytes, size_t imageExtraBytes, const void* pUserData)
-{
- dr2d_context* pContext = (dr2d_context*)malloc(sizeof(dr2d_context) + contextExtraBytes);
- if (pContext != NULL)
- {
- pContext->drawingCallbacks = drawingCallbacks;
- pContext->imageExtraBytes = imageExtraBytes;
- pContext->fontExtraBytes = fontExtraBytes;
- pContext->surfaceExtraBytes = surfaceExtraBytes;
- pContext->contextExtraBytes = contextExtraBytes;
- memset(pContext->pExtraData, 0, contextExtraBytes);
-
- // The create_context callback is optional. If it is set to NULL, we just finish up here and return successfully. Otherwise
- // we want to call the create_context callback and check it's return value. If it's return value if false it means there
- // was an error and we need to return null.
- if (pContext->drawingCallbacks.on_create_context != NULL)
- {
- if (!pContext->drawingCallbacks.on_create_context(pContext, pUserData))
- {
- // An error was thrown from the callback.
- free(pContext);
- pContext = NULL;
- }
- }
- }
-
- return pContext;
-}
-
-void dr2d_delete_context(dr2d_context* pContext)
-{
- if (pContext != NULL) {
- if (pContext->drawingCallbacks.on_delete_context != NULL) {
- pContext->drawingCallbacks.on_delete_context(pContext);
- }
-
- free(pContext);
- }
-}
-
-void* dr2d_get_context_extra_data(dr2d_context* pContext)
-{
- return pContext->pExtraData;
-}
-
-
-dr2d_surface* dr2d_create_surface(dr2d_context* pContext, float width, float height)
-{
- if (pContext != NULL)
- {
- dr2d_surface* pSurface = (dr2d_surface*)malloc(sizeof(dr2d_surface) + pContext->surfaceExtraBytes);
- if (pSurface != NULL)
- {
- pSurface->pContext = pContext;
- pSurface->width = width;
- pSurface->height = height;
- memset(pSurface->pExtraData, 0, pContext->surfaceExtraBytes);
-
- // The create_surface callback is optional. If it is set to NULL, we just finish up here and return successfully. Otherwise
- // we want to call the create_surface callback and check it's return value. If it's return value if false it means there
- // was an error and we need to return null.
- if (pContext->drawingCallbacks.on_create_surface != NULL)
- {
- if (!pContext->drawingCallbacks.on_create_surface(pSurface, width, height))
- {
- free(pSurface);
- pSurface = NULL;
- }
- }
- }
-
- return pSurface;
- }
-
- return NULL;
-}
-
-void dr2d_delete_surface(dr2d_surface* pSurface)
-{
- if (pSurface != NULL)
- {
- assert(pSurface->pContext != NULL);
-
- if (pSurface->pContext->drawingCallbacks.on_delete_surface != NULL) {
- pSurface->pContext->drawingCallbacks.on_delete_surface(pSurface);
- }
-
- free(pSurface);
- }
-}
-
-float dr2d_get_surface_width(const dr2d_surface* pSurface)
-{
- if (pSurface != NULL) {
- return pSurface->width;
- }
-
- return 0;
-}
-
-float dr2d_get_surface_height(const dr2d_surface* pSurface)
-{
- if (pSurface != NULL) {
- return pSurface->height;
- }
-
- return 0;
-}
-
-void* dr2d_get_surface_extra_data(dr2d_surface* pSurface)
-{
- if (pSurface != NULL) {
- return pSurface->pExtraData;
- }
-
- return NULL;
-}
-
-
-void dr2d_begin_draw(dr2d_surface* pSurface)
-{
- if (pSurface != NULL)
- {
- assert(pSurface->pContext != NULL);
-
- if (pSurface->pContext->drawingCallbacks.begin_draw != NULL) {
- pSurface->pContext->drawingCallbacks.begin_draw(pSurface);
- }
- }
-}
-
-void dr2d_end_draw(dr2d_surface* pSurface)
-{
- if (pSurface != NULL)
- {
- assert(pSurface->pContext != NULL);
-
- if (pSurface->pContext->drawingCallbacks.end_draw != NULL) {
- pSurface->pContext->drawingCallbacks.end_draw(pSurface);
- }
- }
-}
-
-void dr2d_clear(dr2d_surface * pSurface, dr2d_color color)
-{
- if (pSurface != NULL)
- {
- assert(pSurface->pContext != NULL);
-
- if (pSurface->pContext->drawingCallbacks.clear != NULL) {
- pSurface->pContext->drawingCallbacks.clear(pSurface, color);
- }
- }
-}
-
-void dr2d_draw_rect(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color)
-{
- if (pSurface != NULL)
- {
- assert(pSurface->pContext != NULL);
-
- if (pSurface->pContext->drawingCallbacks.draw_rect != NULL) {
- pSurface->pContext->drawingCallbacks.draw_rect(pSurface, left, top, right, bottom, color);
- }
- }
-}
-
-void dr2d_draw_rect_outline(dr2d_surface * pSurface, float left, float top, float right, float bottom, dr2d_color color, float outlineWidth)
-{
- if (pSurface != NULL)
- {
- assert(pSurface->pContext != NULL);
-
- if (pSurface->pContext->drawingCallbacks.draw_rect_outline != NULL) {
- pSurface->pContext->drawingCallbacks.draw_rect_outline(pSurface, left, top, right, bottom, color, outlineWidth);
- }
- }
-}
-
-void dr2d_draw_rect_with_outline(dr2d_surface * pSurface, float left, float top, float right, float bottom, dr2d_color color, float outlineWidth, dr2d_color outlineColor)
-{
- if (pSurface != NULL)
- {
- assert(pSurface->pContext != NULL);
-
- if (pSurface->pContext->drawingCallbacks.draw_rect_with_outline != NULL) {
- pSurface->pContext->drawingCallbacks.draw_rect_with_outline(pSurface, left, top, right, bottom, color, outlineWidth, outlineColor);
- }
- }
-}
-
-void dr2d_draw_round_rect(dr2d_surface * pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius)
-{
- if (pSurface != NULL)
- {
- assert(pSurface->pContext != NULL);
-
- if (pSurface->pContext->drawingCallbacks.draw_round_rect != NULL) {
- pSurface->pContext->drawingCallbacks.draw_round_rect(pSurface, left, top, right, bottom, color, radius);
- }
- }
-}
-
-void dr2d_draw_round_rect_outline(dr2d_surface * pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius, float outlineWidth)
-{
- if (pSurface != NULL)
- {
- assert(pSurface->pContext != NULL);
-
- if (pSurface->pContext->drawingCallbacks.draw_round_rect_outline != NULL) {
- pSurface->pContext->drawingCallbacks.draw_round_rect_outline(pSurface, left, top, right, bottom, color, radius, outlineWidth);
- }
- }
-}
-
-void dr2d_draw_round_rect_with_outline(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius, float outlineWidth, dr2d_color outlineColor)
-{
- if (pSurface != NULL)
- {
- assert(pSurface->pContext != NULL);
-
- if (pSurface->pContext->drawingCallbacks.draw_round_rect_with_outline != NULL) {
- pSurface->pContext->drawingCallbacks.draw_round_rect_with_outline(pSurface, left, top, right, bottom, color, radius, outlineWidth, outlineColor);
- }
- }
-}
-
-void dr2d_draw_text(dr2d_surface* pSurface, dr2d_font* pFont, const char* text, size_t textSizeInBytes, float posX, float posY, dr2d_color color, dr2d_color backgroundColor)
-{
- if (pSurface != NULL)
- {
- assert(pSurface->pContext != NULL);
-
- if (pSurface->pContext->drawingCallbacks.draw_text != NULL) {
- pSurface->pContext->drawingCallbacks.draw_text(pSurface, pFont, text, textSizeInBytes, posX, posY, color, backgroundColor);
- }
- }
-}
-
-void dr2d_draw_image(dr2d_surface* pSurface, dr2d_image* pImage, dr2d_draw_image_args* pArgs)
-{
- if (pSurface == NULL || pImage == NULL || pArgs == NULL) {
- return;
- }
-
- assert(pSurface->pContext != NULL);
-
- if (pSurface->pContext->drawingCallbacks.draw_image) {
- pSurface->pContext->drawingCallbacks.draw_image(pSurface, pImage, pArgs);
- }
-}
-
-void dr2d_set_clip(dr2d_surface* pSurface, float left, float top, float right, float bottom)
-{
- if (pSurface != NULL)
- {
- assert(pSurface->pContext != NULL);
-
- if (pSurface->pContext->drawingCallbacks.set_clip != NULL) {
- pSurface->pContext->drawingCallbacks.set_clip(pSurface, left, top, right, bottom);
- }
- }
-}
-
-void dr2d_get_clip(dr2d_surface* pSurface, float* pLeftOut, float* pTopOut, float* pRightOut, float* pBottomOut)
-{
- if (pSurface != NULL)
- {
- assert(pSurface->pContext != NULL);
-
- if (pSurface->pContext->drawingCallbacks.get_clip != NULL) {
- pSurface->pContext->drawingCallbacks.get_clip(pSurface, pLeftOut, pTopOut, pRightOut, pBottomOut);
- }
- }
-}
-
-dr2d_font* dr2d_create_font(dr2d_context* pContext, const char* family, unsigned int size, dr2d_font_weight weight, dr2d_font_slant slant, float rotation, unsigned int flags)
-{
- if (pContext == NULL) {
- return NULL;
- }
-
- dr2d_font* pFont = (dr2d_font*)malloc(sizeof(dr2d_font) + pContext->fontExtraBytes);
- if (pFont == NULL) {
- return NULL;
- }
-
- pFont->pContext = pContext;
- pFont->family[0] = '\0';
- pFont->size = size;
- pFont->weight = weight;
- pFont->slant = slant;
- pFont->rotation = rotation;
- pFont->flags = flags;
-
- if (family != NULL) {
- dr2d_strcpy_s(pFont->family, sizeof(pFont->family), family);
- }
-
- if (pContext->drawingCallbacks.on_create_font != NULL) {
- if (!pContext->drawingCallbacks.on_create_font(pFont)) {
- free(pFont);
- return NULL;
- }
- }
-
- return pFont;
-}
-
-void dr2d_delete_font(dr2d_font* pFont)
-{
- if (pFont == NULL) {
- return;
- }
-
- assert(pFont->pContext != NULL);
-
- if (pFont->pContext->drawingCallbacks.on_delete_font != NULL) {
- pFont->pContext->drawingCallbacks.on_delete_font(pFont);
- }
-
- free(pFont);
-}
-
-void* dr2d_get_font_extra_data(dr2d_font* pFont)
-{
- if (pFont == NULL) {
- return NULL;
- }
-
- return pFont->pExtraData;
-}
-
-unsigned int dr2d_get_font_size(dr2d_font* pFont)
-{
- if (pFont == NULL) {
- return 0;
- }
-
- return pFont->size;
-}
-
-bool dr2d_get_font_metrics(dr2d_font* pFont, dr2d_font_metrics* pMetricsOut)
-{
- if (pFont == NULL) {
- return false;
- }
-
- assert(pFont->pContext != NULL);
-
- if (pFont->pContext->drawingCallbacks.get_font_metrics != NULL) {
- return pFont->pContext->drawingCallbacks.get_font_metrics(pFont, pMetricsOut);
- }
-
- return false;
-}
-
-bool dr2d_get_glyph_metrics(dr2d_font* pFont, unsigned int utf32, dr2d_glyph_metrics* pMetricsOut)
-{
- if (pFont == NULL || pMetricsOut == NULL) {
- return false;
- }
-
- assert(pFont->pContext != NULL);
-
- if (pFont->pContext->drawingCallbacks.get_glyph_metrics != NULL) {
- return pFont->pContext->drawingCallbacks.get_glyph_metrics(pFont, utf32, pMetricsOut);
- }
-
- return false;
-}
-
-bool dr2d_measure_string(dr2d_font* pFont, const char* text, size_t textSizeInBytes, float* pWidthOut, float* pHeightOut)
-{
- if (pFont == NULL) {
- return false;
- }
-
- assert(pFont->pContext != NULL);
-
- if (pFont->pContext->drawingCallbacks.measure_string != NULL) {
- return pFont->pContext->drawingCallbacks.measure_string(pFont, text, textSizeInBytes, pWidthOut, pHeightOut);
- }
-
- return false;
-}
-
-bool dr2d_get_text_cursor_position_from_point(dr2d_font* pFont, const char* text, size_t textSizeInBytes, float maxWidth, float inputPosX, float* pTextCursorPosXOut, size_t* pCharacterIndexOut)
-{
- if (pFont == NULL) {
- return false;
- }
-
- assert(pFont->pContext != NULL);
-
- if (pFont->pContext->drawingCallbacks.get_text_cursor_position_from_point) {
- return pFont->pContext->drawingCallbacks.get_text_cursor_position_from_point(pFont, text, textSizeInBytes, maxWidth, inputPosX, pTextCursorPosXOut, pCharacterIndexOut);
- }
-
- return false;
-}
-
-bool dr2d_get_text_cursor_position_from_char(dr2d_font* pFont, const char* text, size_t characterIndex, float* pTextCursorPosXOut)
-{
- if (pFont == NULL) {
- return false;
- }
-
- assert(pFont->pContext != NULL);
-
- if (pFont->pContext->drawingCallbacks.get_text_cursor_position_from_char) {
- return pFont->pContext->drawingCallbacks.get_text_cursor_position_from_char(pFont, text, characterIndex, pTextCursorPosXOut);
- }
-
- return false;
-}
-
-
-dr2d_image* dr2d_create_image(dr2d_context* pContext, unsigned int width, unsigned int height, dr2d_image_format format, unsigned int stride, const void* pData)
-{
- if (pContext == NULL || width == 0 || height == 0) {
- return NULL;
- }
-
- dr2d_image* pImage = (dr2d_image*)malloc(sizeof(dr2d_image) + pContext->imageExtraBytes);
- if (pImage == NULL) {
- return NULL;
- }
-
- pImage->pContext = pContext;
- pImage->width = width;
- pImage->height = height;
- pImage->format = format;
- pImage->isMapped = false;
-
- if (pContext->drawingCallbacks.on_create_image != NULL) {
- if (!pContext->drawingCallbacks.on_create_image(pImage, stride, pData)) {
- free(pImage);
- return NULL;
- }
- }
-
- return pImage;
-}
-
-void dr2d_delete_image(dr2d_image* pImage)
-{
- if (pImage == NULL) {
- return;
- }
-
- assert(pImage->pContext != NULL);
-
- if (pImage->pContext->drawingCallbacks.on_delete_image != NULL) {
- pImage->pContext->drawingCallbacks.on_delete_image(pImage);
- }
-
- free(pImage);
-}
-
-void* dr2d_get_image_extra_data(dr2d_image* pImage)
-{
- if (pImage == NULL) {
- return NULL;
- }
-
- return pImage->pExtraData;
-}
-
-void dr2d_get_image_size(dr2d_image* pImage, unsigned int* pWidthOut, unsigned int* pHeightOut)
-{
- if (pImage == NULL) {
- return;
- }
-
- if (pWidthOut) {
- *pWidthOut = pImage->width;
- }
- if (pHeightOut) {
- *pHeightOut = pImage->height;
- }
-}
-
-void* dr2d_map_image_data(dr2d_image* pImage, unsigned int accessFlags)
-{
- if (pImage == NULL || pImage->pContext->drawingCallbacks.map_image_data == NULL || pImage->pContext->drawingCallbacks.unmap_image_data == NULL || pImage->isMapped) {
- return NULL;
- }
-
- void* pImageData = pImage->pContext->drawingCallbacks.map_image_data(pImage, accessFlags);
- if (pImageData != NULL) {
- pImage->isMapped = true;
- }
-
- return pImageData;
-}
-
-void dr2d_unmap_image_data(dr2d_image* pImage)
-{
- if (pImage == NULL || pImage->pContext->drawingCallbacks.unmap_image_data == NULL || !pImage->isMapped) {
- return;
- }
-
- pImage->pContext->drawingCallbacks.unmap_image_data(pImage);
- pImage->isMapped = false;
-}
-
-dr2d_image_format dr2d_get_optimal_image_format(dr2d_context* pContext)
-{
- if (pContext == NULL || pContext->drawingCallbacks.get_optimal_image_format == NULL) {
- return dr2d_image_format_rgba8;
- }
-
- return pContext->drawingCallbacks.get_optimal_image_format(pContext);
-}
-
-
-
-/////////////////////////////////////////////////////////////////
-//
-// UTILITY API
-//
-/////////////////////////////////////////////////////////////////
-
-dr2d_color dr2d_rgba(dr2d_byte r, dr2d_byte g, dr2d_byte b, dr2d_byte a)
-{
- dr2d_color color;
- color.r = r;
- color.g = g;
- color.b = b;
- color.a = a;
-
- return color;
-}
-
-dr2d_color dr2d_rgb(dr2d_byte r, dr2d_byte g, dr2d_byte b)
-{
- dr2d_color color;
- color.r = r;
- color.g = g;
- color.b = b;
- color.a = 255;
-
- return color;
-}
-
-
-
-/////////////////////////////////////////////////////////////////
-//
-// PRIVATE UTILITY API
-//
-/////////////////////////////////////////////////////////////////
-
-// RGBA8 <-> BGRA8 swap with alpha pre-multiply.
-void dr2d__rgba8_bgra8_swap__premul(const void* pSrc, void* pDst, unsigned int width, unsigned int height, unsigned int srcStride, unsigned int dstStride)
-{
- assert(pSrc != NULL);
- assert(pDst != NULL);
-
- const unsigned int srcStride32 = srcStride/4;
- const unsigned int dstStride32 = dstStride/4;
-
- const unsigned int* pSrcRow = (const unsigned int*)pSrc;
- unsigned int* pDstRow = (unsigned int*)pDst;
-
- for (unsigned int iRow = 0; iRow < height; ++iRow)
- {
- for (unsigned int iCol = 0; iCol < width; ++iCol)
- {
- unsigned int srcTexel = pSrcRow[iCol];
- unsigned int srcTexelA = (srcTexel & 0xFF000000) >> 24;
- unsigned int srcTexelB = (srcTexel & 0x00FF0000) >> 16;
- unsigned int srcTexelG = (srcTexel & 0x0000FF00) >> 8;
- unsigned int srcTexelR = (srcTexel & 0x000000FF) >> 0;
-
- srcTexelB = (unsigned int)(srcTexelB * (srcTexelA / 255.0f));
- srcTexelG = (unsigned int)(srcTexelG * (srcTexelA / 255.0f));
- srcTexelR = (unsigned int)(srcTexelR * (srcTexelA / 255.0f));
-
- pDstRow[iCol] = (srcTexelR << 16) | (srcTexelG << 8) | (srcTexelB << 0) | (srcTexelA << 24);
- }
-
- pSrcRow += srcStride32;
- pDstRow += dstStride32;
- }
-}
-
-
-/////////////////////////////////////////////////////////////////
-//
-// WINDOWS GDI 2D API
-//
-/////////////////////////////////////////////////////////////////
-#ifndef DR2D_NO_GDI
-
-typedef struct
-{
- /// The device context that owns every surface HBITMAP object. All drawing is done through this DC.
- HDC hDC;
-
- /// The buffer used to store wchar strings when converting from char* to wchar_t* strings. We just use a global buffer for
- /// this so we can avoid unnecessary allocations.
- wchar_t* wcharBuffer;
-
- /// The size of wcharBuffer (including the null terminator).
- unsigned int wcharBufferLength;
-
- /// The cache of glyph character positions.
- int* pGlyphCache;
- size_t glyphCacheSize;
-
- /// Whether or not the context owns the device context.
- bool ownsDC;
-
-} gdi_context_data;
-
-typedef struct
-{
- /// The window to draw to. The can be null, which is the case when creating the surface with dr2d_create_surface(). When this
- /// is non-null the size of the surface is always tied to the window.
- HWND hWnd;
-
- /// The HDC to use when drawing to the surface.
- HDC hDC;
-
- /// The intermediate DC to use when drawing bitmaps.
- HDC hIntermediateDC;
-
- /// The PAINTSTRUCT object that is filled by BeginPaint(). Only used when hWnd is non-null.
- PAINTSTRUCT ps;
-
- /// The bitmap to render to. This is created with GDI's CreateDIBSection() API, using the DC above. This is only used
- /// when hDC is NULL, which is the default.
- HBITMAP hBitmap;
-
- /// A pointer to the raw bitmap data. This is allocated CreateDIBSection().
- void* pBitmapData;
-
-
- /// The stock DC brush.
- HGDIOBJ hStockDCBrush;
-
- /// The stock null brush.
- HGDIOBJ hStockNullBrush;
-
- /// The stock DC pen.
- HGDIOBJ hStockDCPen;
-
- /// The stock null pen.
- HGDIOBJ hStockNullPen;
-
-
- /// The pen that was active at the start of drawing. This is restored at the end of drawing.
- HGDIOBJ hPrevPen;
-
- /// The brush that was active at the start of drawing.
- HGDIOBJ hPrevBrush;
-
- /// The brush color at the start of drawing.
- COLORREF prevBrushColor;
-
- /// The previous font.
- HGDIOBJ hPrevFont;
-
- /// The previous text background mode.
- int prevBkMode;
-
- /// The previous text background color.
- COLORREF prevBkColor;
-
-
-} gdi_surface_data;
-
-typedef struct
-{
- /// A handle to the Win32 font.
- HFONT hFont;
-
- /// The font metrics for faster retrieval. We cache the metrics when the font is loaded.
- dr2d_font_metrics metrics;
-
-} gdi_font_data;
-
-typedef struct
-{
- /// A handle to the primary bitmap object.
- HBITMAP hSrcBitmap;
-
- /// A pointer to the raw bitmap data.
- unsigned int* pSrcBitmapData;
-
- /// A handle to the secondary bitmap object that we use when we need to change the color values of
- /// the primary bitmap before drawing.
- HBITMAP hIntermediateBitmap;
-
- /// A pointer to the raw data of the intermediate bitmap.
- unsigned int* pIntermediateBitmapData;
-
-
- /// A pointer to the mapped data. This is null when the image data is not mapped.
- void* pMappedImageData;
-
- /// The mapping flags.
- unsigned int mapAccessFlags;
-
-} gdi_image_data;
-
-
-bool dr2d_on_create_context_gdi(dr2d_context* pContext, const void* pUserData);
-void dr2d_on_delete_context_gdi(dr2d_context* pContext);
-bool dr2d_on_create_surface_gdi(dr2d_surface* pSurface, float width, float height);
-void dr2d_on_delete_surface_gdi(dr2d_surface* pSurface);
-bool dr2d_on_create_font_gdi(dr2d_font* pFont);
-void dr2d_on_delete_font_gdi(dr2d_font* pFont);
-bool dr2d_on_create_image_gdi(dr2d_image* pImage, unsigned int stride, const void* pData);
-void dr2d_on_delete_image_gdi(dr2d_image* pImage);
-
-void dr2d_begin_draw_gdi(dr2d_surface* pSurface);
-void dr2d_end_draw_gdi(dr2d_surface* pSurface);
-void dr2d_clear_gdi(dr2d_surface* pSurface, dr2d_color color);
-void dr2d_draw_rect_gdi(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color);
-void dr2d_draw_rect_outline_gdi(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float outlineWidth);
-void dr2d_draw_rect_with_outline_gdi(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float outlineWidth, dr2d_color outlineColor);
-void dr2d_draw_round_rect_gdi(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius);
-void dr2d_draw_round_rect_outline_gdi(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius, float outlineWidth);
-void dr2d_draw_round_rect_with_outline_gdi(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius, float outlineWidth, dr2d_color outlineColor);
-void dr2d_draw_text_gdi(dr2d_surface* pSurface, dr2d_font* pFont, const char* text, size_t textSizeInBytes, float posX, float posY, dr2d_color color, dr2d_color backgroundColor);
-void dr2d_draw_image_gdi(dr2d_surface* pSurface, dr2d_image* pImage, dr2d_draw_image_args* pArgs);
-void dr2d_set_clip_gdi(dr2d_surface* pSurface, float left, float top, float right, float bottom);
-void dr2d_get_clip_gdi(dr2d_surface* pSurface, float* pLeftOut, float* pTopOut, float* pRightOut, float* pBottomOut);
-
-dr2d_image_format dr2d_get_optimal_image_format_gdi(dr2d_context* pContext);
-void* dr2d_map_image_data_gdi(dr2d_image* pImage, unsigned accessFlags);
-void dr2d_unmap_image_data_gdi(dr2d_image* pImage);
-
-bool dr2d_get_font_metrics_gdi(dr2d_font* pFont, dr2d_font_metrics* pMetricsOut);
-bool dr2d_get_glyph_metrics_gdi(dr2d_font* pFont, unsigned int utf32, dr2d_glyph_metrics* pGlyphMetrics);
-bool dr2d_measure_string_gdi(dr2d_font* pFont, const char* text, size_t textSizeInBytes, float* pWidthOut, float* pHeightOut);
-bool dr2d_get_text_cursor_position_from_point_gdi(dr2d_font* pFont, const char* text, size_t textSizeInBytes, float maxWidth, float inputPosX, float* pTextCursorPosXOut, size_t* pCharacterIndexOut);
-bool dr2d_get_text_cursor_position_from_char_gdi(dr2d_font* pFont, const char* text, size_t characterIndex, float* pTextCursorPosXOut);
-
-/// Converts a char* to a wchar_t* string.
-wchar_t* dr2d_to_wchar_gdi(dr2d_context* pContext, const char* text, size_t textSizeInBytes, unsigned int* characterCountOut);
-
-/// Converts a UTF-32 character to a UTF-16.
-static int dr2d_utf32_to_utf16(unsigned int utf32, unsigned short utf16[2])
-{
- if (utf16 == NULL) {
- return 0;
- }
-
- if (utf32 < 0xD800 || (utf32 >= 0xE000 && utf32 <= 0xFFFF))
- {
- utf16[0] = (unsigned short)utf32;
- utf16[1] = 0;
- return 1;
- }
- else
- {
- if (utf32 >= 0x10000 && utf32 <= 0x10FFFF)
- {
- utf16[0] = (unsigned short)(0xD7C0 + (unsigned short)(utf32 >> 10));
- utf16[1] = (unsigned short)(0xDC00 + (unsigned short)(utf32 & 0x3FF));
- return 2;
- }
- else
- {
- // Invalid.
- utf16[0] = 0;
- utf16[0] = 0;
- return 0;
- }
- }
-}
-
-dr2d_context* dr2d_create_context_gdi(HDC hDC)
-{
- dr2d_drawing_callbacks callbacks;
- callbacks.on_create_context = dr2d_on_create_context_gdi;
- callbacks.on_delete_context = dr2d_on_delete_context_gdi;
- callbacks.on_create_surface = dr2d_on_create_surface_gdi;
- callbacks.on_delete_surface = dr2d_on_delete_surface_gdi;
- callbacks.on_create_font = dr2d_on_create_font_gdi;
- callbacks.on_delete_font = dr2d_on_delete_font_gdi;
- callbacks.on_create_image = dr2d_on_create_image_gdi;
- callbacks.on_delete_image = dr2d_on_delete_image_gdi;
-
- callbacks.begin_draw = dr2d_begin_draw_gdi;
- callbacks.end_draw = dr2d_end_draw_gdi;
- callbacks.clear = dr2d_clear_gdi;
- callbacks.draw_rect = dr2d_draw_rect_gdi;
- callbacks.draw_rect_outline = dr2d_draw_rect_outline_gdi;
- callbacks.draw_rect_with_outline = dr2d_draw_rect_with_outline_gdi;
- callbacks.draw_round_rect = dr2d_draw_round_rect_gdi;
- callbacks.draw_round_rect_outline = dr2d_draw_round_rect_outline_gdi;
- callbacks.draw_round_rect_with_outline = dr2d_draw_round_rect_with_outline_gdi;
- callbacks.draw_text = dr2d_draw_text_gdi;
- callbacks.draw_image = dr2d_draw_image_gdi;
- callbacks.set_clip = dr2d_set_clip_gdi;
- callbacks.get_clip = dr2d_get_clip_gdi;
-
- callbacks.get_optimal_image_format = dr2d_get_optimal_image_format_gdi;
- callbacks.map_image_data = dr2d_map_image_data_gdi;
- callbacks.unmap_image_data = dr2d_unmap_image_data_gdi;
-
- callbacks.get_font_metrics = dr2d_get_font_metrics_gdi;
- callbacks.get_glyph_metrics = dr2d_get_glyph_metrics_gdi;
- callbacks.measure_string = dr2d_measure_string_gdi;
- callbacks.get_text_cursor_position_from_point = dr2d_get_text_cursor_position_from_point_gdi;
- callbacks.get_text_cursor_position_from_char = dr2d_get_text_cursor_position_from_char_gdi;
-
- return dr2d_create_context(callbacks, sizeof(gdi_context_data), sizeof(gdi_surface_data), sizeof(gdi_font_data), sizeof(gdi_image_data), &hDC);
-}
-
-dr2d_surface* dr2d_create_surface_gdi_HWND(dr2d_context* pContext, HWND hWnd)
-{
- dr2d_surface* pSurface = dr2d_create_surface(pContext, 0, 0);
- if (pSurface != NULL) {
- gdi_surface_data* pGDIData = (gdi_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pGDIData != NULL) {
- pGDIData->hWnd = hWnd;
- }
- }
-
- return pSurface;
-}
-
-dr2d_surface* dr2d_create_surface_gdi_HDC(dr2d_context* pContext, HDC hDC)
-{
- dr2d_surface* pSurface = dr2d_create_surface(pContext, 0, 0);
- if (pSurface != NULL) {
- gdi_surface_data* pGDIData = (gdi_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pGDIData != NULL) {
- pGDIData->hDC = hDC;
- }
- }
-
- return pSurface;
-}
-
-HDC dr2d_get_HDC(dr2d_surface* pSurface)
-{
- if (pSurface != NULL) {
- gdi_surface_data* pGDIData = (gdi_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pGDIData != NULL) {
- return pGDIData->hDC;
- }
- }
-
- return NULL;
-}
-
-HBITMAP dr2d_get_HBITMAP(dr2d_surface* pSurface)
-{
- if (pSurface != NULL) {
- gdi_surface_data* pGDIData = (gdi_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pGDIData != NULL) {
- return pGDIData->hBitmap;
- }
- }
-
- return NULL;
-}
-
-HFONT dr2d_get_HFONT(dr2d_font* pFont)
-{
- gdi_font_data* pGDIData = (gdi_font_data*)dr2d_get_font_extra_data(pFont);
- if (pGDIData == NULL) {
- return NULL;
- }
-
- return pGDIData->hFont;
-}
-
-
-bool dr2d_on_create_context_gdi(dr2d_context* pContext, const void* pUserData)
-{
- assert(pContext != NULL);
-
- HDC hDC = NULL;
- if (pUserData != NULL) {
- hDC = *(HDC*)pUserData;
- }
-
- bool ownsDC = false;
- if (hDC == NULL) {
- hDC = CreateCompatibleDC(GetDC(GetDesktopWindow()));
- ownsDC = true;
- }
-
-
- // We need to create the DC that all of our rendering commands will be drawn to.
- gdi_context_data* pGDIData = (gdi_context_data*)dr2d_get_context_extra_data(pContext);
- if (pGDIData == NULL) {
- return false;
- }
-
- pGDIData->hDC = hDC;
- if (pGDIData->hDC == NULL) {
- return false;
- }
-
- pGDIData->ownsDC = ownsDC;
-
-
- // We want to use the advanced graphics mode so that GetTextExtentPoint32() performs the conversions for font rotation for us.
- SetGraphicsMode(pGDIData->hDC, GM_ADVANCED);
-
-
- pGDIData->wcharBuffer = NULL;
- pGDIData->wcharBufferLength = 0;
-
- pGDIData->pGlyphCache = NULL;
- pGDIData->glyphCacheSize = 0;
-
- return true;
-}
-
-void dr2d_on_delete_context_gdi(dr2d_context* pContext)
-{
- assert(pContext != NULL);
-
- gdi_context_data* pGDIData = (gdi_context_data*)dr2d_get_context_extra_data(pContext);
- if (pGDIData != NULL)
- {
- free(pGDIData->pGlyphCache);
- pGDIData->glyphCacheSize = 0;
-
- free(pGDIData->wcharBuffer);
- pGDIData->wcharBuffer = 0;
- pGDIData->wcharBufferLength = 0;
-
- if (pGDIData->ownsDC) {
- DeleteDC(pGDIData->hDC);
- }
-
- pGDIData->hDC = NULL;
- }
-}
-
-bool dr2d_on_create_surface_gdi(dr2d_surface* pSurface, float width, float height)
-{
- assert(pSurface != NULL);
-
- gdi_context_data* pGDIContextData = (gdi_context_data*)dr2d_get_context_extra_data(pSurface->pContext);
- if (pGDIContextData == NULL) {
- return false;
- }
-
- gdi_surface_data* pGDISurfaceData = (gdi_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pGDISurfaceData == NULL) {
- return false;
- }
-
- HDC hDC = pGDIContextData->hDC;
- if (hDC == NULL) {
- return false;
- }
-
- HDC hIntermediateDC = CreateCompatibleDC(hDC);
- if (hIntermediateDC == NULL) {
- return false;
- }
-
- pGDISurfaceData->hIntermediateDC = hIntermediateDC;
- pGDISurfaceData->hWnd = NULL;
-
-
- if (width != 0 && height != 0)
- {
- pGDISurfaceData->hDC = hDC;
-
- BITMAPINFO bmi;
- ZeroMemory(&bmi, sizeof(bmi));
- bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
- bmi.bmiHeader.biWidth = (LONG)width;
- bmi.bmiHeader.biHeight = (LONG)height;
- bmi.bmiHeader.biPlanes = 1;
- bmi.bmiHeader.biBitCount = 32;
- bmi.bmiHeader.biCompression = BI_RGB;
- pGDISurfaceData->hBitmap = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, &pGDISurfaceData->pBitmapData, NULL, 0);
- if (pGDISurfaceData->hBitmap == NULL) {
- return false;
- }
- }
- else
- {
- pGDISurfaceData->hBitmap = NULL;
- pGDISurfaceData->hDC = NULL;
- }
-
-
- return true;
-}
-
-void dr2d_on_delete_surface_gdi(dr2d_surface* pSurface)
-{
- assert(pSurface != NULL);
-
- gdi_surface_data* pGDIData = (gdi_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pGDIData != NULL)
- {
- DeleteObject(pGDIData->hBitmap);
- pGDIData->hBitmap = NULL;
-
- DeleteDC(pGDIData->hIntermediateDC);
- pGDIData->hIntermediateDC = NULL;
- }
-}
-
-bool dr2d_on_create_font_gdi(dr2d_font* pFont)
-{
- assert(pFont != NULL);
-
- gdi_font_data* pGDIData = (gdi_font_data*)dr2d_get_font_extra_data(pFont);
- if (pGDIData == NULL) {
- return false;
- }
-
-
- LONG weightGDI = FW_REGULAR;
- switch (pFont->weight)
- {
- case dr2d_font_weight_medium: weightGDI = FW_MEDIUM; break;
- case dr2d_font_weight_thin: weightGDI = FW_THIN; break;
- case dr2d_font_weight_extra_light: weightGDI = FW_EXTRALIGHT; break;
- case dr2d_font_weight_light: weightGDI = FW_LIGHT; break;
- case dr2d_font_weight_semi_bold: weightGDI = FW_SEMIBOLD; break;
- case dr2d_font_weight_bold: weightGDI = FW_BOLD; break;
- case dr2d_font_weight_extra_bold: weightGDI = FW_EXTRABOLD; break;
- case dr2d_font_weight_heavy: weightGDI = FW_HEAVY; break;
- default: break;
- }
-
- BYTE slantGDI = FALSE;
- if (pFont->slant == dr2d_font_slant_italic || pFont->slant == dr2d_font_slant_oblique) {
- slantGDI = TRUE;
- }
-
-
- LOGFONTA logfont;
- memset(&logfont, 0, sizeof(logfont));
-
-
-
- logfont.lfHeight = -(LONG)pFont->size;
- logfont.lfWeight = weightGDI;
- logfont.lfItalic = slantGDI;
- logfont.lfCharSet = DEFAULT_CHARSET;
- //logfont.lfQuality = (pFont->size > 36) ? ANTIALIASED_QUALITY : CLEARTYPE_QUALITY;
- logfont.lfQuality = (pFont->flags & DR2D_FONT_NO_CLEARTYPE) ? ANTIALIASED_QUALITY : CLEARTYPE_QUALITY;
- logfont.lfEscapement = (LONG)pFont->rotation * 10;
- logfont.lfOrientation = (LONG)pFont->rotation * 10;
-
- size_t familyLength = strlen(pFont->family);
- memcpy(logfont.lfFaceName, pFont->family, (familyLength < 31) ? familyLength : 31);
-
-
- pGDIData->hFont = CreateFontIndirectA(&logfont);
- if (pGDIData->hFont == NULL) {
- return false;
- }
-
-
- gdi_context_data* pGDIContextData = (gdi_context_data*)dr2d_get_context_extra_data(pFont->pContext);
- if (pGDIContextData == NULL) {
- return false;
- }
-
- // Cache the font metrics.
- HGDIOBJ hPrevFont = SelectObject(pGDIContextData->hDC, pGDIData->hFont);
- {
- TEXTMETRIC metrics;
- GetTextMetrics(pGDIContextData->hDC, &metrics);
-
- pGDIData->metrics.ascent = metrics.tmAscent;
- pGDIData->metrics.descent = metrics.tmDescent;
- pGDIData->metrics.lineHeight = metrics.tmHeight;
-
-
- const MAT2 transform = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; // <-- Identity matrix
-
- GLYPHMETRICS spaceMetrics;
- DWORD bitmapBufferSize = GetGlyphOutlineW(pGDIContextData->hDC, ' ', GGO_NATIVE, &spaceMetrics, 0, NULL, &transform);
- if (bitmapBufferSize == GDI_ERROR) {
- pGDIData->metrics.spaceWidth = 4;
- } else {
- pGDIData->metrics.spaceWidth = spaceMetrics.gmCellIncX;
- }
- }
- SelectObject(pGDIContextData->hDC, hPrevFont);
-
-
- return true;
-}
-
-void dr2d_on_delete_font_gdi(dr2d_font* pFont)
-{
- assert(pFont != NULL);
-
- gdi_font_data* pGDIData = (gdi_font_data*)dr2d_get_font_extra_data(pFont);
- if (pGDIData == NULL) {
- return;
- }
-
- DeleteObject(pGDIData->hFont);
-}
-
-bool dr2d_on_create_image_gdi(dr2d_image* pImage, unsigned int stride, const void* pData)
-{
- assert(pImage != NULL);
-
- gdi_image_data* pGDIData = (gdi_image_data*)dr2d_get_image_extra_data(pImage);
- if (pGDIData == NULL) {
- return false;
- }
-
- gdi_context_data* pGDIContextData = (gdi_context_data*)dr2d_get_context_extra_data(pImage->pContext);
- if (pGDIContextData == NULL) {
- return false;
- }
-
-
- BITMAPINFO bmi;
- ZeroMemory(&bmi, sizeof(bmi));
- bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
- bmi.bmiHeader.biWidth = pImage->width;
- bmi.bmiHeader.biHeight = pImage->height;
- bmi.bmiHeader.biPlanes = 1;
- bmi.bmiHeader.biBitCount = 32; // Only supporting 32-bit formats.
- bmi.bmiHeader.biCompression = BI_RGB;
- pGDIData->hSrcBitmap = CreateDIBSection(pGDIContextData->hDC, &bmi, DIB_RGB_COLORS, (void**)&pGDIData->pSrcBitmapData, NULL, 0);
- if (pGDIData->hSrcBitmap == NULL) {
- return false;
- }
-
- pGDIData->hIntermediateBitmap = CreateDIBSection(pGDIContextData->hDC, &bmi, DIB_RGB_COLORS, (void**)&pGDIData->pIntermediateBitmapData, NULL, 0);
- if (pGDIData->hIntermediateBitmap == NULL) {
- DeleteObject(pGDIData->hSrcBitmap);
- return false;
- }
-
-
- // We need to convert the data so it renders correctly with AlphaBlend().
- if (pData != NULL) {
- dr2d__rgba8_bgra8_swap__premul(pData, pGDIData->pSrcBitmapData, pImage->width, pImage->height, stride, pImage->width*4);
- }
-
- // Flush GDI to let it know we are finished with the bitmap object's data.
- GdiFlush();
-
-
- pGDIData->pMappedImageData = NULL;
- pGDIData->mapAccessFlags = 0;
-
- // At this point everything should be good.
- return true;
-}
-
-void dr2d_on_delete_image_gdi(dr2d_image* pImage)
-{
- assert(pImage != NULL);
-
- gdi_image_data* pGDIData = (gdi_image_data*)dr2d_get_image_extra_data(pImage);
- if (pGDIData == NULL) {
- return;
- }
-
- DeleteObject(pGDIData->hSrcBitmap);
- pGDIData->hSrcBitmap = NULL;
-
- DeleteObject(pGDIData->hIntermediateBitmap);
- pGDIData->hIntermediateBitmap = NULL;
-}
-
-
-void dr2d_begin_draw_gdi(dr2d_surface* pSurface)
-{
- assert(pSurface != NULL);
-
- gdi_surface_data* pGDIData = (gdi_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pGDIData != NULL) {
- if (pGDIData->hWnd != NULL) {
- pGDIData->hDC = BeginPaint(pGDIData->hWnd, &pGDIData->ps);
- } else {
- SelectObject(dr2d_get_HDC(pSurface), pGDIData->hBitmap);
- }
-
- HDC hDC = dr2d_get_HDC(pSurface);
-
- pGDIData->hStockDCBrush = GetStockObject(DC_BRUSH);
- pGDIData->hStockNullBrush = GetStockObject(NULL_BRUSH);
- pGDIData->hStockDCPen = GetStockObject(DC_PEN);
- pGDIData->hStockNullPen = GetStockObject(NULL_PEN);
-
- // Retrieve the defaults so they can be restored later.
- pGDIData->hPrevPen = GetCurrentObject(hDC, OBJ_PEN);
- pGDIData->hPrevBrush = GetCurrentObject(hDC, OBJ_BRUSH);
- pGDIData->prevBrushColor = GetDCBrushColor(hDC);
- pGDIData->hPrevFont = GetCurrentObject(hDC, OBJ_FONT);
- pGDIData->prevBkMode = GetBkMode(hDC);
- pGDIData->prevBkColor = GetBkColor(hDC);
- }
-}
-
-void dr2d_end_draw_gdi(dr2d_surface* pSurface)
-{
- assert(pSurface != NULL);
-
- gdi_surface_data* pGDIData = (gdi_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pGDIData != NULL) {
- HDC hDC = dr2d_get_HDC(pSurface);
-
- SelectClipRgn(hDC, NULL);
-
- SelectObject(hDC, pGDIData->hPrevPen);
- SelectObject(hDC, pGDIData->hPrevBrush);
- SetDCBrushColor(hDC, pGDIData->prevBrushColor);
- SelectObject(hDC, pGDIData->hPrevFont);
- SetBkMode(hDC, pGDIData->prevBkMode);
- SetBkColor(hDC, pGDIData->prevBkColor);
-
- if (pGDIData->hWnd != NULL) {
- EndPaint(pGDIData->hWnd, &pGDIData->ps);
- }
- }
-}
-
-void dr2d_clear_gdi(dr2d_surface* pSurface, dr2d_color color)
-{
- assert(pSurface != NULL);
-
- dr2d_draw_rect_gdi(pSurface, 0, 0, pSurface->width, pSurface->height, color);
-}
-
-void dr2d_draw_rect_gdi(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color)
-{
- assert(pSurface != NULL);
-
- gdi_surface_data* pGDIData = (gdi_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pGDIData != NULL)
- {
- HDC hDC = dr2d_get_HDC(pSurface);
-
- SelectObject(hDC, pGDIData->hStockNullPen);
- SelectObject(hDC, pGDIData->hStockDCBrush);
- SetDCBrushColor(hDC, RGB(color.r, color.g, color.b));
-
- // Now draw the rectangle. The documentation for this says that the width and height is 1 pixel less when the pen is null. Therefore we will
- // increase the width and height by 1 since we have got the pen set to null.
- Rectangle(hDC, (int)left, (int)top, (int)right + 1, (int)bottom + 1);
- }
-}
-
-void dr2d_draw_rect_outline_gdi(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float outlineWidth)
-{
- assert(pSurface != NULL);
-
- gdi_surface_data* pGDIData = (gdi_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pGDIData != NULL)
- {
- HDC hDC = dr2d_get_HDC(pSurface);
-
- SelectObject(hDC, pGDIData->hStockNullPen);
- SelectObject(hDC, pGDIData->hStockDCBrush);
- SetDCBrushColor(hDC, RGB(color.r, color.g, color.b));
-
- // Now draw the rectangle. The documentation for this says that the width and height is 1 pixel less when the pen is null. Therefore we will
- // increase the width and height by 1 since we have got the pen set to null.
-
- Rectangle(hDC, (int)left, (int)top, (int)(left + outlineWidth + 1), (int)(bottom + 1)); // Left.
- Rectangle(hDC, (int)(right - outlineWidth), (int)top, (int)(right + 1), (int)(bottom + 1)); // Right.
- Rectangle(hDC, (int)(left + outlineWidth), (int)top, (int)(right - outlineWidth + 1), (int)(top + outlineWidth + 1)); // Top
- Rectangle(hDC, (int)(left + outlineWidth), (int)(bottom - outlineWidth), (int)(right - outlineWidth + 1), (int)(bottom + 1)); // Bottom
- }
-}
-
-void dr2d_draw_rect_with_outline_gdi(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float outlineWidth, dr2d_color outlineColor)
-{
- assert(pSurface != NULL);
-
- gdi_surface_data* pGDIData = (gdi_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pGDIData != NULL)
- {
- HDC hDC = dr2d_get_HDC(pSurface);
-
- HPEN hPen = CreatePen(PS_SOLID | PS_INSIDEFRAME, (int)outlineWidth, RGB(outlineColor.r, outlineColor.g, outlineColor.b));
- if (hPen != NULL)
- {
- SelectObject(hDC, hPen);
- SelectObject(hDC, pGDIData->hStockDCBrush);
- SetDCBrushColor(hDC, RGB(color.r, color.g, color.b));
-
- Rectangle(hDC, (int)left, (int)top, (int)right, (int)bottom);
-
- DeleteObject(hPen);
- }
- }
-}
-
-void dr2d_draw_round_rect_gdi(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius)
-{
- assert(pSurface != NULL);
-
- gdi_surface_data* pGDIData = (gdi_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pGDIData != NULL)
- {
- HDC hDC = dr2d_get_HDC(pSurface);
-
- SelectObject(hDC, pGDIData->hStockNullPen);
- SelectObject(hDC, pGDIData->hStockDCBrush);
- SetDCBrushColor(hDC, RGB(color.r, color.g, color.b));
-
- RoundRect(hDC, (int)left, (int)top, (int)right + 1, (int)bottom + 1, (int)(radius*2), (int)(radius*2));
- }
-}
-
-void dr2d_draw_round_rect_outline_gdi(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius, float outlineWidth)
-{
- assert(pSurface != NULL);
-
- gdi_surface_data* pGDIData = (gdi_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pGDIData != NULL)
- {
- HDC hDC = dr2d_get_HDC(pSurface);
-
- HPEN hPen = CreatePen(PS_SOLID | PS_INSIDEFRAME, (int)outlineWidth, RGB(color.r, color.g, color.b));
- if (hPen != NULL)
- {
- SelectObject(hDC, pGDIData->hStockNullBrush);
- SelectObject(hDC, hPen);
-
- RoundRect(hDC, (int)left, (int)top, (int)right, (int)bottom, (int)(radius*2), (int)(radius*2));
-
- DeleteObject(hPen);
- }
- }
-}
-
-void dr2d_draw_round_rect_with_outline_gdi(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius, float outlineWidth, dr2d_color outlineColor)
-{
- assert(pSurface != NULL);
-
- gdi_surface_data* pGDIData = (gdi_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pGDIData != NULL)
- {
- HDC hDC = dr2d_get_HDC(pSurface);
-
- HPEN hPen = CreatePen(PS_SOLID | PS_INSIDEFRAME, (int)outlineWidth, RGB(outlineColor.r, outlineColor.g, outlineColor.b));
- if (hPen != NULL)
- {
- SelectObject(hDC, hPen);
- SelectObject(hDC, pGDIData->hStockDCBrush);
- SetDCBrushColor(hDC, RGB(color.r, color.g, color.b));
-
- RoundRect(hDC, (int)left, (int)top, (int)right, (int)bottom, (int)(radius*2), (int)(radius*2));
-
- DeleteObject(hPen);
- }
- }
-}
-
-void dr2d_draw_text_gdi(dr2d_surface* pSurface, dr2d_font* pFont, const char* text, size_t textSizeInBytes, float posX, float posY, dr2d_color color, dr2d_color backgroundColor)
-{
- gdi_font_data* pGDIFontData = (gdi_font_data*)dr2d_get_font_extra_data(pFont);
- if (pGDIFontData == NULL) {
- return;
- }
-
-
- HDC hDC = dr2d_get_HDC(pSurface);
-
- HFONT hFontGDI = pGDIFontData->hFont;
- if (hFontGDI != NULL)
- {
- // We actually want to use the W version of TextOut because otherwise unicode doesn't work properly.
-
- unsigned int textWLength;
- wchar_t* textW = dr2d_to_wchar_gdi(pSurface->pContext, text, textSizeInBytes, &textWLength);
- if (textW != NULL)
- {
- SelectObject(hDC, hFontGDI);
-
- UINT options = 0;
- RECT rect = {0, 0, 0, 0};
-
- if (backgroundColor.a == 0) {
- SetBkMode(hDC, TRANSPARENT);
- } else {
- SetBkMode(hDC, OPAQUE);
- SetBkColor(hDC, RGB(backgroundColor.r, backgroundColor.g, backgroundColor.b));
-
- // There is an issue with the way GDI draws the background of a string of text. When ClearType is enabled, the rectangle appears
- // to be wider than it is supposed to be. As a result, drawing text right next to each other results in the most recent one being
- // drawn slightly on top of the previous one. To fix this we need to use ExtTextOut() with the ETO_CLIPPED parameter enabled.
- options |= ETO_CLIPPED;
-
- SIZE textSize = {0, 0};
- GetTextExtentPoint32W(hDC, textW, textWLength, &textSize);
- rect.left = (LONG)posX;
- rect.top = (LONG)posY;
- rect.right = (LONG)(posX + textSize.cx);
- rect.bottom = (LONG)(posY + textSize.cy);
- }
-
- SetTextColor(hDC, RGB(color.r, color.g, color.b));
-
- ExtTextOutW(hDC, (int)posX, (int)posY, options, &rect, textW, textWLength, NULL);
- }
- }
-}
-
-void dr2d_draw_image_gdi(dr2d_surface* pSurface, dr2d_image* pImage, dr2d_draw_image_args* pArgs)
-{
- gdi_image_data* pGDIImageData = (gdi_image_data*)dr2d_get_image_extra_data(pImage);
- if (pGDIImageData == NULL) {
- return;
- }
-
- gdi_surface_data* pGDISurfaceData = (gdi_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pGDISurfaceData == NULL) {
- return;
- }
-
- bool drawFlipped = false;
- HBITMAP hSrcBitmap = NULL;
-
- if ((pArgs->options & DR2D_IMAGE_DRAW_BACKGROUND) == 0 && (pArgs->options & DR2D_IMAGE_HINT_NO_ALPHA) != 0 && pArgs->foregroundTint.r == 255 && pArgs->foregroundTint.g == 255 && pArgs->foregroundTint.b == 255)
- {
- // Fast path. No tint, no background, no alpha.
- hSrcBitmap = pGDIImageData->hSrcBitmap;
- drawFlipped = true;
- }
- else
- {
- // Slow path. We need to manually change the color values of the intermediate bitmap and use that as the source when drawing it. This is also flipped.
- unsigned int* pSrcBitmapData = pGDIImageData->pSrcBitmapData;
- unsigned int* pDstBitmapData = pGDIImageData->pIntermediateBitmapData;
- for (unsigned int iRow = 0; iRow < pImage->height; ++iRow)
- {
- for (unsigned int iCol = 0; iCol < pImage->width; ++iCol)
- {
- unsigned int srcTexel = *(pSrcBitmapData + (iRow * pImage->width) + iCol);
- unsigned int* dstTexel = (pDstBitmapData + ((pImage->height - iRow - 1) * pImage->width) + iCol);
-
- unsigned int srcTexelA = (srcTexel & 0xFF000000) >> 24;
- unsigned int srcTexelR = (unsigned int)(((srcTexel & 0x00FF0000) >> 16) * (pArgs->foregroundTint.r / 255.0f));
- unsigned int srcTexelG = (unsigned int)(((srcTexel & 0x0000FF00) >> 8) * (pArgs->foregroundTint.g / 255.0f));
- unsigned int srcTexelB = (unsigned int)(((srcTexel & 0x000000FF) >> 0) * (pArgs->foregroundTint.b / 255.0f));
-
- if (srcTexelR > 255) srcTexelR = 255;
- if (srcTexelG > 255) srcTexelG = 255;
- if (srcTexelB > 255) srcTexelB = 255;
-
- if ((pArgs->options & DR2D_IMAGE_DRAW_BACKGROUND) != 0)
- {
- srcTexelB += (unsigned int)(pArgs->backgroundColor.b * ((255 - srcTexelA) / 255.0f));
- srcTexelG += (unsigned int)(pArgs->backgroundColor.g * ((255 - srcTexelA) / 255.0f));
- srcTexelR += (unsigned int)(pArgs->backgroundColor.r * ((255 - srcTexelA) / 255.0f));
- srcTexelA = 0xFF;
- }
-
- *dstTexel = (srcTexelR << 16) | (srcTexelG << 8) | (srcTexelB << 0) | (srcTexelA << 24);
- }
- }
-
- // Flush GDI to let it know we are finished with the bitmap object's data.
- GdiFlush();
-
- // If we have drawn the background manually we don't need to do an alpha blend.
- if ((pArgs->options & DR2D_IMAGE_DRAW_BACKGROUND) != 0) {
- pArgs->options |= DR2D_IMAGE_HINT_NO_ALPHA;
- }
-
- hSrcBitmap = pGDIImageData->hIntermediateBitmap;
- }
-
- HGDIOBJ hPrevBitmap = SelectObject(pGDISurfaceData->hIntermediateDC, hSrcBitmap);
- if ((pArgs->options & DR2D_IMAGE_HINT_NO_ALPHA) != 0)
- {
- if (drawFlipped) {
- StretchBlt(pGDISurfaceData->hDC, (int)pArgs->dstX, (int)pArgs->dstY + (int)pArgs->dstHeight - 1, (int)pArgs->dstWidth, -(int)pArgs->dstHeight, pGDISurfaceData->hIntermediateDC, (int)pArgs->srcX, (int)pArgs->srcY, (int)pArgs->srcWidth, (int)pArgs->srcHeight, SRCCOPY);
- } else {
- StretchBlt(pGDISurfaceData->hDC, (int)pArgs->dstX, (int)pArgs->dstY, (int)pArgs->dstWidth, (int)pArgs->dstHeight, pGDISurfaceData->hIntermediateDC, (int)pArgs->srcX, (int)pArgs->srcY, (int)pArgs->srcWidth, (int)pArgs->srcHeight, SRCCOPY);
- }
- }
- else
- {
- assert(drawFlipped == false); // <-- Error if this is hit.
- BLENDFUNCTION blend = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
- AlphaBlend(pGDISurfaceData->hDC, (int)pArgs->dstX, (int)pArgs->dstY, (int)pArgs->dstWidth, (int)pArgs->dstHeight, pGDISurfaceData->hIntermediateDC, (int)pArgs->srcX, (int)pArgs->srcY, (int)pArgs->srcWidth, (int)pArgs->srcHeight, blend);
- }
- SelectObject(pGDISurfaceData->hIntermediateDC, hPrevBitmap);
-}
-
-void dr2d_set_clip_gdi(dr2d_surface* pSurface, float left, float top, float right, float bottom)
-{
- assert(pSurface != NULL);
-
- gdi_surface_data* pGDIData = (gdi_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pGDIData != NULL)
- {
- HDC hDC = dr2d_get_HDC(pSurface);
-
- SelectClipRgn(hDC, NULL);
- IntersectClipRect(hDC, (int)left, (int)top, (int)right, (int)bottom);
- }
-}
-
-void dr2d_get_clip_gdi(dr2d_surface* pSurface, float* pLeftOut, float* pTopOut, float* pRightOut, float* pBottomOut)
-{
- assert(pSurface != NULL);
-
- gdi_surface_data* pGDIData = (gdi_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pGDIData != NULL)
- {
- RECT rect;
- GetClipBox(dr2d_get_HDC(pSurface), &rect);
-
- if (pLeftOut != NULL) {
- *pLeftOut = (float)rect.left;
- }
- if (pTopOut != NULL) {
- *pTopOut = (float)rect.top;
- }
- if (pRightOut != NULL) {
- *pRightOut = (float)rect.right;
- }
- if (pBottomOut != NULL) {
- *pBottomOut = (float)rect.bottom;
- }
- }
-}
-
-
-dr2d_image_format dr2d_get_optimal_image_format_gdi(dr2d_context* pContext)
-{
- (void)pContext;
- return dr2d_image_format_bgra8;
-}
-
-void* dr2d_map_image_data_gdi(dr2d_image* pImage, unsigned accessFlags)
-{
- assert(pImage != NULL);
-
- gdi_image_data* pGDIImageData = (gdi_image_data*)dr2d_get_image_extra_data(pImage);
- if (pGDIImageData == NULL) {
- return NULL;
- }
-
- assert(pGDIImageData->pMappedImageData == NULL); // This function should never be called while the image is already mapped.
-
- pGDIImageData->mapAccessFlags = accessFlags;
-
- if (pImage->format == dr2d_image_format_bgra8)
- {
- pGDIImageData->pMappedImageData = pGDIImageData->pSrcBitmapData;
- }
- else
- {
- pGDIImageData->pMappedImageData = malloc(pImage->width * pImage->height * 4);
- if (pGDIImageData->pMappedImageData == NULL) {
- return NULL;
- }
-
- for (unsigned int iRow = 0; iRow < pImage->height; ++iRow)
- {
- const unsigned int iRowSrc = pImage->height - (iRow + 1);
- const unsigned int iRowDst = iRow;
-
- for (unsigned int iCol = 0; iCol < pImage->width; ++iCol)
- {
- unsigned int srcTexel = ((const unsigned int*)(pGDIImageData->pSrcBitmapData))[ (iRowSrc * pImage->width) + iCol];
- unsigned int* dstTexel = (( unsigned int*)(pGDIImageData->pMappedImageData)) + (iRowDst * pImage->width) + iCol;
-
- unsigned int srcTexelA = (srcTexel & 0xFF000000) >> 24;
- unsigned int srcTexelB = (srcTexel & 0x00FF0000) >> 16;
- unsigned int srcTexelG = (srcTexel & 0x0000FF00) >> 8;
- unsigned int srcTexelR = (srcTexel & 0x000000FF) >> 0;
-
- srcTexelB = (unsigned int)(srcTexelB * (srcTexelA / 255.0f));
- srcTexelG = (unsigned int)(srcTexelG * (srcTexelA / 255.0f));
- srcTexelR = (unsigned int)(srcTexelR * (srcTexelA / 255.0f));
-
- *dstTexel = (srcTexelR << 16) | (srcTexelG << 8) | (srcTexelB << 0) | (srcTexelA << 24);
- }
- }
- }
-
- return pGDIImageData->pMappedImageData;
-}
-
-void dr2d_unmap_image_data_gdi(dr2d_image* pImage)
-{
- assert(pImage != NULL);
-
- gdi_image_data* pGDIImageData = (gdi_image_data*)dr2d_get_image_extra_data(pImage);
- if (pGDIImageData == NULL) {
- return;
- }
-
- assert(pGDIImageData->pMappedImageData != NULL); // This function should never be called while the image is not mapped.
-
- if (pImage->format == dr2d_image_format_bgra8)
- {
- // It's in the native format, so just do a flush.
- GdiFlush();
- }
- else
- {
- // Update the actual image data if applicable.
- if (pGDIImageData->mapAccessFlags & DR2D_WRITE) {
- dr2d__rgba8_bgra8_swap__premul(pGDIImageData->pMappedImageData, pGDIImageData->pSrcBitmapData, pImage->width, pImage->height, pImage->width*4, pImage->width*4);
- }
-
- free(pGDIImageData->pMappedImageData);
- }
-
- pGDIImageData->pMappedImageData = NULL;
- pGDIImageData->mapAccessFlags = 0;
-}
-
-
-bool dr2d_get_font_metrics_gdi(dr2d_font* pFont, dr2d_font_metrics* pMetricsOut)
-{
- assert(pFont != NULL);
- assert(pMetricsOut != NULL);
-
- gdi_font_data* pGDIFontData = (gdi_font_data*)dr2d_get_font_extra_data(pFont);
- if (pGDIFontData == NULL) {
- return false;
- }
-
- *pMetricsOut = pGDIFontData->metrics;
- return true;
-}
-
-bool dr2d_get_glyph_metrics_gdi(dr2d_font* pFont, unsigned int utf32, dr2d_glyph_metrics* pGlyphMetrics)
-{
- assert(pFont != NULL);
- assert(pGlyphMetrics != NULL);
-
- gdi_font_data* pGDIFontData = (gdi_font_data*)dr2d_get_font_extra_data(pFont);
- if (pGDIFontData == NULL) {
- return false;
- }
-
- gdi_context_data* pGDIContextData = (gdi_context_data*)dr2d_get_context_extra_data(pFont->pContext);
- if (pGDIContextData == NULL) {
- return false;
- }
-
-
- SelectObject(pGDIContextData->hDC, pGDIFontData->hFont);
-
-
- const MAT2 transform = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; // <-- Identity matrix
-
- unsigned short utf16[2];
- int utf16Len = dr2d_utf32_to_utf16(utf32, utf16);
-
- WCHAR glyphIndices[2];
-
- GCP_RESULTSW glyphResults;
- ZeroMemory(&glyphResults, sizeof(glyphResults));
- glyphResults.lStructSize = sizeof(glyphResults);
- glyphResults.lpGlyphs = glyphIndices;
- glyphResults.nGlyphs = 2;
- if (GetCharacterPlacementW(pGDIContextData->hDC, (LPCWSTR)utf16, utf16Len, 0, &glyphResults, 0) != 0)
- {
- GLYPHMETRICS metrics;
- DWORD bitmapBufferSize = GetGlyphOutlineW(pGDIContextData->hDC, glyphIndices[0], GGO_NATIVE | GGO_GLYPH_INDEX, &metrics, 0, NULL, &transform);
- if (bitmapBufferSize != GDI_ERROR)
- {
- pGlyphMetrics->width = metrics.gmBlackBoxX;
- pGlyphMetrics->height = metrics.gmBlackBoxY;
- pGlyphMetrics->originX = metrics.gmptGlyphOrigin.x;
- pGlyphMetrics->originY = metrics.gmptGlyphOrigin.y;
- pGlyphMetrics->advanceX = metrics.gmCellIncX;
- pGlyphMetrics->advanceY = metrics.gmCellIncY;
-
- return true;
- }
- }
-
- return false;
-}
-
-bool dr2d_measure_string_gdi(dr2d_font* pFont, const char* text, size_t textSizeInBytes, float* pWidthOut, float* pHeightOut)
-{
- assert(pFont != NULL);
-
- gdi_font_data* pGDIFontData = (gdi_font_data*)dr2d_get_font_extra_data(pFont);
- if (pGDIFontData == NULL) {
- return false;
- }
-
- gdi_context_data* pGDIContextData = (gdi_context_data*)dr2d_get_context_extra_data(pFont->pContext);
- if (pGDIContextData == NULL) {
- return false;
- }
-
-
- SelectObject(pGDIContextData->hDC, pGDIFontData->hFont);
-
- unsigned int textWLength;
- wchar_t* textW = dr2d_to_wchar_gdi(pFont->pContext, text, textSizeInBytes, &textWLength);
- if (textW != NULL)
- {
- SIZE sizeWin32;
- if (GetTextExtentPoint32W(pGDIContextData->hDC, textW, textWLength, &sizeWin32))
- {
- if (pWidthOut != NULL) {
- *pWidthOut = (float)sizeWin32.cx;
- }
- if (pHeightOut != NULL) {
- *pHeightOut = (float)sizeWin32.cy;
- }
-
- return true;
- }
- }
-
- return false;
-}
-
-bool dr2d_get_text_cursor_position_from_point_gdi(dr2d_font* pFont, const char* text, size_t textSizeInBytes, float maxWidth, float inputPosX, float* pTextCursorPosXOut, size_t* pCharacterIndexOut)
-{
- bool successful = false;
-
- assert(pFont != NULL);
-
- gdi_font_data* pGDIFontData = (gdi_font_data*)dr2d_get_font_extra_data(pFont);
- if (pGDIFontData == NULL) {
- return false;
- }
-
- gdi_context_data* pGDIContextData = (gdi_context_data*)dr2d_get_context_extra_data(pFont->pContext);
- if (pGDIContextData == NULL) {
- return false;
- }
-
-
- SelectObject(pGDIContextData->hDC, pGDIFontData->hFont);
-
-
- GCP_RESULTSW results;
- ZeroMemory(&results, sizeof(results));
- results.lStructSize = sizeof(results);
- results.nGlyphs = (UINT)textSizeInBytes;
-
- unsigned int textWLength;
- wchar_t* textW = dr2d_to_wchar_gdi(pFont->pContext, text, textSizeInBytes, &textWLength);
- if (textW != NULL)
- {
- if (results.nGlyphs > pGDIContextData->glyphCacheSize) {
- free(pGDIContextData->pGlyphCache);
- pGDIContextData->pGlyphCache = (int*)malloc(sizeof(int) * results.nGlyphs);
- pGDIContextData->glyphCacheSize = results.nGlyphs;
- }
-
- results.lpCaretPos = pGDIContextData->pGlyphCache;
- if (results.lpCaretPos != NULL)
- {
- if (GetCharacterPlacementW(pGDIContextData->hDC, textW, results.nGlyphs, (int)maxWidth, &results, GCP_MAXEXTENT | GCP_USEKERNING) != 0)
- {
- float textCursorPosX = 0;
- unsigned int iChar;
- for (iChar = 0; iChar < results.nGlyphs; ++iChar)
- {
- float charBoundsLeft = charBoundsLeft = (float)results.lpCaretPos[iChar];
- float charBoundsRight = 0;
- if (iChar < results.nGlyphs - 1) {
- charBoundsRight = (float)results.lpCaretPos[iChar + 1];
- } else {
- charBoundsRight = maxWidth;
- }
-
- if (inputPosX >= charBoundsLeft && inputPosX <= charBoundsRight)
- {
- // The input position is somewhere on top of this character. If it's positioned on the left side of the character, set the output
- // value to the character at iChar. Otherwise it should be set to the character at iChar + 1.
- float charBoundsRightHalf = charBoundsLeft + ceilf(((charBoundsRight - charBoundsLeft) / 2.0f));
- if (inputPosX <= charBoundsRightHalf) {
- break;
- } else {
- textCursorPosX = charBoundsRight;
- iChar += 1;
- break;
- }
- }
-
- textCursorPosX = charBoundsRight;
- }
-
- if (pTextCursorPosXOut) {
- *pTextCursorPosXOut = textCursorPosX;
- }
- if (pCharacterIndexOut) {
- *pCharacterIndexOut = iChar;
- }
-
- successful = true;
- }
- }
- }
-
- return successful;
-}
-
-bool dr2d_get_text_cursor_position_from_char_gdi(dr2d_font* pFont, const char* text, size_t characterIndex, float* pTextCursorPosXOut)
-{
- bool successful = false;
-
- assert(pFont != NULL);
-
- gdi_font_data* pGDIFontData = (gdi_font_data*)dr2d_get_font_extra_data(pFont);
- if (pGDIFontData == NULL) {
- return false;
- }
-
- gdi_context_data* pGDIContextData = (gdi_context_data*)dr2d_get_context_extra_data(pFont->pContext);
- if (pGDIContextData == NULL) {
- return false;
- }
-
-
- SelectObject(pGDIContextData->hDC, pGDIFontData->hFont);
-
-
- GCP_RESULTSW results;
- ZeroMemory(&results, sizeof(results));
- results.lStructSize = sizeof(results);
- results.nGlyphs = (DWORD)(characterIndex + 1);
-
- unsigned int textWLength;
- wchar_t* textW = dr2d_to_wchar_gdi(pFont->pContext, text, (int)results.nGlyphs, &textWLength);
- if (textW != NULL)
- {
- if (results.nGlyphs > pGDIContextData->glyphCacheSize) {
- free(pGDIContextData->pGlyphCache);
- pGDIContextData->pGlyphCache = (int*)malloc(sizeof(int) * results.nGlyphs);
- pGDIContextData->glyphCacheSize = results.nGlyphs;
- }
-
- results.lpCaretPos = pGDIContextData->pGlyphCache;
- if (results.lpCaretPos != NULL)
- {
- if (GetCharacterPlacementW(pGDIContextData->hDC, textW, results.nGlyphs, 0, &results, GCP_USEKERNING) != 0)
- {
- if (pTextCursorPosXOut) {
- *pTextCursorPosXOut = (float)results.lpCaretPos[characterIndex];
- }
-
- successful = true;
- }
- }
- }
-
- return successful;
-}
-
-
-wchar_t* dr2d_to_wchar_gdi(dr2d_context* pContext, const char* text, size_t textSizeInBytes, unsigned int* characterCountOut)
-{
- if (pContext == NULL || text == NULL) {
- return NULL;
- }
-
- gdi_context_data* pGDIData = (gdi_context_data*)dr2d_get_context_extra_data(pContext);
- if (pGDIData == NULL) {
- return NULL;
- }
-
- int wcharCount = 0;
-
-
- // We first try to copy the string into the already-allocated buffer. If it fails we fall back to the slow path which requires
- // two conversions.
- if (pGDIData->wcharBuffer == NULL) {
- goto fallback;
- }
-
- wcharCount = MultiByteToWideChar(CP_UTF8, 0, text, (int)textSizeInBytes, pGDIData->wcharBuffer, pGDIData->wcharBufferLength);
- if (wcharCount != 0) {
- if (characterCountOut) *characterCountOut = wcharCount;
- return pGDIData->wcharBuffer;
- }
-
-
-
-fallback:;
- wcharCount = MultiByteToWideChar(CP_UTF8, 0, text, (int)textSizeInBytes, NULL, 0);
- if (wcharCount == 0) {
- return NULL;
- }
-
- if (pGDIData->wcharBufferLength < (unsigned int)wcharCount + 1) {
- free(pGDIData->wcharBuffer);
- pGDIData->wcharBuffer = (wchar_t*)malloc(sizeof(wchar_t) * (wcharCount + 1));
- pGDIData->wcharBufferLength = wcharCount + 1;
- }
-
- wcharCount = MultiByteToWideChar(CP_UTF8, 0, text, (int)textSizeInBytes, pGDIData->wcharBuffer, pGDIData->wcharBufferLength);
- if (wcharCount == 0) {
- return NULL;
- }
-
-
- if (characterCountOut != NULL) {
- *characterCountOut = wcharCount;
- }
-
- return pGDIData->wcharBuffer;
-}
-
-#endif // GDI
-
-
-/////////////////////////////////////////////////////////////////
-//
-// CAIRO 2D API
-//
-/////////////////////////////////////////////////////////////////
-#ifndef DR2D_NO_CAIRO
-
-typedef struct
-{
- cairo_surface_t* pCairoSurface;
- cairo_t* pCairoContext;
-
- float clipRectLeft;
- float clipRectTop;
- float clipRectRight;
- float clipRectBottom;
-
-} cairo_surface_data;
-
-typedef struct
-{
- cairo_font_face_t* pFace;
- cairo_scaled_font_t* pFont;
-
- // The font metrics. This is initialized when the font is created.
- dr2d_font_metrics metrics;
-
-} cairo_font_data;
-
-typedef struct
-{
- /// Images in Cairo are implemented as surfaces.
- cairo_surface_t* pCairoSurface;
-
- /// A pointer to the raw data.
- unsigned char* pData;
-
-} cairo_image_data;
-
-bool dr2d_on_create_context_cairo(dr2d_context* pContext, const void* pUserData);
-void dr2d_on_delete_context_cairo(dr2d_context* pContext);
-bool dr2d_on_create_surface_cairo(dr2d_surface* pSurface, float width, float height);
-void dr2d_on_delete_surface_cairo(dr2d_surface* pSurface);
-bool dr2d_on_create_font_cairo(dr2d_font* pFont);
-void dr2d_on_delete_font_cairo(dr2d_font* pFont);
-bool dr2d_on_create_image_cairo(dr2d_image* pImage, unsigned int stride, const void* pData);
-void dr2d_on_delete_image_cairo(dr2d_image* pImage);
-
-void dr2d_begin_draw_cairo(dr2d_surface* pSurface);
-void dr2d_end_draw_cairo(dr2d_surface* pSurface);
-void dr2d_clear_cairo(dr2d_surface* pSurface, dr2d_color color);
-void dr2d_draw_rect_cairo(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color);
-void dr2d_draw_rect_outline_cairo(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float outlineWidth);
-void dr2d_draw_rect_with_outline_cairo(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float outlineWidth, dr2d_color outlineColor);
-void dr2d_draw_round_rect_cairo(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius);
-void dr2d_draw_round_rect_outline_cairo(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius, float outlineWidth);
-void dr2d_draw_round_rect_with_outline_cairo(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius, float outlineWidth, dr2d_color outlineColor);
-void dr2d_draw_text_cairo(dr2d_surface* pSurface, dr2d_font* pFont, const char* text, size_t textSizeInBytes, float posX, float posY, dr2d_color color, dr2d_color backgroundColor);
-void dr2d_draw_image_cairo(dr2d_surface* pSurface, dr2d_image* pImage, dr2d_draw_image_args* pArgs);
-void dr2d_set_clip_cairo(dr2d_surface* pSurface, float left, float top, float right, float bottom);
-void dr2d_get_clip_cairo(dr2d_surface* pSurface, float* pLeftOut, float* pTopOut, float* pRightOut, float* pBottomOut);
-
-dr2d_image_format dr2d_get_optimal_image_format_cairo(dr2d_context* pContext);
-void* dr2d_map_image_data_cairo(dr2d_image* pImage, unsigned accessFlags);
-void dr2d_unmap_image_data_cairo(dr2d_image* pImage);
-
-bool dr2d_get_font_metrics_cairo(dr2d_font* pFont, dr2d_font_metrics* pMetricsOut);
-bool dr2d_get_glyph_metrics_cairo(dr2d_font* pFont, unsigned int utf32, dr2d_glyph_metrics* pGlyphMetrics);
-bool dr2d_measure_string_cairo(dr2d_font* pFont, const char* text, size_t textSizeInBytes, float* pWidthOut, float* pHeightOut);
-bool dr2d_get_text_cursor_position_from_point_cairo(dr2d_font* pFont, const char* text, size_t textSizeInBytes, float maxWidth, float inputPosX, float* pTextCursorPosXOut, size_t* pCharacterIndexOut);
-bool dr2d_get_text_cursor_position_from_char_cairo(dr2d_font* pFont, const char* text, size_t characterIndex, float* pTextCursorPosXOut);
-
-
-dr2d_context* dr2d_create_context_cairo()
-{
- dr2d_drawing_callbacks callbacks;
- callbacks.on_create_context = dr2d_on_create_context_cairo;
- callbacks.on_delete_context = dr2d_on_delete_context_cairo;
- callbacks.on_create_surface = dr2d_on_create_surface_cairo;
- callbacks.on_delete_surface = dr2d_on_delete_surface_cairo;
- callbacks.on_create_font = dr2d_on_create_font_cairo;
- callbacks.on_delete_font = dr2d_on_delete_font_cairo;
- callbacks.on_create_image = dr2d_on_create_image_cairo;
- callbacks.on_delete_image = dr2d_on_delete_image_cairo;
-
- callbacks.begin_draw = dr2d_begin_draw_cairo;
- callbacks.end_draw = dr2d_end_draw_cairo;
- callbacks.clear = dr2d_clear_cairo;
- callbacks.draw_rect = dr2d_draw_rect_cairo;
- callbacks.draw_rect_outline = dr2d_draw_rect_outline_cairo;
- callbacks.draw_rect_with_outline = dr2d_draw_rect_with_outline_cairo;
- callbacks.draw_round_rect = dr2d_draw_round_rect_cairo;
- callbacks.draw_round_rect_outline = dr2d_draw_round_rect_outline_cairo;
- callbacks.draw_round_rect_with_outline = dr2d_draw_round_rect_with_outline_cairo;
- callbacks.draw_text = dr2d_draw_text_cairo;
- callbacks.draw_image = dr2d_draw_image_cairo;
- callbacks.set_clip = dr2d_set_clip_cairo;
- callbacks.get_clip = dr2d_get_clip_cairo;
-
- callbacks.map_image_data = dr2d_map_image_data_cairo;
- callbacks.unmap_image_data = dr2d_unmap_image_data_cairo;
-
- callbacks.get_font_metrics = dr2d_get_font_metrics_cairo;
- callbacks.get_glyph_metrics = dr2d_get_glyph_metrics_cairo;
- callbacks.measure_string = dr2d_measure_string_cairo;
- callbacks.get_text_cursor_position_from_point = dr2d_get_text_cursor_position_from_point_cairo;
- callbacks.get_text_cursor_position_from_char = dr2d_get_text_cursor_position_from_char_cairo;
-
-
- return dr2d_create_context(callbacks, 0, sizeof(cairo_surface_data), sizeof(cairo_font_data), sizeof(cairo_image_data), NULL);
-}
-
-dr2d_surface* dr2d_create_surface_cairo(dr2d_context* pContext, cairo_t* cr)
-{
- if (cr == NULL) {
- return NULL;
- }
-
- dr2d_surface* pSurface = dr2d_create_surface(pContext, 0, 0);
- if (pSurface != NULL) {
- cairo_surface_data* pCairoData = (cairo_surface_data*)dr2d_get_surface_extra_data(pSurface);
- if (pCairoData != NULL) {
- pCairoData->pCairoContext = cairo_reference(cr);
- pCairoData->pCairoSurface = cairo_surface_reference(cairo_get_target(cr));
- }
- }
-
- return pSurface;
-}
-
-cairo_surface_t* dr2d_get_cairo_surface_t(dr2d_surface* pSurface)
-{
- cairo_surface_data* pCairoData = dr2d_get_surface_extra_data(pSurface);
- if (pCairoData != NULL) {
- return pCairoData->pCairoSurface;
- }
-
- return NULL;
-}
-
-cairo_t* dr2d_get_cairo_t(dr2d_surface* pSurface)
-{
- cairo_surface_data* pCairoData = dr2d_get_surface_extra_data(pSurface);
- if (pCairoData != NULL) {
- return pCairoData->pCairoContext;
- }
-
- return NULL;
-}
-
-
-bool dr2d_on_create_context_cairo(dr2d_context* pContext, const void* pUserData)
-{
- assert(pContext != NULL);
- (void)pContext;
- (void)pUserData;
-
- return true;
-}
-
-void dr2d_on_delete_context_cairo(dr2d_context* pContext)
-{
- assert(pContext != NULL);
- (void)pContext;
-}
-
-bool dr2d_on_create_surface_cairo(dr2d_surface* pSurface, float width, float height)
-{
- assert(pSurface != NULL);
-
- cairo_surface_data* pCairoData = dr2d_get_surface_extra_data(pSurface);
- if (pCairoData == NULL) {
- return false;
- }
-
- if (width != 0 && height != 0) {
- pCairoData->pCairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, (int)width, (int)height);
- if (pCairoData->pCairoSurface == NULL) {
- return false;
- }
-
- pCairoData->pCairoContext = cairo_create(pCairoData->pCairoSurface);
- if (pCairoData->pCairoContext == NULL) {
- cairo_surface_destroy(pCairoData->pCairoSurface);
- return false;
- }
- } else {
- pCairoData->pCairoSurface = NULL;
- pCairoData->pCairoContext = NULL;
- }
-
-
- return true;
-}
-
-void dr2d_on_delete_surface_cairo(dr2d_surface* pSurface)
-{
- assert(pSurface != NULL);
-
- cairo_surface_data* pCairoData = dr2d_get_surface_extra_data(pSurface);
- if (pCairoData != NULL)
- {
- cairo_destroy(pCairoData->pCairoContext);
- cairo_surface_destroy(pCairoData->pCairoSurface);
- }
-}
-
-bool dr2d_on_create_font_cairo(dr2d_font* pFont)
-{
- cairo_font_data* pCairoFont = dr2d_get_font_extra_data(pFont);
- if (pCairoFont == NULL) {
- return false;
- }
-
- cairo_font_slant_t cairoSlant = CAIRO_FONT_SLANT_NORMAL;
- if (pFont->slant == dr2d_font_slant_italic) {
- cairoSlant = CAIRO_FONT_SLANT_ITALIC;
- } else if (pFont->slant == dr2d_font_slant_oblique) {
- cairoSlant = CAIRO_FONT_SLANT_OBLIQUE;
- }
-
- cairo_font_weight_t cairoWeight = CAIRO_FONT_WEIGHT_NORMAL;
- if (pFont->weight == dr2d_font_weight_bold || pFont->weight == dr2d_font_weight_semi_bold || pFont->weight == dr2d_font_weight_extra_bold || pFont->weight == dr2d_font_weight_heavy) {
- cairoWeight = CAIRO_FONT_WEIGHT_BOLD;
- }
-
- pCairoFont->pFace = cairo_toy_font_face_create(pFont->family, cairoSlant, cairoWeight);
- if (pCairoFont->pFace == NULL) {
- return false;
- }
-
- cairo_matrix_t fontMatrix;
- cairo_matrix_init_scale(&fontMatrix, (double)pFont->size, (double)pFont->size);
- cairo_matrix_rotate(&fontMatrix, pFont->rotation * (3.14159265 / 180.0));
-
- cairo_matrix_t ctm;
- cairo_matrix_init_identity(&ctm);
-
- cairo_font_options_t* options = cairo_font_options_create();
- cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_SUBPIXEL); // TODO: Control this with option flags in pFont.
-
- pCairoFont->pFont = cairo_scaled_font_create(pCairoFont->pFace, &fontMatrix, &ctm, options);
- if (pCairoFont->pFont == NULL) {
- cairo_font_face_destroy(pCairoFont->pFace);
- return false;
- }
-
-
- // Metrics.
- cairo_font_extents_t fontMetrics;
- cairo_scaled_font_extents(pCairoFont->pFont, &fontMetrics);
-
- pCairoFont->metrics.ascent = fontMetrics.ascent;
- pCairoFont->metrics.descent = fontMetrics.descent;
- //pCairoFont->metrics.lineHeight = fontMetrics.height;
- pCairoFont->metrics.lineHeight = fontMetrics.ascent + fontMetrics.descent;
-
- // The width of a space needs to be retrieved via glyph metrics.
- const char space[] = " ";
- cairo_text_extents_t spaceMetrics;
- cairo_scaled_font_text_extents(pCairoFont->pFont, space, &spaceMetrics);
- pCairoFont->metrics.spaceWidth = spaceMetrics.x_advance;
-
- return true;
-}
-
-void dr2d_on_delete_font_cairo(dr2d_font* pFont)
-{
- cairo_font_data* pCairoFont = dr2d_get_font_extra_data(pFont);
- if (pCairoFont == NULL) {
- return;
- }
-
- cairo_scaled_font_destroy(pCairoFont->pFont);
- cairo_font_face_destroy(pCairoFont->pFace);
-}
-
-bool dr2d_on_create_image_cairo(dr2d_image* pImage, unsigned int stride, const void* pData)
-{
- cairo_image_data* pCairoImage = dr2d_get_image_extra_data(pImage);
- if (pCairoImage == NULL) {
- return false;
- }
-
- size_t dataSize = pImage->height * pImage->width * 4;
- pCairoImage->pData = malloc(dataSize);
- if (pCairoImage->pData == NULL) {
- return false;
- }
-
- if (pData != NULL)
- {
- for (unsigned int iRow = 0; iRow < pImage->height; ++iRow)
- {
- const unsigned int iRowSrc = iRow; //pImage->height - (iRow + 1);
- const unsigned int iRowDst = iRow;
-
- for (unsigned int iCol = 0; iCol < pImage->width; ++iCol)
- {
- unsigned int srcTexel = ((const unsigned int*)(pData ))[ (iRowSrc * (stride/4)) + iCol];
- unsigned int* dstTexel = (( unsigned int*)(pCairoImage->pData)) + (iRowDst * pImage->width) + iCol;
-
- unsigned int srcTexelA = (srcTexel & 0xFF000000) >> 24;
- unsigned int srcTexelB = (srcTexel & 0x00FF0000) >> 16;
- unsigned int srcTexelG = (srcTexel & 0x0000FF00) >> 8;
- unsigned int srcTexelR = (srcTexel & 0x000000FF) >> 0;
-
- srcTexelB = (unsigned int)(srcTexelB * (srcTexelA / 255.0f));
- srcTexelG = (unsigned int)(srcTexelG * (srcTexelA / 255.0f));
- srcTexelR = (unsigned int)(srcTexelR * (srcTexelA / 255.0f));
-
- *dstTexel = (srcTexelR << 16) | (srcTexelG << 8) | (srcTexelB << 0) | (srcTexelA << 24);
- }
- }
- }
-
- pCairoImage->pCairoSurface = cairo_image_surface_create_for_data(pCairoImage->pData, CAIRO_FORMAT_ARGB32, (int)pImage->width, (int)pImage->height, (int)pImage->width*4);
- if (pCairoImage->pCairoSurface == NULL) {
- free(pCairoImage->pData);
- return false;
- }
-
- return true;
-}
-
-void dr2d_on_delete_image_cairo(dr2d_image* pImage)
-{
- cairo_image_data* pCairoImage = dr2d_get_image_extra_data(pImage);
- if (pCairoImage == NULL) {
- return;
- }
-
- cairo_surface_destroy(pCairoImage->pCairoSurface);
- free(pCairoImage->pData);
-}
-
-
-void dr2d_begin_draw_cairo(dr2d_surface* pSurface)
-{
- assert(pSurface != NULL);
-
- cairo_surface_data* pCairoData = dr2d_get_surface_extra_data(pSurface);
- if (pCairoData == NULL) {
- return;
- }
-
- cairo_set_antialias(pCairoData->pCairoContext, CAIRO_ANTIALIAS_NONE);
-}
-
-void dr2d_end_draw_cairo(dr2d_surface* pSurface)
-{
- assert(pSurface != NULL);
-
- cairo_surface_data* pCairoData = dr2d_get_surface_extra_data(pSurface);
- if (pCairoData == NULL) {
- return;
- }
-
- cairo_set_antialias(pCairoData->pCairoContext, CAIRO_ANTIALIAS_DEFAULT);
-}
-
-void dr2d_clear_cairo(dr2d_surface* pSurface, dr2d_color color)
-{
- // TODO: I forget... is this supposed to ignore the current clip? If so, this needs to be changed so that
- // the clip is reset first then restored afterwards.
- dr2d_draw_rect_cairo(pSurface, 0, 0, dr2d_get_surface_width(pSurface), dr2d_get_surface_height(pSurface), color);
-}
-
-void dr2d_draw_rect_cairo(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color)
-{
- assert(pSurface != NULL);
-
- cairo_surface_data* pCairoData = dr2d_get_surface_extra_data(pSurface);
- if (pCairoData != NULL)
- {
- cairo_set_source_rgba(pCairoData->pCairoContext, color.r / 255.0, color.g / 255.0, color.b / 255.0, color.a / 255.0);
- cairo_rectangle(pCairoData->pCairoContext, left, top, (right - left), (bottom - top));
- cairo_fill(pCairoData->pCairoContext);
- }
-}
-
-void dr2d_draw_rect_outline_cairo(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float outlineWidth)
-{
- cairo_surface_data* pCairoData = dr2d_get_surface_extra_data(pSurface);
- if (pCairoData == NULL) {
- return;
- }
-
- cairo_t* cr = pCairoData->pCairoContext;
-
- cairo_set_source_rgba(cr, color.r / 255.0, color.g / 255.0, color.b / 255.0, color.a / 255.0);
-
- // We do this as 4 separate rectangles.
- cairo_rectangle(cr, left, top, outlineWidth, bottom - top); // Left
- cairo_fill(cr);
- cairo_rectangle(cr, right - outlineWidth, top, outlineWidth, bottom - top); // Right
- cairo_fill(cr);
- cairo_rectangle(cr, left + outlineWidth, top, right - left - (outlineWidth*2), outlineWidth); // Top
- cairo_fill(cr);
- cairo_rectangle(cr, left + outlineWidth, bottom - outlineWidth, right - left - (outlineWidth*2), outlineWidth); // Bottom
- cairo_fill(cr);
-}
-
-void dr2d_draw_rect_with_outline_cairo(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float outlineWidth, dr2d_color outlineColor)
-{
- dr2d_draw_rect_cairo(pSurface, left + outlineWidth, top + outlineWidth, right - outlineWidth, bottom - outlineWidth, color);
- dr2d_draw_rect_outline_cairo(pSurface, left, top, right, bottom, outlineColor, outlineWidth);
-}
-
-void dr2d_draw_round_rect_cairo(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius)
-{
- // FIXME: This does not draw rounded corners.
- (void)radius;
-
- dr2d_draw_rect_cairo(pSurface, left, top, right, bottom, color);
-}
-
-void dr2d_draw_round_rect_outline_cairo(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius, float outlineWidth)
-{
- // FIXME: This does not draw rounded corners.
- (void)radius;
-
- dr2d_draw_rect_outline_cairo(pSurface, left, top, right, bottom, color, outlineWidth);
-}
-
-void dr2d_draw_round_rect_with_outline_cairo(dr2d_surface* pSurface, float left, float top, float right, float bottom, dr2d_color color, float radius, float outlineWidth, dr2d_color outlineColor)
-{
- // FIXME: This does not draw rounded corners.
- (void)radius;
-
- dr2d_draw_rect_with_outline_cairo(pSurface, left, top, right, bottom, color, outlineWidth, outlineColor);
-}
-
-void dr2d_draw_text_cairo(dr2d_surface* pSurface, dr2d_font* pFont, const char* text, size_t textSizeInBytes, float posX, float posY, dr2d_color color, dr2d_color backgroundColor)
-{
- cairo_surface_data* pCairoSurface = dr2d_get_surface_extra_data(pSurface);
- if (pCairoSurface == NULL) {
- return;
- }
-
- cairo_t* cr = pCairoSurface->pCairoContext;
-
-
- cairo_font_data* pCairoFont = dr2d_get_font_extra_data(pFont);
- if (pCairoFont == NULL) {
- return;
- }
-
- // Cairo expends null terminated strings, however the input string is not guaranteed to be null terminated.
- char* textNT;
- if (textSizeInBytes != (size_t)-1) {
- textNT = malloc(textSizeInBytes + 1);
- memcpy(textNT, text, textSizeInBytes);
- textNT[textSizeInBytes] = '\0';
- } else {
- textNT = (char*)text;
- }
-
-
- cairo_set_scaled_font(cr, pCairoFont->pFont);
-
-
-
- // Background.
- cairo_text_extents_t textMetrics;
- cairo_text_extents(cr, textNT, &textMetrics);
- cairo_set_source_rgba(cr, backgroundColor.r / 255.0, backgroundColor.g / 255.0, backgroundColor.b / 255.0, backgroundColor.a / 255.0);
- cairo_rectangle(cr, posX, posY, textMetrics.x_advance, pCairoFont->metrics.lineHeight);
- cairo_fill(cr);
-
-
- // Text.
- cairo_move_to(cr, posX, posY + pCairoFont->metrics.ascent);
- cairo_set_source_rgba(cr, color.r / 255.0, color.g / 255.0, color.b / 255.0, color.a / 255.0);
- cairo_show_text(cr, textNT);
-
-
- if (textNT != text) {
- free(textNT);
- }
-}
-
-void dr2d_draw_image_cairo(dr2d_surface* pSurface, dr2d_image* pImage, dr2d_draw_image_args* pArgs)
-{
- cairo_surface_data* pCairoSurface = dr2d_get_surface_extra_data(pSurface);
- if (pCairoSurface == NULL) {
- return;
- }
-
- cairo_image_data* pCairoImage = dr2d_get_image_extra_data(pImage);
- if (pCairoImage == NULL) {
- return;
- }
-
- cairo_t* cr = pCairoSurface->pCairoContext;
-
- cairo_save(cr);
- cairo_translate(cr, pArgs->dstX, pArgs->dstY);
-
- // Background.
- if ((pArgs->options & DR2D_IMAGE_DRAW_BACKGROUND) != 0)
- {
- cairo_set_source_rgba(cr, pArgs->backgroundColor.r / 255.0, pArgs->backgroundColor.g / 255.0, pArgs->backgroundColor.b / 255.0, pArgs->backgroundColor.a / 255.0);
- cairo_rectangle(cr, 0, 0, pArgs->dstWidth, pArgs->dstHeight);
- cairo_fill(cr);
- }
-
- if (pArgs->foregroundTint.r == 255 && pArgs->foregroundTint.g == 255 && pArgs->foregroundTint.b == 255 && pArgs->foregroundTint.a == 255) {
- cairo_scale(cr, pArgs->dstWidth / pArgs->srcWidth, pArgs->dstHeight / pArgs->srcHeight);
- cairo_set_source_surface(cr, pCairoImage->pCairoSurface, pArgs->srcX, pArgs->srcY);
- cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
- cairo_paint(cr);
- } else {
- // Slower path. The image needs to be tinted. We create a temporary image for this.
- // NOTE: This is incorrect. It's just a temporary solution until I figure out a better way.
- cairo_surface_t* pTempImageSurface = cairo_surface_create_similar_image(pCairoImage->pCairoSurface, CAIRO_FORMAT_ARGB32,
- cairo_image_surface_get_width(pCairoImage->pCairoSurface), cairo_image_surface_get_height(pCairoImage->pCairoSurface));
- if (pTempImageSurface != NULL) {
- cairo_t* cr2 = cairo_create(pTempImageSurface);
-
- cairo_set_operator(cr2, CAIRO_OPERATOR_SOURCE);
- cairo_set_source_surface(cr2, pCairoImage->pCairoSurface, 0, 0);
- cairo_pattern_set_filter(cairo_get_source(cr2), CAIRO_FILTER_NEAREST);
- cairo_paint(cr2);
-
- // Tint.
- cairo_set_operator(cr2, CAIRO_OPERATOR_ATOP);
- cairo_set_source_rgba(cr2, pArgs->foregroundTint.r / 255.0, pArgs->foregroundTint.g / 255.0, pArgs->foregroundTint.b / 255.0, 1);
- cairo_rectangle(cr2, 0, 0, pArgs->dstWidth, pArgs->dstHeight);
- cairo_fill(cr2);
-
- /*cairo_set_operator(cr2, CAIRO_OPERATOR_MULTIPLY);
- cairo_set_source_surface(cr2, pCairoImage->pCairoSurface, 0, 0);
- cairo_pattern_set_filter(cairo_get_source(cr2), CAIRO_FILTER_NEAREST);
- cairo_paint(cr2);*/
-
- // Draw the temporary surface onto the main surface.
- cairo_scale(cr, pArgs->dstWidth / pArgs->srcWidth, pArgs->dstHeight / pArgs->srcHeight);
- cairo_set_source_surface(cr, pTempImageSurface, pArgs->srcX, pArgs->srcY);
- cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
- //cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
- cairo_paint(cr);
-
- cairo_destroy(cr2);
- cairo_surface_destroy(pTempImageSurface);
- }
- }
-
- cairo_restore(cr);
-}
-
-void dr2d_set_clip_cairo(dr2d_surface* pSurface, float left, float top, float right, float bottom)
-{
- cairo_surface_data* pCairoData = dr2d_get_surface_extra_data(pSurface);
- if (pCairoData == NULL) {
- return;
- }
-
- pCairoData->clipRectLeft = left;
- pCairoData->clipRectTop = top;
- pCairoData->clipRectRight = right;
- pCairoData->clipRectBottom = bottom;
-
- cairo_reset_clip(pCairoData->pCairoContext);
- cairo_rectangle(pCairoData->pCairoContext, left, top, right - left, bottom - top);
- cairo_clip(pCairoData->pCairoContext);
-}
-
-void dr2d_get_clip_cairo(dr2d_surface* pSurface, float* pLeftOut, float* pTopOut, float* pRightOut, float* pBottomOut)
-{
- (void)pSurface;
- (void)pLeftOut;
- (void)pTopOut;
- (void)pRightOut;
- (void)pBottomOut;
-
- cairo_surface_data* pCairoData = dr2d_get_surface_extra_data(pSurface);
- if (pCairoData == NULL) {
- return;
- }
-
- if (pLeftOut) { *pLeftOut = pCairoData->clipRectLeft; }
- if (pTopOut) { *pTopOut = pCairoData->clipRectTop; }
- if (pRightOut) { *pRightOut = pCairoData->clipRectRight; }
- if (pBottomOut) { *pBottomOut = pCairoData->clipRectBottom; }
-}
-
-
-dr2d_image_format dr2d_get_optimal_image_format_cairo(dr2d_context* pContext)
-{
- (void)pContext;
- return dr2d_image_format_argb8;
-}
-
-void* dr2d_map_image_data_cairo(dr2d_image* pImage, unsigned accessFlags)
-{
- (void)pImage;
- (void)accessFlags;
- return NULL;
-}
-
-void dr2d_unmap_image_data_cairo(dr2d_image* pImage)
-{
- (void)pImage;
-}
-
-
-bool dr2d_get_font_metrics_cairo(dr2d_font* pFont, dr2d_font_metrics* pMetricsOut)
-{
- cairo_font_data* pCairoFont = dr2d_get_font_extra_data(pFont);
- if (pCairoFont == NULL) {
- return false;
- }
-
- if (pMetricsOut) {
- *pMetricsOut = pCairoFont->metrics;
- }
-
- return true;
-}
-
-static size_t dr2d__utf32_to_utf8(unsigned int utf32, char* utf8, size_t utf8Size)
-{
- // NOTE: This function is untested.
-
- size_t utf8ByteCount = 0;
- if (utf32 < 0x80) {
- utf8ByteCount = 1;
- } else if (utf32 < 0x800) {
- utf8ByteCount = 2;
- } else if (utf32 < 0x10000) {
- utf8ByteCount = 3;
- } else if (utf32 < 0x110000) {
- utf8ByteCount = 4;
- }
-
- if (utf8ByteCount > utf8Size) {
- if (utf8 != NULL && utf8Size > 0) {
- utf8[0] = '\0';
- }
- return 0;
- }
-
- utf8 += utf8ByteCount;
- if (utf8ByteCount < utf8Size) {
- utf8[0] = '\0'; // Null terminate.
- }
-
- const unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC};
- switch (utf8ByteCount)
- {
- case 4: *--utf8 = (char)((utf32 | 0x80) & 0xBF); utf32 >>= 6;
- case 3: *--utf8 = (char)((utf32 | 0x80) & 0xBF); utf32 >>= 6;
- case 2: *--utf8 = (char)((utf32 | 0x80) & 0xBF); utf32 >>= 6;
- case 1: *--utf8 = (char)(utf32 | firstByteMark[utf8ByteCount]);
- default: break;
- }
-
- return utf8ByteCount;
-}
-
-bool dr2d_get_glyph_metrics_cairo(dr2d_font* pFont, unsigned int utf32, dr2d_glyph_metrics* pGlyphMetrics)
-{
- cairo_font_data* pCairoFont = dr2d_get_font_extra_data(pFont);
- if (pCairoFont == NULL) {
- return false;
- }
-
- // The UTF-32 code point needs to be converted to a UTF-8 character.
- char utf8[16];
- size_t utf8len = dr2d__utf32_to_utf8(utf32, utf8, sizeof(utf8)); // This will null-terminate.
- if (utf8len == 0) {
- return false; // Error converting UTF-32 to UTF-8.
- }
-
-
- cairo_text_extents_t glyphExtents;
- cairo_scaled_font_text_extents(pCairoFont->pFont, utf8, &glyphExtents);
-
- if (pGlyphMetrics)
- {
- pGlyphMetrics->width = glyphExtents.width;
- pGlyphMetrics->height = glyphExtents.height;
- pGlyphMetrics->originX = glyphExtents.x_bearing;
- pGlyphMetrics->originY = glyphExtents.y_bearing;
- pGlyphMetrics->advanceX = glyphExtents.x_advance;
- pGlyphMetrics->advanceY = glyphExtents.y_advance;
- }
-
- return true;
-}
-
-bool dr2d_measure_string_cairo(dr2d_font* pFont, const char* text, size_t textSizeInBytes, float* pWidthOut, float* pHeightOut)
-{
- cairo_font_data* pCairoFont = dr2d_get_font_extra_data(pFont);
- if (pCairoFont == NULL) {
- return false;
- }
-
-
- // Cairo expends null terminated strings, however the input string is not guaranteed to be null terminated.
- char* textNT;
- if (textSizeInBytes != (size_t)-1) {
- textNT = malloc(textSizeInBytes + 1);
- if (textNT == NULL) {
- return false;
- }
- memcpy(textNT, text, textSizeInBytes);
- textNT[textSizeInBytes] = '\0';
- } else {
- textNT = (char*)text;
- }
-
-
- cairo_text_extents_t textMetrics;
- cairo_scaled_font_text_extents(pCairoFont->pFont, textNT, &textMetrics);
-
- if (pWidthOut) {
- *pWidthOut = textMetrics.x_advance;
- }
- if (pHeightOut) {
- //*pHeightOut = textMetrics.height;
- *pHeightOut = pCairoFont->metrics.ascent + pCairoFont->metrics.descent;
- }
-
-
- if (textNT != text) {
- free(textNT);
- }
-
- return true;
-}
-
-bool dr2d_get_text_cursor_position_from_point_cairo(dr2d_font* pFont, const char* text, size_t textSizeInBytes, float maxWidth, float inputPosX, float* pTextCursorPosXOut, size_t* pCharacterIndexOut)
-{
- cairo_font_data* pCairoFont = dr2d_get_font_extra_data(pFont);
- if (pCairoFont == NULL) {
- return false;
- }
-
- cairo_glyph_t* pGlyphs = NULL;
- int glyphCount = 0;
- cairo_status_t result = cairo_scaled_font_text_to_glyphs(pCairoFont->pFont, 0, 0, text, textSizeInBytes, &pGlyphs, &glyphCount, NULL, NULL, NULL);
- if (result != CAIRO_STATUS_SUCCESS) {
- return false;
- }
-
- float cursorPosX = 0;
- int charIndex = 0;
-
- // We just iterate over each glyph until we find the one sitting under .
- float runningPosX = 0;
- for (int iGlyph = 0; iGlyph < glyphCount; ++iGlyph)
- {
- cairo_text_extents_t glyphMetrics;
- cairo_scaled_font_glyph_extents(pCairoFont->pFont, pGlyphs + iGlyph, 1, &glyphMetrics);
-
- float glyphLeft = runningPosX;
- float glyphRight = glyphLeft + glyphMetrics.x_advance;
-
- // Are we sitting on top of inputPosX?
- if (inputPosX >= glyphLeft && inputPosX <= glyphRight)
- {
- float glyphHalf = glyphLeft + ceilf(((glyphRight - glyphLeft) / 2.0f));
- if (inputPosX <= glyphHalf) {
- cursorPosX = glyphLeft;
- charIndex = iGlyph;
- } else {
- cursorPosX = glyphRight;
- charIndex = iGlyph + 1;
- }
-
- break;
- }
- else
- {
- // Have we moved past maxWidth?
- if (glyphRight > maxWidth)
- {
- cursorPosX = maxWidth;
- charIndex = iGlyph;
- break;
- }
- else
- {
- runningPosX = glyphRight;
-
- cursorPosX = runningPosX;
- charIndex = iGlyph;
- }
- }
- }
-
- cairo_glyph_free(pGlyphs);
-
- if (pTextCursorPosXOut) {
- *pTextCursorPosXOut = cursorPosX;
- }
- if (pCharacterIndexOut) {
- *pCharacterIndexOut = charIndex;
- }
-
- return true;
-}
-
-bool dr2d_get_text_cursor_position_from_char_cairo(dr2d_font* pFont, const char* text, size_t characterIndex, float* pTextCursorPosXOut)
-{
- cairo_font_data* pCairoFont = dr2d_get_font_extra_data(pFont);
- if (pCairoFont == NULL) {
- return false;
- }
-
- cairo_glyph_t* pGlyphs = NULL;
- int glyphCount = 0;
- cairo_status_t result = cairo_scaled_font_text_to_glyphs(pCairoFont->pFont, 0, 0, text, -1, &pGlyphs, &glyphCount, NULL, NULL, NULL);
- if (result != CAIRO_STATUS_SUCCESS) {
- return false;
- }
-
- float cursorPosX = 0;
-
- // We just iterate over each glyph until we find the one sitting under .
- for (int iGlyph = 0; iGlyph < glyphCount; ++iGlyph)
- {
- if (iGlyph == (int)characterIndex) {
- break;
- }
-
- cairo_text_extents_t glyphMetrics;
- cairo_scaled_font_glyph_extents(pCairoFont->pFont, pGlyphs + iGlyph, 1, &glyphMetrics);
-
- cursorPosX += glyphMetrics.x_advance;
- }
-
- cairo_glyph_free(pGlyphs);
-
- if (pTextCursorPosXOut) {
- *pTextCursorPosXOut = cursorPosX;
- }
-
- return true;
-}
-#endif // Cairo
-#endif
-
-/*
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-For more information, please refer to
-*/
diff --git a/source/engine/thirdparty/dr_libs/old/dr_audio.h b/source/engine/thirdparty/dr_libs/old/dr_audio.h
deleted file mode 100644
index 87abde1..0000000
--- a/source/engine/thirdparty/dr_libs/old/dr_audio.h
+++ /dev/null
@@ -1,5307 +0,0 @@
-// Audio playback, recording and mixing. Public domain. See "unlicense" statement at the end of this file.
-// dr_audio - v0.0 (unversioned) - Release Date TBD
-//
-// David Reid - mackron@gmail.com
-
-// !!!!! THIS IS WORK IN PROGRESS !!!!!
-
-// USAGE
-//
-// dr_audio is a single-file library. To use it, do something like the following in one .c file.
-// #define DR_AUDIO_IMPLEMENTATION
-// #include "dr_audio.h"
-//
-// You can then #include this file in other parts of the program as you would with any other header file.
-//
-// dr_audio supports loading and decoding of WAV, FLAC and Vorbis streams via dr_wav, dr_flac and stb_vorbis respectively. To enable
-// these all you need to do is #include "dr_audio.h" _after_ #include "dr_wav.h", #include "dr_flac.h" and #include "stb_vorbis.c" in
-// the implementation file, like so:
-//
-// #define DR_WAV_IMPLEMENTATION
-// #include "dr_wav.h"
-//
-// #define DR_FLAC_IMPLEMENTATION
-// #include "dr_flac.h"
-//
-// #define STB_VORBIS_IMPLEMENTATION
-// #include "stb_vorbis.c"
-//
-// #define DR_AUDIO_IMPLEMENTATION
-// #include "dr_audio.h"
-//
-// dr_wav, dr_flac and stb_vorbis are entirely optional, and dr_audio will automatically detect the ones that are available without
-// any additional intervention on your part.
-//
-//
-// dr_audio has a layered API with different levels of flexibility vs simplicity. An example of the high level API follows:
-//
-// dra_device* pDevice;
-// dra_result result = dra_device_create(NULL, dra_device_type_playback, &pDevice);
-// if (result != DRA_RESULT_SUCCESS) {
-// return -1;
-// }
-//
-// dra_voice* pVoice;
-// result = dra_voice_create_from_file(pDevice, "my_song.flac", &pVoice);
-// if (result != DRA_RESULT_SUCCESS) {
-// return -1;
-// }
-//
-// dra_voice_play(pVoice, DR_FALSE);
-//
-// ...
-//
-// dra_voice_delete(pVoice);
-// dra_device_delete(pDevice);
-//
-//
-// An example of the low level API:
-//
-// dra_context context;
-// dra_result result = dra_context_init(&context); // Initializes the backend (DirectSound/ALSA)
-// if (result != DRA_RESULT_SUCCESS) {
-// return -1;
-// }
-//
-// unsigned int deviceID = 0; // Default device
-// unsigned int channels = 2; // Stereo
-// unsigned int sampleRate = 48000;
-// unsigned int latencyInMilliseconds = 0; // 0 will default to DR_AUDIO_DEFAULT_LATENCY.
-// dra_device device;
-// result = dra_device_init_ex(&context, dra_device_type_playback, deviceID, channels, sampleRate, latencyInMilliseconds, &device);
-// if (result != DRA_RESULT_SUCCESS) {
-// return -1;
-// }
-//
-// dra_voice* pVoice;
-// dra_result result = dra_voice_create(pDevice, dra_format_f32, channels, sampleRate, voiceBufferSizeInBytes, pVoiceSampleData, &pVoice);
-// if (result != DRA_RESULT_SUCCESS) {
-// return -1;
-// }
-//
-// ...
-//
-// // Sometime later you may need to update the data inside the voice's internal buffer... It's your job to handle
-// // synchronization - have fun! Hint: use playback events at fixed locations to know when a region of the buffer
-// // is available for updating.
-// float* pVoiceData = (float*)dra_voice_get_buffer_ptr_by_sample(pVoice, sampleOffset);
-// if (pVoiceData == NULL) {
-// return -1;
-// }
-//
-// memcpy(pVoiceData, pNewVoiceData, sizeof(float) * samplesToCopy);
-//
-// ...
-//
-// dra_voice_delete(pVoice);
-// dra_device_uninit(pDevice);
-// dra_context_uninit(pContext);
-//
-// In the above example the voice and device are configured to use the same number of channels and sample rate, however they are
-// allowed to differ, in which case dr_audio will automatically convert the data. Note that sample rate conversion is currently
-// very low quality.
-//
-// To handle streaming buffers, you can attach a callback that's fired when a voice's playback position reaches a certain point.
-// Usually you would set this to the middle and end of the buffer, filling the previous half with new data. Use the
-// dra_voice_add_playback_event() API for this.
-//
-// The playback position of a voice can be retrieved and set with dra_voice_get_playback_position() and dra_voice_set_playback_position()
-// respctively. The playback is specified in samples. dra_voice_get_playback_position() will always return a value which is a multiple
-// of the channel count. dra_voice_set_playback_position() will round the specified sample index to a multiple of the channel count.
-//
-//
-// dr_audio has support for submixing which basically allows you to control volume (and in the future, effects) for groups of sounds
-// which would typically be organized into categories. An abvious example would be in games where you may want to have separate volume
-// controls for music, voices, special effects, etc. To do submixing, all you need to do is create a mixer. There is a master mixer
-// associated with every device, and all newly created mixers are a child of the master mixer, by default:
-//
-// dra_mixer* pMusicMixer;
-// dra_result result = dra_mixer_create(pDevice, &pMusicMixer);
-// if (result != DRA_RESULT_SUCCESS) {
-// return -1;
-// }
-//
-// // At this point pMusicMixer is a child of the device's master mixer. To change the hierarchy, just do something like this:
-// dra_mixer_attach_submixer(pSomeOtherMixer, pMusicMixer);
-//
-// // A voice is attached to the master mixer by default, but you can attach it to a different mixer like this:
-// dra_mixer_attach_voice(pMusicMixer, pMyMusicVoice);
-//
-// // Control the volume of the mixer...
-// dra_mixer_set_volume(pMusicMixer, 0.5f); // <-- The volume is linear, so this is half volume.
-//
-//
-//
-// dr_audio includes an abstraction for audio decoding. Built-in support is included for WAV, FLAC and Vorbis streams:
-//
-// dra_decoder decoder;
-// if (dra_decoder_open_file(&decoder, filePath) != DRA_RESULT_SUCCESS) {
-// return -1;
-// }
-//
-// dr_uint64 samplesRead = dra_decoder_read_f32(&decoder, samplesToRead, pSamples);
-// update_my_voice_data(pVoice, pSamples, samplesRead);
-//
-// dra_decoder_close(&decoder);
-//
-// Decoders can be opened/initialized from files, a block of memory, or application-defined callbacks.
-//
-//
-//
-// OPTIONS
-// #define these options before including this file.
-//
-// #define DR_AUDIO_NO_DSOUND
-// Disables the DirectSound backend. Note that this is the only backend for the Windows platform.
-//
-// #define DR_AUDIO_NO_ALSA
-// Disables the ALSA backend. Note that this is the only backend for the Linux platform.
-//
-// #define DR_AUDIO_NO_STDIO
-// Disables any functions that use stdio, such as dra_sound_create_from_file().
-
-
-#ifndef dr_audio2_h
-#define dr_audio2_h
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include
-
-#ifndef DR_SIZED_TYPES_DEFINED
-#define DR_SIZED_TYPES_DEFINED
-#if defined(_MSC_VER) && _MSC_VER < 1600
-typedef signed char dr_int8;
-typedef unsigned char dr_uint8;
-typedef signed short dr_int16;
-typedef unsigned short dr_uint16;
-typedef signed int dr_int32;
-typedef unsigned int dr_uint32;
-typedef signed __int64 dr_int64;
-typedef unsigned __int64 dr_uint64;
-#else
-#include
-typedef int8_t dr_int8;
-typedef uint8_t dr_uint8;
-typedef int16_t dr_int16;
-typedef uint16_t dr_uint16;
-typedef int32_t dr_int32;
-typedef uint32_t dr_uint32;
-typedef int64_t dr_int64;
-typedef uint64_t dr_uint64;
-#endif
-typedef dr_int8 dr_bool8;
-typedef dr_int32 dr_bool32;
-#define DR_TRUE 1
-#define DR_FALSE 0
-#endif
-
-#ifndef DR_AUDIO_MAX_CHANNEL_COUNT
-#define DR_AUDIO_MAX_CHANNEL_COUNT 16
-#endif
-
-#ifndef DR_AUDIO_MAX_EVENT_COUNT
-#define DR_AUDIO_MAX_EVENT_COUNT 16
-#endif
-
-#define DR_AUDIO_EVENT_ID_STOP 0xFFFFFFFFFFFFFFFFULL
-#define DR_AUDIO_EVENT_ID_PLAY 0xFFFFFFFFFFFFFFFEULL
-
-typedef int dra_result;
-#define DRA_RESULT_SUCCESS 0
-#define DRA_RESULT_UNKNOWN_ERROR -1
-#define DRA_RESULT_INVALID_ARGS -2
-#define DRA_RESULT_OUT_OF_MEMORY -3
-#define DRA_RESULT_FAILED_TO_OPEN_FILE -4
-#define DRA_RESULT_NO_BACKEND -1024
-#define DRA_RESULT_NO_BACKEND_DEVICE -1025
-#define DRA_RESULT_NO_DECODER -1026
-#define DRA_FAILED(result) ((result) != 0)
-#define DRA_SUCCEEDED(result) ((result) == 0)
-
-#define DRA_MIXER_FLAG_PAUSED (1 << 0)
-
-typedef enum
-{
- dra_device_type_playback = 0,
- dra_device_type_capture
-} dra_device_type;
-
-typedef enum
-{
- dra_format_u8 = 0,
- dra_format_s16,
- dra_format_s24,
- dra_format_s32,
- dra_format_f32,
- dra_format_default = dra_format_f32
-} dra_format;
-
-typedef enum
-{
- dra_src_algorithm_none,
- dra_src_algorithm_linear,
-} dra_src_algorithm;
-
-// dra_thread_event_type is used internally for thread management.
-typedef enum
-{
- dra_thread_event_type_none,
- dra_thread_event_type_terminate,
- dra_thread_event_type_play
-} dra_thread_event_type;
-
-typedef struct dra_backend dra_backend;
-typedef struct dra_backend_device dra_backend_device;
-
-typedef struct dra_context dra_context;
-typedef struct dra_device dra_device;
-typedef struct dra_mixer dra_mixer;
-typedef struct dra_voice dra_voice;
-
-typedef void* dra_thread;
-typedef void* dra_mutex;
-typedef void* dra_semaphore;
-
-typedef void (* dra_event_proc) (dr_uint64 eventID, void* pUserData);
-typedef void (* dra_samples_processed_proc)(dra_device* pDevice, const size_t sampleCount, const float* pSamples, void* pUserData);
-
-typedef struct
-{
- dr_uint64 id;
- void* pUserData;
- dr_uint64 sampleIndex;
- dra_event_proc proc;
- dra_voice* pVoice;
- dr_bool32 hasBeenSignaled;
-} dra__event;
-
-typedef struct
-{
- size_t firstEvent;
- size_t eventCount;
- size_t eventBufferSize;
- dra__event* pEvents;
- dra_mutex lock;
-} dra__event_queue;
-
-struct dra_context
-{
- dra_backend* pBackend;
-};
-
-struct dra_device
-{
- // The context that created and owns this device.
- dra_context* pContext;
-
- // The backend device. This is used to connect the cross-platform front-end with the backend.
- dra_backend_device* pBackendDevice;
-
- // The main mutex for handling thread-safety.
- dra_mutex mutex;
-
- // The main thread. For playback devices, this is the thread that waits for fragments to finish processing an then
- // mixes and pushes new audio data to the hardware buffer for playback.
- dra_thread thread;
-
- // The semaphore used by the main thread to determine whether or not an event is waiting to be processed.
- dra_semaphore threadEventSem;
-
- // The event type of the most recent event.
- dra_thread_event_type nextThreadEventType;
-
- // TODO: Make these booleans flags.
-
- // Whether or not the device owns the context. This basically just means whether or not the device was created with a null
- // context and needs to delete the context itself when the device is deleted.
- dr_bool32 ownsContext;
-
- // Whether or not the device is being closed. This is used by the thread to determine if it needs to terminate. When
- // dra_device_close() is called, this flag will be set and threadEventSem released. The thread will then see this as it's
- // signal to terminate.
- dr_bool32 isClosed;
-
- // Whether or not the device is currently playing. When at least one voice is playing, this will be DR_TRUE. When there
- // are no voices playing, this will be set to DR_FALSE and the background thread will sit dormant until another voice
- // starts playing or the device is closed.
- dr_bool32 isPlaying;
-
- // Whether or not the device should stop on the next fragment. This is used for stopping playback of devices that
- // have no voice's playing.
- dr_bool32 stopOnNextFragment;
-
-
- // The master mixer. This is the one and only top-level mixer.
- dra_mixer* pMasterMixer;
-
- // The number of voices currently being played. This is used to determine whether or not the device should be placed
- // into a dormant state when nothing is being played.
- size_t playingVoicesCount;
-
-
- // When a playback event is scheduled it is added to this queue. Events are not posted straight away, but are instead
- // placed in a queue for processing later at specific times to ensure the event is posted AFTER the device has actually
- // played the sample the event is set for.
- dra__event_queue eventQueue;
-
- // The function to call when a segment of samples has been processed. This is only really needed for capture devices,
- // but can also be used to keep track of all of the audio data that's passed through a playback device. This is called
- // as the read pointer moves passed each segment and again for the leftover partial segment that'll occur when the
- // device is stopped.
- dra_samples_processed_proc onSamplesProcessed;
- void* pUserDataForOnSamplesProcessed;
-
-
- // The number of channels being used by the device.
- unsigned int channels;
-
- // The sample rate in seconds.
- unsigned int sampleRate;
-};
-
-struct dra_mixer
-{
- // The device that created and owns this mixer.
- dra_device* pDevice;
-
- // Whether or not the mixer is paused.
- dr_uint32 flags;
-
-
- // The parent mixer.
- dra_mixer* pParentMixer;
-
- // The first child mixer.
- dra_mixer* pFirstChildMixer;
-
- // The last child mixer.
- dra_mixer* pLastChildMixer;
-
- // The next sibling mixer.
- dra_mixer* pNextSiblingMixer;
-
- // The previous sibling mixer.
- dra_mixer* pPrevSiblingMixer;
-
-
- // The first voice attached to the mixer.
- dra_voice* pFirstVoice;
-
- // The last voice attached to the mixer.
- dra_voice* pLastVoice;
-
-
- // The volume of the buffer as a linear scale. A value of 0.5 means the sound is at half volume. There is no hard
- // limit on the volume, however going beyond 1 may introduce clipping.
- float linearVolume;
-
-
- // Mixers output the results of the final mix into a buffer referred to as the staging buffer. A parent mixer will
- // use the staging buffer when it mixes the results of it's submixers. This is an offset of pData.
- float* pStagingBuffer;
-
- // A pointer to the buffer containing the sample data of the buffer currently being mixed, as floating point values.
- // This is an offset of pData.
- float* pNextSamplesToMix;
-
-
- // The sample data for pStagingBuffer and pNextSamplesToMix.
- float pData[1];
-};
-
-struct dra_voice
-{
- // The device that created and owns this voice.
- dra_device* pDevice;
-
- // The mixer the voice is attached to. Should never be null. The mixer doesn't "own" the voice - the voice
- // is simply attached to it.
- dra_mixer* pMixer;
-
-
- // The next voice in the linked list of voices attached to the mixer.
- dra_voice* pNextVoice;
-
- // The previous voice in the linked list of voices attached to the mixer.
- dra_voice* pPrevVoice;
-
-
- // The format of the audio data contained within this voice.
- dra_format format;
-
- // The number of channels.
- unsigned int channels;
-
- // The sample rate in seconds.
- unsigned int sampleRate;
-
-
- // The volume of the voice as a linear scale. A value of 0.5 means the sound is at half volume. There is no hard
- // limit on the volume, however going beyond 1 may introduce clipping.
- float linearVolume;
-
-
-
- // Whether or not the voice is currently playing.
- dr_bool32 isPlaying;
-
- // Whether or not the voice is currently looping. Whether or not the voice is looping is determined by the last
- // call to dra_voice_play().
- dr_bool32 isLooping;
-
-
- // The total number of frames in the voice.
- dr_uint64 frameCount;
-
- // The current read position, in frames. An important detail with this is that it's based on the sample rate of the
- // device, not the voice.
- dr_uint64 currentReadPos;
-
-
- // A buffer for storing converted frames. This is used by dra_voice__next_frame(). As frames are converted to
- // floats, that are placed into this buffer.
- float convertedFrame[DR_AUDIO_MAX_CHANNEL_COUNT];
-
-
- // Data for sample rate conversion. Different SRC algorithms will use different data, which will be stored in their
- // own structure.
- struct
- {
- // The sample rate conversion algorithm to use.
- dra_src_algorithm algorithm;
-
- union
- {
- struct
- {
- dr_uint64 prevFrameIndex;
- float prevFrame[DR_AUDIO_MAX_CHANNEL_COUNT];
- float nextFrame[DR_AUDIO_MAX_CHANNEL_COUNT];
- } linear;
- } data;
- } src;
-
-
- // The number of playback notification events. This does not include the stop and play events.
- size_t playbackEventCount;
-
- // A pointer to the list of playback events.
- dra__event playbackEvents[DR_AUDIO_MAX_EVENT_COUNT];
-
- // The event to call when the voice has stopped playing, either naturally or explicitly with dra_voice_stop().
- dra__event stopEvent;
-
- // The event to call when the voice starts playing.
- dra__event playEvent;
-
-
- // Application defined user data.
- void* pUserData;
-
-
- // The size of the buffer in bytes.
- size_t sizeInBytes;
-
- // The actual buffer containing the raw audio data in it's native format. At mix time the data will be converted
- // to floats.
- dr_uint8 pData[1];
-};
-
-
-// dra_context_init()
-dra_result dra_context_init(dra_context* pContext);
-void dra_context_uninit(dra_context* pContext);
-
-// Helper for allocating and initializing a context. If you want to do your own memory management or just want to put the context
-// object on the stack, just use dra_context_init() instead.
-dra_result dra_context_create(dra_context** ppContext);
-void dra_context_delete(dra_context* pContext);
-
-
-// dra_device_init_ex()
-//
-// If deviceID is 0 the default device for the given type is used.
-// format can be dra_format_default which is dra_format_s32.
-// If channels is set to 0, defaults 2 channels (stereo).
-// If sampleRate is set to 0, defaults to 48000.
-// If latency is 0, defaults to 50 milliseconds. See notes about latency above.
-//
-// Concerning the DirectSound backend (From MSDN):
-// Note that if your application is playing sounds as well as capturing them, capture buffer creation can fail when
-// the format of the capture buffer is not the same as that of the primary buffer. The reason is that some cards have
-// only a single clock and cannot support capture and playback at two different frequencies.
-//
-// This means you will need to keep the channels and sample rate consistent across playback and capture devices when
-// using the DirectSound backend.
-dra_result dra_device_init_ex(dra_context* pContext, dra_device_type type, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds, dra_device* pDevice);
-dra_result dra_device_init(dra_context* pContext, dra_device_type type, dra_device* pDevice);
-void dra_device_uninit(dra_device* pDevice);
-
-// Helper for allocating and initializing a device object.
-dra_result dra_device_create_ex(dra_context* pContext, dra_device_type type, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds, dra_device** ppDevice);
-dra_result dra_device_create(dra_context* pContext, dra_device_type type, dra_device** ppDevice);
-void dra_device_delete(dra_device* pDevice);
-
-// Starts a capture device.
-//
-// Do not call this on a playback device - this is managed by dr_audio. This will fail for playback devices.
-dra_result dra_device_start(dra_device* pDevice);
-
-// Stops a capture device.
-//
-// Do not call this on a playback device - this is managed by dr_audio. This will fail for playback devices.
-dra_result dra_device_stop(dra_device* pDevice);
-
-// Sets the function to call when a segment of samples have been processed by the device (either captured
-// or played back). Use this to keep track of the audio data that's passed to a playback device or from a
-// capture device.
-void dra_device_set_samples_processed_callback(dra_device* pDevice, dra_samples_processed_proc proc, void* pUserData);
-
-
-
-
-// dra_mixer_create()
-dra_result dra_mixer_create(dra_device* pDevice, dra_mixer** ppMixer);
-
-// dra_mixer_delete()
-//
-// Deleting a mixer will detach any attached voices and sub-mixers and attach them to the master mixer. It is
-// up to the application to manage the allocation of sub-mixers and voices. Typically you'll want to delete
-// child mixers and voices before deleting a mixer.
-void dra_mixer_delete(dra_mixer* pMixer);
-
-// dra_mixer_attach_submixer()
-void dra_mixer_attach_submixer(dra_mixer* pMixer, dra_mixer* pSubmixer);
-
-// dra_mixer_detach_submixer()
-void dra_mixer_detach_submixer(dra_mixer* pMixer, dra_mixer* pSubmixer);
-
-// dra_mixer_detach_all_submixers()
-void dra_mixer_detach_all_submixers(dra_mixer* pMixer);
-
-// dra_mixer_attach_voice()
-void dra_mixer_attach_voice(dra_mixer* pMixer, dra_voice* pVoice);
-
-// dra_mixer_detach_voice()
-void dra_mixer_detach_voice(dra_mixer* pMixer, dra_voice* pVoice);
-
-// dra_mixer_detach_all_voices()
-void dra_mixer_detach_all_voices(dra_mixer* pMixer);
-
-// dra_voice_set_volume()
-void dra_mixer_set_volume(dra_mixer* pMixer, float linearVolume);
-
-// dra_voice_get_volume()
-float dra_mixer_get_volume(dra_mixer* pMixer);
-
-// Mixes the next number of frameCount and moves the playback position appropriately.
-//
-// pMixer [in] The mixer.
-// frameCount [in] The number of frames to mix.
-//
-// Returns the number of frames actually mixed.
-//
-// The return value is used to determine whether or not there's anything left to mix in the future. When there are
-// no samples left to mix, the device can be put into a dormant state to prevent unnecessary processing.
-//
-// Mixed samples will be placed in pMixer->pStagingBuffer.
-size_t dra_mixer_mix_next_frames(dra_mixer* pMixer, size_t frameCount);
-
-
-// Non-recursively counts the number of voices that are attached to the given mixer.
-size_t dra_mixer_count_attached_voices(dra_mixer* pMixer);
-
-// Recursively counts the number of voices that are attached to the given mixer.
-size_t dra_mixer_count_attached_voices_recursive(dra_mixer* pMixer);
-
-// Non-recursively gathers all of the voices that are currently attached to the given mixer.
-size_t dra_mixer_gather_attached_voices(dra_mixer* pMixer, dra_voice** ppVoicesOut);
-
-// Recursively gathers all of the voices that are currently attached to the given mixer.
-size_t dra_mixer_gather_attached_voices_recursive(dra_mixer* pMixer, dra_voice** ppVoicesOut);
-
-
-// Marks the given mixer as paused.
-void dra_mixer_pause(dra_mixer* pMixer);
-
-// Unmarks the given mixer as paused.
-void dra_mixer_resume(dra_mixer* pMixer);
-
-// Determines whether or not the given mixer is paused.
-dr_bool32 dra_mixer_is_paused(dra_mixer* pMixer);
-
-
-// dra_voice_create()
-dra_result dra_voice_create(dra_device* pDevice, dra_format format, unsigned int channels, unsigned int sampleRate, size_t sizeInBytes, const void* pInitialData, dra_voice** ppVoice);
-dra_result dra_voice_create_compatible(dra_device* pDevice, size_t sizeInBytes, const void* pInitialData, dra_voice** ppVoice);
-
-// dra_voice_delete()
-void dra_voice_delete(dra_voice* pVoice);
-
-// dra_voice_play()
-//
-// If the mixer the voice is attached to is not playing, the voice will be marked as playing, but won't actually play anything until
-// the mixer is started again.
-void dra_voice_play(dra_voice* pVoice, dr_bool32 loop);
-
-// dra_voice_stop()
-void dra_voice_stop(dra_voice* pVoice);
-
-// dra_voice_is_playing()
-dr_bool32 dra_voice_is_playing(dra_voice* pVoice);
-
-// dra_voice_is_looping()
-dr_bool32 dra_voice_is_looping(dra_voice* pVoice);
-
-
-// dra_voice_set_volume()
-void dra_voice_set_volume(dra_voice* pVoice, float linearVolume);
-
-// dra_voice_get_volume()
-float dra_voice_get_volume(dra_voice* pVoice);
-
-
-void dra_voice_set_on_stop(dra_voice* pVoice, dra_event_proc proc, void* pUserData);
-void dra_voice_set_on_play(dra_voice* pVoice, dra_event_proc proc, void* pUserData);
-dr_bool32 dra_voice_add_playback_event(dra_voice* pVoice, dr_uint64 sampleIndex, dr_uint64 eventID, dra_event_proc proc, void* pUserData);
-void dra_voice_remove_playback_event(dra_voice* pVoice, dr_uint64 eventID);
-
-// dra_voice_get_playback_position()
-dr_uint64 dra_voice_get_playback_position(dra_voice* pVoice);
-
-// dra_voice_set_playback_position()
-void dra_voice_set_playback_position(dra_voice* pVoice, dr_uint64 sampleIndex);
-
-
-// dra_voice_get_buffer_ptr_by_sample()
-void* dra_voice_get_buffer_ptr_by_sample(dra_voice* pVoice, dr_uint64 sample);
-
-// dra_voice_write_silence()
-void dra_voice_write_silence(dra_voice* pVoice, dr_uint64 sampleOffset, dr_uint64 sampleCount);
-
-
-//// Other APIs ////
-
-// Frees memory that was allocated internally by dr_audio.
-void dra_free(void* p);
-
-// Retrieves the number of bits per sample based on the given format.
-unsigned int dra_get_bits_per_sample_by_format(dra_format format);
-
-// Retrieves the number of bytes per sample based on the given format.
-unsigned int dra_get_bytes_per_sample_by_format(dra_format format);
-
-
-//// Decoder APIs ////
-
-typedef enum
-{
- dra_seek_origin_start,
- dra_seek_origin_current
-} dra_seek_origin;
-
-typedef size_t (* dra_decoder_on_read_proc) (void* pUserData, void* pDataOut, size_t bytesToRead);
-typedef dr_bool32 (* dra_decoder_on_seek_proc) (void* pUserData, int offset, dra_seek_origin origin);
-
-typedef void (* dra_decoder_on_delete_proc) (void* pBackendDecoder);
-typedef dr_uint64 (* dra_decoder_on_read_samples_proc) (void* pBackendDecoder, dr_uint64 samplesToRead, float* pSamplesOut);
-typedef dr_bool32 (* dra_decoder_on_seek_samples_proc) (void* pBackendDecoder, dr_uint64 sample);
-
-typedef struct
-{
- const dr_uint8* data;
- size_t dataSize;
- size_t currentReadPos;
-} dra__memory_stream;
-
-typedef struct
-{
- unsigned int channels;
- unsigned int sampleRate;
- dr_uint64 totalSampleCount; // <-- Can be 0.
-
- dra_decoder_on_read_proc onRead;
- dra_decoder_on_seek_proc onSeek;
- void* pUserData;
-
- void* pBackendDecoder;
- dra_decoder_on_delete_proc onDelete;
- dra_decoder_on_read_samples_proc onReadSamples;
- dra_decoder_on_seek_samples_proc onSeekSamples;
-
- // A hack to enable decoding from memory without mallocing the user data.
- dra__memory_stream memoryStream;
-} dra_decoder;
-
-// dra_decoder_open()
-dra_result dra_decoder_open(dra_decoder* pDecoder, dra_decoder_on_read_proc onRead, dra_decoder_on_seek_proc onSeek, void* pUserData);
-float* dra_decoder_open_and_decode_f32(dra_decoder_on_read_proc onRead, dra_decoder_on_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, dr_uint64* totalSampleCount);
-
-dra_result dra_decoder_open_memory(dra_decoder* pDecoder, const void* pData, size_t dataSize);
-float* dra_decoder_open_and_decode_memory_f32(const void* pData, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, dr_uint64* totalSampleCount);
-
-#ifndef DR_AUDIO_NO_STDIO
-dra_result dra_decoder_open_file(dra_decoder* pDecoder, const char* filePath);
-float* dra_decoder_open_and_decode_file_f32(const char* filePath, unsigned int* channels, unsigned int* sampleRate, dr_uint64* totalSampleCount);
-#endif
-
-// dra_decoder_close()
-void dra_decoder_close(dra_decoder* pDecoder);
-
-// dra_decoder_read_f32()
-dr_uint64 dra_decoder_read_f32(dra_decoder* pDecoder, dr_uint64 samplesToRead, float* pSamplesOut);
-
-// dra_decoder_seek_to_sample()
-dr_bool32 dra_decoder_seek_to_sample(dra_decoder* pDecoder, dr_uint64 sample);
-
-
-//// High Level Helper APIs ////
-
-#ifndef DR_AUDIO_NO_STDIO
-// Creates a voice from a file.
-dra_result dra_voice_create_from_file(dra_device* pDevice, const char* filePath, dra_voice** ppVoice);
-#endif
-
-
-//// High Level World API ////
-//
-// This section is for the sound world APIs. These are high-level APIs that sit directly on top of the main API.
-typedef struct dra_sound_world dra_sound_world;
-typedef struct dra_sound dra_sound;
-typedef struct dra_sound_desc dra_sound_desc;
-
-typedef void (* dra_sound_on_delete_proc) (dra_sound* pSound);
-typedef dr_uint64 (* dra_sound_on_read_proc) (dra_sound* pSound, dr_uint64 samplesToRead, void* pSamplesOut);
-typedef dr_bool32 (* dra_sound_on_seek_proc) (dra_sound* pSound, dr_uint64 sample);
-
-struct dra_sound_desc
-{
- // The format of the sound.
- dra_format format;
-
- // The number of channels in the audio data.
- unsigned int channels;
-
- // The sample rate of the audio data.
- unsigned int sampleRate;
-
-
- // The size of the audio data in bytes. If this is 0 it is assumed the data will be streamed.
- size_t dataSize;
-
- // A pointer to the audio data. If this is null it is assumed the audio data is streamed.
- void* pData;
-
-
- // A pointer to the function to call when the sound object is deleted. This is used to give the application an
- // opportunity to do any clean up, such as closing decoders or whatnot.
- dra_sound_on_delete_proc onDelete;
-
- // A pointer to the function to call when dr_audio needs to request a chunk of audio data. This is only used when
- // streaming data.
- dra_sound_on_read_proc onRead;
-
- // A pointer to the function to call when dr_audio needs to seek the audio data. This is only used when streaming
- // data.
- dra_sound_on_seek_proc onSeek;
-
- // A pointer to some application defined user data that can be associated with the sound.
- void* pUserData;
-};
-
-struct dra_sound_world
-{
- // The playback device.
- dra_device* pPlaybackDevice;
-
- // Whether or not the world owns the playback device. When this is set to DR_TRUE, it will be deleted when the world is deleted.
- dr_bool32 ownsPlaybackDevice;
-};
-
-struct dra_sound
-{
- // The world that owns this sound.
- dra_sound_world* pWorld;
-
- // The voice object for emitting audio out of the device.
- dra_voice* pVoice;
-
- // The descriptor of the sound that was used to initialize the sound.
- dra_sound_desc desc;
-
- // Whether or not the sound is looping.
- dr_bool32 isLooping;
-
- // Whether or not the sound should be stopped at the end of the chunk that's currently playing.
- dr_bool32 stopOnNextChunk;
-
- // Application defined user data.
- void* pUserData;
-};
-
-// dra_sound_world_create()
-//
-// The playback device can be null, in which case a default one will be created.
-dra_sound_world* dra_sound_world_create(dra_device* pPlaybackDevice);
-
-// dra_sound_world_delete()
-//
-// This will delete every sound this world owns.
-void dra_sound_world_delete(dra_sound_world* pWorld);
-
-// dra_sound_world_play_inline()
-//
-// pMixer [in, optional] The mixer to attach the sound to. Can null, in which case it's attached to the master mixer.
-void dra_sound_world_play_inline(dra_sound_world* pWorld, dra_sound_desc* pDesc, dra_mixer* pMixer);
-
-// Plays an inlined sound in 3D space.
-//
-// This is a placeholder function. 3D position is not yet implemented.
-void dra_sound_world_play_inline_3f(dra_sound_world* pWorld, dra_sound_desc* pDesc, dra_mixer* pMixer, float xPos, float yPos, float zPos);
-
-// Stops playing every sound.
-//
-// This will stop all voices that are attached to the world's playback deviecs, including those that are not attached to a dra_sound object.
-void dra_sound_world_stop_all_sounds(dra_sound_world* pWorld);
-
-
-// Sets the position of the listener for 3D effects.
-//
-// This is placeholder.
-void dra_sound_world_set_listener_position(dra_sound_world* pWorld, float xPos, float yPos, float zPos);
-
-// Sets the orientation of the listener for 3D effects.
-//
-// This is placeholder.
-void dra_sound_world_set_listener_orientation(dra_sound_world* pWorld, float xForward, float yForward, float zForward, float xUp, float yUp, float zUp);
-
-
-
-
-// dra_sound_create()
-//
-// The datails in "desc" can be accessed from the returned object directly.
-//
-// This uses the pUserData member of the internal voice. Do not overwrite this. Instead, use the pUserData member of
-// the returned dra_sound object.
-dra_sound* dra_sound_create(dra_sound_world* pWorld, dra_sound_desc* pDesc);
-
-#ifndef DR_AUDIO_NO_STDIO
-// dra_sound_create_from_file()
-//
-// This will hold a handle to the file for the life of the sound.
-dra_sound* dra_sound_create_from_file(dra_sound_world* pWorld, const char* filePath);
-#endif
-
-// dra_sound_delete()
-void dra_sound_delete(dra_sound* pSound);
-
-
-// dra_sound_play()
-void dra_sound_play(dra_sound* pSound, dr_bool32 loop);
-
-// dra_sound_stop()
-void dra_sound_stop(dra_sound* pSound);
-
-
-// Attaches the given sound to the given mixer.
-//
-// Setting pMixer to null will detach the sound from the mixer it is currently attached to and attach it
-// to the master mixer.
-void dra_sound_attach_to_mixer(dra_sound* pSound, dra_mixer* pMixer);
-
-
-// dra_sound_set_on_stop()
-void dra_sound_set_on_stop(dra_sound* pSound, dra_event_proc proc, void* pUserData);
-
-// dra_sound_set_on_play()
-void dra_sound_set_on_play(dra_sound* pSound, dra_event_proc proc, void* pUserData);
-
-
-#ifdef __cplusplus
-}
-#endif
-#endif //dr_audio2_h
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// IMPLEMENTATION
-//
-///////////////////////////////////////////////////////////////////////////////
-#ifdef DR_AUDIO_IMPLEMENTATION
-#include
-#include
-#include
-#include // For good old printf debugging. Delete later.
-
-#ifdef _MSC_VER
-#define DR_AUDIO_INLINE static __forceinline
-#else
-#define DR_AUDIO_INLINE static inline
-#endif
-
-#define DR_AUDIO_DEFAULT_CHANNEL_COUNT 2
-#define DR_AUDIO_DEFAULT_SAMPLE_RATE 48000
-#define DR_AUDIO_DEFAULT_LATENCY 100 // Milliseconds. TODO: Test this with very low values. DirectSound appears to not signal the fragment events when it's too small. With values of about 20 it sounds crackly.
-#define DR_AUDIO_DEFAULT_FRAGMENT_COUNT 3 // The hardware buffer is divided up into latency-sized blocks. This controls that number. Must be at least 2.
-
-#define DR_AUDIO_BACKEND_TYPE_NULL 0
-#define DR_AUDIO_BACKEND_TYPE_DSOUND 1
-#define DR_AUDIO_BACKEND_TYPE_ALSA 2
-
-#ifdef dr_wav_h
-#define DR_AUDIO_HAS_WAV
- #ifndef DR_WAV_NO_STDIO
- #define DR_AUDIO_HAS_WAV_STDIO
- #endif
-#endif
-#ifdef dr_flac_h
-#define DR_AUDIO_HAS_FLAC
- #ifndef DR_FLAC_NO_STDIO
- #define DR_AUDIO_HAS_FLAC_STDIO
- #endif
-#endif
-#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
-#define DR_AUDIO_HAS_VORBIS
- #ifndef STB_VORBIS_NO_STDIO
- #define DR_AUDIO_HAS_VORBIS_STDIO
- #endif
-#endif
-
-#if defined(DR_AUDIO_HAS_WAV) || \
- defined(DR_AUDIO_HAS_FLAC) || \
- defined(DR_AUDIO_HAS_VORBIS)
-#define DR_AUDIO_HAS_EXTERNAL_DECODER
-#endif
-
-
-// Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
-DR_AUDIO_INLINE unsigned int dra_next_power_of_2(unsigned int x)
-{
- x--;
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
- x |= x >> 8;
- x |= x >> 16;
- x++;
-
- return x;
-}
-
-DR_AUDIO_INLINE unsigned int dra_prev_power_of_2(unsigned int x)
-{
- return dra_next_power_of_2(x) >> 1;
-}
-
-
-DR_AUDIO_INLINE float dra_mixf(float x, float y, float a)
-{
- return x*(1-a) + y*a;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// Platform Specific
-//
-///////////////////////////////////////////////////////////////////////////////
-
-// Every backend struct should begin with these properties.
-struct dra_backend
-{
-#define DR_AUDIO_BASE_BACKEND_ATTRIBS \
- unsigned int type; \
-
- DR_AUDIO_BASE_BACKEND_ATTRIBS
-};
-
-// Every backend device struct should begin with these properties.
-struct dra_backend_device
-{
-#define DR_AUDIO_BASE_BACKEND_DEVICE_ATTRIBS \
- dra_backend* pBackend; \
- dra_device_type type; \
- unsigned int channels; \
- unsigned int sampleRate; \
- unsigned int fragmentCount; \
- unsigned int samplesPerFragment; \
-
- DR_AUDIO_BASE_BACKEND_DEVICE_ATTRIBS
-};
-
-
-
-
-// Compile-time platform detection and #includes.
-#ifdef _WIN32
-#include
-
-//// Threading (Win32) ////
-typedef DWORD (* dra_thread_entry_proc)(LPVOID pData);
-
-dra_thread dra_thread_create(dra_thread_entry_proc entryProc, void* pData)
-{
- return (dra_thread)CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)entryProc, pData, 0, NULL);
-}
-
-void dra_thread_delete(dra_thread thread)
-{
- CloseHandle((HANDLE)thread);
-}
-
-void dra_thread_wait(dra_thread thread)
-{
- WaitForSingleObject((HANDLE)thread, INFINITE);
-}
-
-
-dra_mutex dra_mutex_create()
-{
- return (dra_mutex)CreateEventA(NULL, FALSE, TRUE, NULL);
-}
-
-void dra_mutex_delete(dra_mutex mutex)
-{
- CloseHandle((HANDLE)mutex);
-}
-
-void dra_mutex_lock(dra_mutex mutex)
-{
- WaitForSingleObject((HANDLE)mutex, INFINITE);
-}
-
-void dra_mutex_unlock(dra_mutex mutex)
-{
- SetEvent((HANDLE)mutex);
-}
-
-
-dra_semaphore dra_semaphore_create(int initialValue)
-{
- return (void*)CreateSemaphoreA(NULL, initialValue, LONG_MAX, NULL);
-}
-
-void dra_semaphore_delete(dra_semaphore semaphore)
-{
- CloseHandle((HANDLE)semaphore);
-}
-
-dr_bool32 dra_semaphore_wait(dra_semaphore semaphore)
-{
- return WaitForSingleObject((HANDLE)semaphore, INFINITE) == WAIT_OBJECT_0;
-}
-
-dr_bool32 dra_semaphore_release(dra_semaphore semaphore)
-{
- return ReleaseSemaphore((HANDLE)semaphore, 1, NULL) != 0;
-}
-
-
-
-//// DirectSound ////
-#ifndef DR_AUDIO_NO_DSOUND
-#define DR_AUDIO_ENABLE_DSOUND
-#include
-#include // WAVEFORMATEX
-
-GUID DR_AUDIO_GUID_NULL = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
-
-static GUID _g_draGUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};
-static GUID _g_draGUID_IID_IDirectSoundCaptureBuffer8 = {0x00990df4, 0x0dbb, 0x4872, {0x83, 0x3e, 0x6d, 0x30, 0x3e, 0x80, 0xae, 0xb6}};
-static GUID _g_draGUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
-
-#ifdef __cplusplus
-static GUID g_draGUID_IID_DirectSoundNotify = _g_draGUID_IID_DirectSoundNotify;
-static GUID g_draGUID_IID_IDirectSoundCaptureBuffer8 = _g_draGUID_IID_IDirectSoundCaptureBuffer8;
-//static GUID g_draGUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = _g_draGUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
-#else
-static GUID* g_draGUID_IID_DirectSoundNotify = &_g_draGUID_IID_DirectSoundNotify;
-static GUID* g_draGUID_IID_IDirectSoundCaptureBuffer8 = &_g_draGUID_IID_IDirectSoundCaptureBuffer8;
-//static GUID* g_draGUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = &_g_draGUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
-#endif
-
-typedef HRESULT (WINAPI * pDirectSoundCreate8Proc)(LPCGUID pcGuidDevice, LPDIRECTSOUND8 *ppDS8, LPUNKNOWN pUnkOuter);
-typedef HRESULT (WINAPI * pDirectSoundEnumerateAProc)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext);
-typedef HRESULT (WINAPI * pDirectSoundCaptureCreate8Proc)(LPCGUID pcGuidDevice, LPDIRECTSOUNDCAPTURE8 *ppDSC8, LPUNKNOWN pUnkOuter);
-typedef HRESULT (WINAPI * pDirectSoundCaptureEnumerateAProc)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext);
-
-typedef struct
-{
- DR_AUDIO_BASE_BACKEND_ATTRIBS
-
- // A handle to the dsound DLL for doing run-time linking.
- HMODULE hDSoundDLL;
-
- pDirectSoundCreate8Proc pDirectSoundCreate8;
- pDirectSoundEnumerateAProc pDirectSoundEnumerateA;
- pDirectSoundCaptureCreate8Proc pDirectSoundCaptureCreate8;
- pDirectSoundCaptureEnumerateAProc pDirectSoundCaptureEnumerateA;
-} dra_backend_dsound;
-
-typedef struct
-{
- DR_AUDIO_BASE_BACKEND_DEVICE_ATTRIBS
-
-
- // The main device object for use with DirectSound.
- LPDIRECTSOUND8 pDS;
-
- // The DirectSound "primary buffer". It's basically just representing the connection between us and the hardware device.
- LPDIRECTSOUNDBUFFER pDSPrimaryBuffer;
-
- // The DirectSound "secondary buffer". This is where the actual audio data will be written to by dr_audio when it's time
- // to play back some audio through the speakers. This represents the hardware buffer.
- LPDIRECTSOUNDBUFFER pDSSecondaryBuffer;
-
-
- // The main capture device object for use with DirectSound. This is only used by capture devices and is created by DirectSoundCaptureCreate8().
- LPDIRECTSOUNDCAPTURE8 pDSCapture;
-
- // The capture buffer. This is where captured audio data will be placed. This is only used by capture devices.
- LPDIRECTSOUNDCAPTUREBUFFER8 pDSCaptureBuffer;
-
-
- // The notification object used by DirectSound to notify dr_audio that it's ready for the next fragment of audio data.
- LPDIRECTSOUNDNOTIFY pDSNotify;
-
- // Notification events for each fragment.
- HANDLE pNotifyEvents[DR_AUDIO_DEFAULT_FRAGMENT_COUNT];
-
- // The event the main playback thread will wait on to determine whether or not the playback loop should terminate.
- HANDLE hStopEvent;
-
- // The index of the fragment that is currently being played.
- unsigned int currentFragmentIndex;
-
- // The address of the mapped fragment. This is set with IDirectSoundBuffer8::Lock() and passed to IDriectSoundBuffer8::Unlock().
- void* pLockPtr;
-
- // The size of the locked buffer. This is set with IDirectSoundBuffer8::Lock() and passed to IDriectSoundBuffer8::Unlock().
- DWORD lockSize;
-
-} dra_backend_device_dsound;
-
-typedef struct
-{
- unsigned int deviceID;
- unsigned int counter;
- const GUID* pGuid;
-} dra_dsound__device_enum_data;
-
-static BOOL CALLBACK dra_dsound__get_device_guid_by_id__callback(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
-{
- (void)lpcstrDescription;
- (void)lpcstrModule;
-
- dra_dsound__device_enum_data* pData = (dra_dsound__device_enum_data*)lpContext;
- assert(pData != NULL);
-
- if (pData->counter == pData->deviceID) {
- pData->pGuid = lpGuid;
- return DR_FALSE;
- }
-
- pData->counter += 1;
- return DR_TRUE;
-}
-
-const GUID* dra_dsound__get_playback_device_guid_by_id(dra_backend* pBackend, unsigned int deviceID)
-{
- // From MSDN:
- //
- // The first device enumerated is always called the Primary Sound Driver, and the lpGUID parameter of the callback is
- // NULL. This device represents the preferred output device set by the user in Control Panel.
- if (deviceID == 0) {
- return NULL;
- }
-
- dra_backend_dsound* pBackendDS = (dra_backend_dsound*)pBackend;
- if (pBackendDS == NULL) {
- return NULL;
- }
-
- // The device ID is treated as the device index. The actual ID for use by DirectSound is a GUID. We use DirectSoundEnumerateA()
- // iterate over each device. This function is usually only going to be used during initialization time so it won't be a performance
- // issue to not cache these.
- dra_dsound__device_enum_data data = {0};
- data.deviceID = deviceID;
- pBackendDS->pDirectSoundEnumerateA(dra_dsound__get_device_guid_by_id__callback, &data);
-
- return data.pGuid;
-}
-
-const GUID* dra_dsound__get_capture_device_guid_by_id(dra_backend* pBackend, unsigned int deviceID)
-{
- // From MSDN:
- //
- // The first device enumerated is always called the Primary Sound Driver, and the lpGUID parameter of the callback is
- // NULL. This device represents the preferred output device set by the user in Control Panel.
- if (deviceID == 0) {
- return NULL;
- }
-
- dra_backend_dsound* pBackendDS = (dra_backend_dsound*)pBackend;
- if (pBackendDS == NULL) {
- return NULL;
- }
-
- // The device ID is treated as the device index. The actual ID for use by DirectSound is a GUID. We use DirectSoundEnumerateA()
- // iterate over each device. This function is usually only going to be used during initialization time so it won't be a performance
- // issue to not cache these.
- dra_dsound__device_enum_data data = {0};
- data.deviceID = deviceID;
- pBackendDS->pDirectSoundCaptureEnumerateA(dra_dsound__get_device_guid_by_id__callback, &data);
-
- return data.pGuid;
-}
-
-dra_backend* dra_backend_create_dsound()
-{
- dra_backend_dsound* pBackendDS = (dra_backend_dsound*)calloc(1, sizeof(*pBackendDS)); // <-- Note the calloc() - makes it easier to handle the on_error goto.
- if (pBackendDS == NULL) {
- return NULL;
- }
-
- pBackendDS->type = DR_AUDIO_BACKEND_TYPE_DSOUND;
-
- pBackendDS->hDSoundDLL = LoadLibraryW(L"dsound.dll");
- if (pBackendDS->hDSoundDLL == NULL) {
- goto on_error;
- }
-
- pBackendDS->pDirectSoundCreate8 = (pDirectSoundCreate8Proc)GetProcAddress(pBackendDS->hDSoundDLL, "DirectSoundCreate8");
- if (pBackendDS->pDirectSoundCreate8 == NULL){
- goto on_error;
- }
-
- pBackendDS->pDirectSoundEnumerateA = (pDirectSoundEnumerateAProc)GetProcAddress(pBackendDS->hDSoundDLL, "DirectSoundEnumerateA");
- if (pBackendDS->pDirectSoundEnumerateA == NULL){
- goto on_error;
- }
-
- pBackendDS->pDirectSoundCaptureCreate8 = (pDirectSoundCaptureCreate8Proc)GetProcAddress(pBackendDS->hDSoundDLL, "DirectSoundCaptureCreate8");
- if (pBackendDS->pDirectSoundCaptureCreate8 == NULL){
- goto on_error;
- }
-
- pBackendDS->pDirectSoundCaptureEnumerateA = (pDirectSoundCaptureEnumerateAProc)GetProcAddress(pBackendDS->hDSoundDLL, "DirectSoundCaptureEnumerateA");
- if (pBackendDS->pDirectSoundCaptureEnumerateA == NULL){
- goto on_error;
- }
-
-
- return (dra_backend*)pBackendDS;
-
-on_error:
- if (pBackendDS != NULL) {
- if (pBackendDS->hDSoundDLL != NULL) {
- FreeLibrary(pBackendDS->hDSoundDLL);
- }
-
- free(pBackendDS);
- }
-
- return NULL;
-}
-
-void dra_backend_delete_dsound(dra_backend* pBackend)
-{
- dra_backend_dsound* pBackendDS = (dra_backend_dsound*)pBackend;
- if (pBackendDS == NULL) {
- return;
- }
-
- if (pBackendDS->hDSoundDLL != NULL) {
- FreeLibrary(pBackendDS->hDSoundDLL);
- }
-
- free(pBackendDS);
-}
-
-void dra_backend_device_close_dsound(dra_backend_device* pDevice)
-{
- dra_backend_device_dsound* pDeviceDS = (dra_backend_device_dsound*)pDevice;
- if (pDeviceDS == NULL) {
- return;
- }
-
- if (pDeviceDS->pDSNotify) IDirectSoundNotify_Release(pDeviceDS->pDSNotify);
-
- if (pDevice->type == dra_device_type_playback) {
- if (pDeviceDS->pDSSecondaryBuffer) IDirectSoundBuffer_Release(pDeviceDS->pDSSecondaryBuffer);
- if (pDeviceDS->pDSPrimaryBuffer) IDirectSoundBuffer_Release(pDeviceDS->pDSPrimaryBuffer);
- if (pDeviceDS->pDS) IDirectSound_Release(pDeviceDS->pDS);
- } else {
- if (pDeviceDS->pDSCaptureBuffer) IDirectSoundCaptureBuffer_Release(pDeviceDS->pDSCaptureBuffer);
- if (pDeviceDS->pDSCapture) IDirectSoundCapture_Release(pDeviceDS->pDSCapture);
- }
-
-
- for (int i = 0; i < DR_AUDIO_DEFAULT_FRAGMENT_COUNT; ++i) {
- CloseHandle(pDeviceDS->pNotifyEvents[i]);
- }
-
- if (pDeviceDS->hStopEvent != NULL) {
- CloseHandle(pDeviceDS->hStopEvent);
- }
-
- free(pDeviceDS);
-}
-
-dra_backend_device* dra_backend_device_open_playback_dsound(dra_backend* pBackend, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds)
-{
- // These are declared at the top to stop compilations errors on GCC about goto statements skipping over variable initialization.
- HRESULT hr;
- WAVEFORMATEXTENSIBLE* actualFormat;
- unsigned int sampleRateInMilliseconds;
- unsigned int proposedFramesPerFragment;
- unsigned int framesPerFragment;
- size_t fragmentSize;
- size_t hardwareBufferSize;
-
- dra_backend_dsound* pBackendDS = (dra_backend_dsound*)pBackend;
- if (pBackendDS == NULL) {
- return NULL;
- }
-
- dra_backend_device_dsound* pDeviceDS = (dra_backend_device_dsound*)calloc(1, sizeof(*pDeviceDS));
- if (pDeviceDS == NULL) {
- goto on_error;
- }
-
- if (channels == 0) {
- channels = DR_AUDIO_DEFAULT_CHANNEL_COUNT;
- }
-
- pDeviceDS->pBackend = pBackend;
- pDeviceDS->type = dra_device_type_playback;
- pDeviceDS->channels = channels;
- pDeviceDS->sampleRate = sampleRate;
-
- hr = pBackendDS->pDirectSoundCreate8(dra_dsound__get_playback_device_guid_by_id(pBackend, deviceID), &pDeviceDS->pDS, NULL);
- if (FAILED(hr)) {
- goto on_error;
- }
-
- // The cooperative level must be set before doing anything else.
- hr = IDirectSound_SetCooperativeLevel(pDeviceDS->pDS, GetForegroundWindow(), DSSCL_PRIORITY);
- if (FAILED(hr)) {
- goto on_error;
- }
-
-
- // The primary buffer is basically just the connection to the hardware.
- DSBUFFERDESC descDSPrimary;
- memset(&descDSPrimary, 0, sizeof(DSBUFFERDESC));
- descDSPrimary.dwSize = sizeof(DSBUFFERDESC);
- descDSPrimary.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
-
- hr = IDirectSound_CreateSoundBuffer(pDeviceDS->pDS, &descDSPrimary, &pDeviceDS->pDSPrimaryBuffer, NULL);
- if (FAILED(hr)) {
- goto on_error;
- }
-
-
- // If the channel count is 0 then we need to use the default. From MSDN:
- //
- // The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest
- // supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer
- // and compare the result with the format that was requested with the SetFormat method.
- WAVEFORMATEXTENSIBLE wf;
- memset(&wf, 0, sizeof(wf));
- wf.Format.cbSize = sizeof(wf);
- wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- wf.Format.nChannels = (WORD)channels;
- wf.Format.nSamplesPerSec = (DWORD)sampleRate;
- wf.Format.wBitsPerSample = sizeof(float)*8;
- wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
- wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
- wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample;
- wf.dwChannelMask = 0;
- wf.SubFormat = _g_draGUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- if (channels > 2) {
- wf.dwChannelMask = ~(((DWORD)-1) << channels);
- }
-
- hr = IDirectSoundBuffer_SetFormat(pDeviceDS->pDSPrimaryBuffer, (WAVEFORMATEX*)&wf);
- if (FAILED(hr)) {
- goto on_error;
- }
-
-
- // Get the ACTUAL properties of the buffer. This is silly API design...
- DWORD requiredSize;
- hr = IDirectSoundBuffer_GetFormat(pDeviceDS->pDSPrimaryBuffer, NULL, 0, &requiredSize);
- if (FAILED(hr)) {
- goto on_error;
- }
-
- char rawdata[1024];
- actualFormat = (WAVEFORMATEXTENSIBLE*)rawdata;
- hr = IDirectSoundBuffer_GetFormat(pDeviceDS->pDSPrimaryBuffer, (WAVEFORMATEX*)actualFormat, requiredSize, NULL);
- if (FAILED(hr)) {
- goto on_error;
- }
-
- pDeviceDS->channels = actualFormat->Format.nChannels;
- pDeviceDS->sampleRate = actualFormat->Format.nSamplesPerSec;
-
- // DirectSound always has the same number of fragments.
- pDeviceDS->fragmentCount = DR_AUDIO_DEFAULT_FRAGMENT_COUNT;
-
-
- // The secondary buffer is the buffer where the real audio data will be written to and used by the hardware device. It's
- // size is based on the latency, sample rate and channels.
- //
- // The format of the secondary buffer should exactly match the primary buffer as to avoid unnecessary data conversions.
- sampleRateInMilliseconds = pDeviceDS->sampleRate / 1000;
- if (sampleRateInMilliseconds == 0) {
- sampleRateInMilliseconds = 1;
- }
-
- // The size of a fragment is sized such that the number of frames contained within it is a multiple of 2. The reason for
- // this is to keep it consistent with the ALSA backend.
- proposedFramesPerFragment = sampleRateInMilliseconds * latencyInMilliseconds;
- framesPerFragment = dra_prev_power_of_2(proposedFramesPerFragment);
- if (framesPerFragment == 0) {
- framesPerFragment = 2;
- }
-
- pDeviceDS->samplesPerFragment = framesPerFragment * pDeviceDS->channels;
-
- fragmentSize = pDeviceDS->samplesPerFragment * sizeof(float);
- hardwareBufferSize = fragmentSize * pDeviceDS->fragmentCount;
- assert(hardwareBufferSize > 0); // <-- If you've triggered this is means you've got something set to 0. You haven't been setting that latency to 0 have you?! That's not allowed!
-
- // Meaning of dwFlags (from MSDN):
- //
- // DSBCAPS_CTRLPOSITIONNOTIFY
- // The buffer has position notification capability.
- //
- // DSBCAPS_GLOBALFOCUS
- // With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to
- // another application, even if the new application uses DirectSound.
- //
- // DSBCAPS_GETCURRENTPOSITION2
- // In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated
- // sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the
- // application can get a more accurate play cursor.
- DSBUFFERDESC descDS;
- memset(&descDS, 0, sizeof(DSBUFFERDESC));
- descDS.dwSize = sizeof(DSBUFFERDESC);
- descDS.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
- descDS.dwBufferBytes = (DWORD)hardwareBufferSize;
- descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
- hr = IDirectSound_CreateSoundBuffer(pDeviceDS->pDS, &descDS, &pDeviceDS->pDSSecondaryBuffer, NULL);
- if (FAILED(hr)) {
- goto on_error;
- }
-
-
- // As DirectSound is playing back the hardware buffer it needs to notify dr_audio when it's ready for new data. This is done
- // through a notification object which we retrieve from the secondary buffer.
- hr = IDirectSoundBuffer8_QueryInterface(pDeviceDS->pDSSecondaryBuffer, g_draGUID_IID_DirectSoundNotify, (void**)&pDeviceDS->pDSNotify);
- if (FAILED(hr)) {
- goto on_error;
- }
-
- DSBPOSITIONNOTIFY notifyPoints[DR_AUDIO_DEFAULT_FRAGMENT_COUNT]; // One notification event for each fragment.
- for (int i = 0; i < DR_AUDIO_DEFAULT_FRAGMENT_COUNT; ++i)
- {
- pDeviceDS->pNotifyEvents[i] = CreateEventA(NULL, FALSE, FALSE, NULL);
- if (pDeviceDS->pNotifyEvents[i] == NULL) {
- goto on_error;
- }
-
- notifyPoints[i].dwOffset = (DWORD)(i * fragmentSize); // <-- This is in bytes.
- notifyPoints[i].hEventNotify = pDeviceDS->pNotifyEvents[i];
- }
-
- hr = IDirectSoundNotify_SetNotificationPositions(pDeviceDS->pDSNotify, DR_AUDIO_DEFAULT_FRAGMENT_COUNT, notifyPoints);
- if (FAILED(hr)) {
- goto on_error;
- }
-
-
-
- // The termination event is used to determine when the playback thread should be terminated. The playback thread
- // will wait on this event in addition to the notification events in it's main loop.
- pDeviceDS->hStopEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
- if (pDeviceDS->hStopEvent == NULL) {
- goto on_error;
- }
-
- return (dra_backend_device*)pDeviceDS;
-
-on_error:
- dra_backend_device_close_dsound((dra_backend_device*)pDeviceDS);
- return NULL;
-}
-
-dra_backend_device* dra_backend_device_open_capture_dsound(dra_backend* pBackend, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds)
-{
- (void)latencyInMilliseconds;
-
- HRESULT hr;
- unsigned int sampleRateInMilliseconds;
- unsigned int proposedFramesPerFragment;
- unsigned int framesPerFragment;
- size_t fragmentSize;
- size_t hardwareBufferSize;
-
- dra_backend_dsound* pBackendDS = (dra_backend_dsound*)pBackend;
- if (pBackendDS == NULL) {
- return NULL;
- }
-
- dra_backend_device_dsound* pDeviceDS = (dra_backend_device_dsound*)calloc(1, sizeof(*pDeviceDS));
- if (pDeviceDS == NULL) {
- goto on_error;
- }
-
- if (channels == 0) {
- channels = DR_AUDIO_DEFAULT_CHANNEL_COUNT;
- }
-
- pDeviceDS->pBackend = pBackend;
- pDeviceDS->type = dra_device_type_capture;
- pDeviceDS->channels = channels;
- pDeviceDS->sampleRate = sampleRate;
-
- hr = pBackendDS->pDirectSoundCaptureCreate8(dra_dsound__get_capture_device_guid_by_id(pBackend, deviceID), &pDeviceDS->pDSCapture, NULL);
- if (FAILED(hr)) {
- goto on_error;
- }
-
- pDeviceDS->fragmentCount = DR_AUDIO_DEFAULT_FRAGMENT_COUNT;
-
- // The secondary buffer is the buffer where the real audio data will be written to and used by the hardware device. It's
- // size is based on the latency, sample rate and channels.
- //
- // The format of the secondary buffer should exactly match the primary buffer as to avoid unnecessary data conversions.
- sampleRateInMilliseconds = pDeviceDS->sampleRate / 1000;
- if (sampleRateInMilliseconds == 0) {
- sampleRateInMilliseconds = 1;
- }
-
- // The size of a fragment is sized such that the number of frames contained within it is a multiple of 2. The reason for
- // this is to keep it consistent with the ALSA backend.
- proposedFramesPerFragment = sampleRateInMilliseconds * latencyInMilliseconds;
- framesPerFragment = dra_prev_power_of_2(proposedFramesPerFragment);
- if (framesPerFragment == 0) {
- framesPerFragment = 2;
- }
-
- pDeviceDS->samplesPerFragment = framesPerFragment * pDeviceDS->channels;
-
- fragmentSize = pDeviceDS->samplesPerFragment * sizeof(float);
- hardwareBufferSize = fragmentSize * pDeviceDS->fragmentCount;
- assert(hardwareBufferSize > 0); // <-- If you've triggered this is means you've got something set to 0. You haven't been setting that latency to 0 have you?! That's not allowed!
-
-
- WAVEFORMATEXTENSIBLE wf;
- memset(&wf, 0, sizeof(wf));
- wf.Format.cbSize = sizeof(wf);
- wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- wf.Format.nChannels = (WORD)channels;
- wf.Format.nSamplesPerSec = (DWORD)sampleRate;
- wf.Format.wBitsPerSample = sizeof(float)*8;
- wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
- wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
- wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample;
- wf.dwChannelMask = 0;
- wf.SubFormat = _g_draGUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- if (channels > 2) {
- wf.dwChannelMask = ~(((DWORD)-1) << channels);
- }
-
- DSCBUFFERDESC descDS;
- memset(&descDS, 0, sizeof(descDS));
- descDS.dwSize = sizeof(descDS);
- descDS.dwFlags = 0;
- descDS.dwBufferBytes = (DWORD)hardwareBufferSize;
- descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
-
- LPDIRECTSOUNDCAPTUREBUFFER pDSCB_Temp;
- hr = IDirectSoundCapture_CreateCaptureBuffer(pDeviceDS->pDSCapture, &descDS, &pDSCB_Temp, NULL);
- if (FAILED(hr)) {
- goto on_error;
- }
-
- hr = IDirectSoundCapture_QueryInterface(pDSCB_Temp, g_draGUID_IID_IDirectSoundCaptureBuffer8, (LPVOID*)&pDeviceDS->pDSCaptureBuffer);
- IDirectSoundCaptureBuffer_Release(pDSCB_Temp);
- if (FAILED(hr)) {
- goto on_error; // Failed to retrieve the DirectSoundCaptureBuffer8 interface.
- }
-
-
- // As DirectSound is playing back the hardware buffer it needs to notify dr_audio when it's ready for new data. This is done
- // through a notification object which we retrieve from the secondary buffer.
- hr = IDirectSoundCaptureBuffer8_QueryInterface(pDeviceDS->pDSCaptureBuffer, g_draGUID_IID_DirectSoundNotify, (void**)&pDeviceDS->pDSNotify);
- if (FAILED(hr)) {
- goto on_error;
- }
-
- DSBPOSITIONNOTIFY notifyPoints[DR_AUDIO_DEFAULT_FRAGMENT_COUNT]; // One notification event for each fragment.
- for (int i = 0; i < DR_AUDIO_DEFAULT_FRAGMENT_COUNT; ++i)
- {
- pDeviceDS->pNotifyEvents[i] = CreateEventA(NULL, FALSE, FALSE, NULL);
- if (pDeviceDS->pNotifyEvents[i] == NULL) {
- goto on_error;
- }
-
- notifyPoints[i].dwOffset = (DWORD)(((i+1) * fragmentSize) % hardwareBufferSize); // <-- This is in bytes.
- notifyPoints[i].hEventNotify = pDeviceDS->pNotifyEvents[i];
- }
-
- hr = IDirectSoundNotify_SetNotificationPositions(pDeviceDS->pDSNotify, DR_AUDIO_DEFAULT_FRAGMENT_COUNT, notifyPoints);
- if (FAILED(hr)) {
- goto on_error;
- }
-
-
- // The termination event is used to determine when the capture thread should be terminated. This thread
- // will wait on this event in addition to the notification events in it's main loop.
- pDeviceDS->hStopEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
- if (pDeviceDS->hStopEvent == NULL) {
- goto on_error;
- }
-
- return (dra_backend_device*)pDeviceDS;
-
-on_error:
- dra_backend_device_close_dsound((dra_backend_device*)pDeviceDS);
- return NULL;
-}
-
-dra_backend_device* dra_backend_device_open_dsound(dra_backend* pBackend, dra_device_type type, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds)
-{
- if (type == dra_device_type_playback) {
- return dra_backend_device_open_playback_dsound(pBackend, deviceID, channels, sampleRate, latencyInMilliseconds);
- } else {
- return dra_backend_device_open_capture_dsound(pBackend, deviceID, channels, sampleRate, latencyInMilliseconds);
- }
-}
-
-void dra_backend_device_play(dra_backend_device* pDevice)
-{
- dra_backend_device_dsound* pDeviceDS = (dra_backend_device_dsound*)pDevice;
- if (pDeviceDS == NULL) {
- return;
- }
-
- if (pDevice->type == dra_device_type_playback) {
- IDirectSoundBuffer_Play(pDeviceDS->pDSSecondaryBuffer, 0, 0, DSBPLAY_LOOPING);
- } else {
- IDirectSoundCaptureBuffer8_Start(pDeviceDS->pDSCaptureBuffer, DSCBSTART_LOOPING);
- }
-}
-
-void dra_backend_device_stop(dra_backend_device* pDevice)
-{
- dra_backend_device_dsound* pDeviceDS = (dra_backend_device_dsound*)pDevice;
- if (pDeviceDS == NULL) {
- return;
- }
-
- if (pDevice->type == dra_device_type_playback) {
- // Don't do anything if the buffer is not already playing.
- DWORD status;
- IDirectSoundBuffer_GetStatus(pDeviceDS->pDSSecondaryBuffer, &status);
- if ((status & DSBSTATUS_PLAYING) == 0) {
- return; // The buffer is already stopped.
- }
-
- // Stop the playback straight away to ensure output to the hardware device is stopped as soon as possible.
- IDirectSoundBuffer_Stop(pDeviceDS->pDSSecondaryBuffer);
- IDirectSoundBuffer_SetCurrentPosition(pDeviceDS->pDSSecondaryBuffer, 0);
- } else {
- // Don't do anything if the buffer is not already playing.
- DWORD status;
- IDirectSoundCaptureBuffer8_GetStatus(pDeviceDS->pDSCaptureBuffer, &status);
- if ((status & DSBSTATUS_PLAYING) == 0) {
- return; // The buffer is already stopped.
- }
-
- // Stop capture straight away to ensure output to the hardware device is stopped as soon as possible.
- //IDirectSoundCaptureBuffer8_Stop(pDeviceDS->pDSCaptureBuffer); // <-- There's actually a typo in my version of dsound.h which trigger's a compilation error here. The call below is safe, albeit slightly less intuitive.
- IDirectSoundCaptureBuffer_Stop(pDeviceDS->pDSCaptureBuffer);
- }
-
- // Now we just need to make dra_backend_device_play() return which in the case of DirectSound we do by
- // simply signaling the stop event.
- SetEvent(pDeviceDS->hStopEvent);
-}
-
-dr_bool32 dra_backend_device_wait(dra_backend_device* pDevice) // <-- Returns DR_TRUE if the function has returned because it needs more data; DR_FALSE if the device has been stopped or an error has occured.
-{
- dra_backend_device_dsound* pDeviceDS = (dra_backend_device_dsound*)pDevice;
- if (pDeviceDS == NULL) {
- return DR_FALSE;
- }
-
- unsigned int eventCount = DR_AUDIO_DEFAULT_FRAGMENT_COUNT + 1;
- HANDLE eventHandles[DR_AUDIO_DEFAULT_FRAGMENT_COUNT + 1]; // +1 for the stop event.
- memcpy(eventHandles, pDeviceDS->pNotifyEvents, sizeof(HANDLE) * DR_AUDIO_DEFAULT_FRAGMENT_COUNT);
- eventHandles[DR_AUDIO_DEFAULT_FRAGMENT_COUNT] = pDeviceDS->hStopEvent;
-
- DWORD rc = WaitForMultipleObjects(DR_AUDIO_DEFAULT_FRAGMENT_COUNT + 1, eventHandles, FALSE, INFINITE);
- if (rc >= WAIT_OBJECT_0 && rc < eventCount)
- {
- unsigned int eventIndex = rc - WAIT_OBJECT_0;
- HANDLE hEvent = eventHandles[eventIndex];
-
- // Has the device been stopped? If so, need to return DR_FALSE.
- if (hEvent == pDeviceDS->hStopEvent) {
- return DR_FALSE;
- }
-
- // If we get here it means the event that's been signaled represents a fragment.
- pDeviceDS->currentFragmentIndex = eventIndex;
- return DR_TRUE;
- }
-
- return DR_FALSE;
-}
-
-void* dra_backend_device_map_next_fragment(dra_backend_device* pDevice, size_t* pSamplesInFragmentOut)
-{
- assert(pSamplesInFragmentOut != NULL);
-
- dra_backend_device_dsound* pDeviceDS = (dra_backend_device_dsound*)pDevice;
- if (pDeviceDS == NULL) {
- return NULL;
- }
-
- if (pDeviceDS->pLockPtr != NULL) {
- return NULL; // A fragment is already mapped. Can only have a single fragment mapped at a time.
- }
-
- if (pDevice->type == dra_device_type_playback) {
- // If the device is not currently playing, we just return the first fragment. Otherwise we return the fragment that's sitting just past the
- // one that's currently playing.
- DWORD dwOffset = 0;
- DWORD dwBytes = pDeviceDS->samplesPerFragment * sizeof(float);
-
- DWORD status;
- IDirectSoundBuffer_GetStatus(pDeviceDS->pDSSecondaryBuffer, &status);
- if ((status & DSBSTATUS_PLAYING) != 0) {
- dwOffset = (((pDeviceDS->currentFragmentIndex + 1) % pDeviceDS->fragmentCount) * pDeviceDS->samplesPerFragment) * sizeof(float);
- }
-
- HRESULT hr = IDirectSoundBuffer_Lock(pDeviceDS->pDSSecondaryBuffer, dwOffset, dwBytes, &pDeviceDS->pLockPtr, &pDeviceDS->lockSize, NULL, NULL, 0);
- if (FAILED(hr)) {
- return NULL;
- }
- } else {
- DWORD dwOffset = (pDeviceDS->currentFragmentIndex * pDeviceDS->samplesPerFragment) * sizeof(float);
- DWORD dwBytes = pDeviceDS->samplesPerFragment * sizeof(float);
-
- HRESULT hr = IDirectSoundCaptureBuffer8_Lock(pDeviceDS->pDSCaptureBuffer, dwOffset, dwBytes, &pDeviceDS->pLockPtr, &pDeviceDS->lockSize, NULL, NULL, 0);
- if (FAILED(hr)) {
- return NULL;
- }
- }
-
- *pSamplesInFragmentOut = pDeviceDS->samplesPerFragment;
- return pDeviceDS->pLockPtr;
-}
-
-void dra_backend_device_unmap_next_fragment(dra_backend_device* pDevice)
-{
- dra_backend_device_dsound* pDeviceDS = (dra_backend_device_dsound*)pDevice;
- if (pDeviceDS == NULL) {
- return;
- }
-
- if (pDeviceDS->pLockPtr == NULL) {
- return; // Nothing is mapped.
- }
-
- if (pDevice->type == dra_device_type_playback) {
- IDirectSoundBuffer_Unlock(pDeviceDS->pDSSecondaryBuffer, pDeviceDS->pLockPtr, pDeviceDS->lockSize, NULL, 0);
- } else {
- IDirectSoundCaptureBuffer8_Unlock(pDeviceDS->pDSCaptureBuffer, pDeviceDS->pLockPtr, pDeviceDS->lockSize, NULL, 0);
- }
-
- pDeviceDS->pLockPtr = NULL;
- pDeviceDS->lockSize = 0;
-}
-#endif // DR_AUDIO_NO_SOUND
-#endif // _WIN32
-
-#ifdef __linux__
-#include
-#include
-#include
-#include
-#include
-#include
-
-//// Threading (POSIX) ////
-typedef void* (* dra_thread_entry_proc)(void* pData);
-
-dra_thread dra_thread_create(dra_thread_entry_proc entryProc, void* pData)
-{
- pthread_t thread;
- if (pthread_create(&thread, NULL, entryProc, pData) != 0) {
- return NULL;
- }
-
- return (dra_thread)thread;
-}
-
-void dra_thread_delete(dra_thread thread)
-{
- (void)thread;
-}
-
-void dra_thread_wait(dra_thread thread)
-{
- pthread_join((pthread_t)thread, NULL);
-}
-
-
-dra_mutex dra_mutex_create()
-{
- // The pthread_mutex_t object is not a void* castable handle type. Just create it on the heap and be done with it.
- pthread_mutex_t* mutex = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
- if (pthread_mutex_init(mutex, NULL) != 0) {
- free(mutex);
- mutex = NULL;
- }
-
- return mutex;
-}
-
-void dra_mutex_delete(dra_mutex mutex)
-{
- pthread_mutex_destroy((pthread_mutex_t*)mutex);
-}
-
-void dra_mutex_lock(dra_mutex mutex)
-{
- pthread_mutex_lock((pthread_mutex_t*)mutex);
-}
-
-void dra_mutex_unlock(dra_mutex mutex)
-{
- pthread_mutex_unlock((pthread_mutex_t*)mutex);
-}
-
-
-dra_semaphore dra_semaphore_create(int initialValue)
-{
- sem_t* semaphore = (sem_t*)malloc(sizeof(sem_t));
- if (sem_init(semaphore, 0, (unsigned int)initialValue) == -1) {
- free(semaphore);
- semaphore = NULL;
- }
-
- return (dra_semaphore)semaphore;
-}
-
-void dra_semaphore_delete(dra_semaphore semaphore)
-{
- sem_close((sem_t*)semaphore);
-}
-
-dr_bool32 dra_semaphore_wait(dra_semaphore semaphore)
-{
- return sem_wait((sem_t*)semaphore) != -1;
-}
-
-dr_bool32 dra_semaphore_release(dra_semaphore semaphore)
-{
- return sem_post((sem_t*)semaphore) != -1;
-}
-
-
-//// ALSA ////
-
-#ifndef DR_AUDIO_NO_ALSA
-#define DR_AUDIO_ENABLE_ALSA
-#include
-
-typedef struct
-{
- DR_AUDIO_BASE_BACKEND_ATTRIBS
-
- int unused;
-} dra_backend_alsa;
-
-typedef struct
-{
- DR_AUDIO_BASE_BACKEND_DEVICE_ATTRIBS
-
- // The ALSA device handle.
- snd_pcm_t* deviceALSA;
-
- // Whether or not the device is currently playing.
- dr_bool32 isPlaying;
-
- // Whether or not the intermediary buffer is mapped.
- dr_bool32 isBufferMapped;
-
- // The intermediary buffer where audio data is written before being submitted to the device.
- float* pIntermediaryBuffer;
-} dra_backend_device_alsa;
-
-
-static dr_bool32 dra_alsa__get_device_name_by_id(dra_backend* pBackend, unsigned int deviceID, char* deviceNameOut)
-{
- assert(pBackend != NULL);
- assert(deviceNameOut != NULL);
-
- deviceNameOut[0] = '\0'; // Safety.
-
- if (deviceID == 0) {
- strcpy(deviceNameOut, "default");
- return DR_TRUE;
- }
-
-
- unsigned int iDevice = 0;
-
- char** deviceHints;
- if (snd_device_name_hint(-1, "pcm", (void***)&deviceHints) < 0) {
- //printf("Failed to iterate devices.");
- return -1;
- }
-
- char** nextDeviceHint = deviceHints;
- while (*nextDeviceHint != NULL && iDevice < deviceID) {
- nextDeviceHint += 1;
- iDevice += 1;
- }
-
- dr_bool32 result = DR_FALSE;
- if (iDevice == deviceID) {
- strcpy(deviceNameOut, snd_device_name_get_hint(*nextDeviceHint, "NAME"));
- result = DR_TRUE;
- }
-
- snd_device_name_free_hint((void**)deviceHints);
-
-
- return result;
-}
-
-
-dra_backend* dra_backend_create_alsa()
-{
- dra_backend_alsa* pBackendALSA = (dra_backend_alsa*)calloc(1, sizeof(*pBackendALSA)); // <-- Note the calloc() - makes it easier to handle the on_error goto.
- if (pBackendALSA == NULL) {
- return NULL;
- }
-
- pBackendALSA->type = DR_AUDIO_BACKEND_TYPE_ALSA;
-
-
- return (dra_backend*)pBackendALSA;
-
-#if 0
-on_error:
- if (pBackendALSA != NULL) {
- free(pBackendALSA);
- }
-
- return NULL;
-#endif
-}
-
-void dra_backend_delete_alsa(dra_backend* pBackend)
-{
- dra_backend_alsa* pBackendALSA = (dra_backend_alsa*)pBackend;
- if (pBackendALSA == NULL) {
- return;
- }
-
- free(pBackend);
-}
-
-
-void dra_backend_device_close_alsa(dra_backend_device* pDevice)
-{
- dra_backend_device_alsa* pDeviceALSA = (dra_backend_device_alsa*)pDevice;
- if (pDeviceALSA == NULL) {
- return;
- }
-
- if (pDeviceALSA->deviceALSA != NULL) {
- snd_pcm_close(pDeviceALSA->deviceALSA);
- }
-
- free(pDeviceALSA->pIntermediaryBuffer);
- free(pDeviceALSA);
-}
-
-dra_backend_device* dra_backend_device_open_playback_alsa(dra_backend* pBackend, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds)
-{
- unsigned int periods;
- int dir;
- size_t sampleRateInMilliseconds;
- unsigned int proposedFramesPerFragment;
- unsigned int framesPerFragment;
- snd_pcm_sw_params_t* pSWParams;
-
- dra_backend_alsa* pBackendALSA = (dra_backend_alsa*)pBackend;
- if (pBackendALSA == NULL) {
- return NULL;
- }
-
- snd_pcm_hw_params_t* pHWParams = NULL;
-
- dra_backend_device_alsa* pDeviceALSA = (dra_backend_device_alsa*)calloc(1, sizeof(*pDeviceALSA));
- if (pDeviceALSA == NULL) {
- goto on_error;
- }
-
- pDeviceALSA->pBackend = pBackend;
- pDeviceALSA->type = dra_device_type_playback;
- pDeviceALSA->channels = channels;
- pDeviceALSA->sampleRate = sampleRate;
-
- char deviceName[1024];
- if (!dra_alsa__get_device_name_by_id(pBackend, deviceID, deviceName)) { // <-- This will return "default" if deviceID is 0.
- goto on_error;
- }
-
- if (snd_pcm_open(&pDeviceALSA->deviceALSA, deviceName, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
- goto on_error;
- }
-
-
- if (snd_pcm_hw_params_malloc(&pHWParams) < 0) {
- goto on_error;
- }
-
- if (snd_pcm_hw_params_any(pDeviceALSA->deviceALSA, pHWParams) < 0) {
- goto on_error;
- }
-
- if (snd_pcm_hw_params_set_access(pDeviceALSA->deviceALSA, pHWParams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
- goto on_error;
- }
-
- if (snd_pcm_hw_params_set_format(pDeviceALSA->deviceALSA, pHWParams, SND_PCM_FORMAT_FLOAT_LE) < 0) {
- goto on_error;
- }
-
-
- if (snd_pcm_hw_params_set_rate_near(pDeviceALSA->deviceALSA, pHWParams, &sampleRate, 0) < 0) {
- goto on_error;
- }
-
- if (snd_pcm_hw_params_set_channels_near(pDeviceALSA->deviceALSA, pHWParams, &channels) < 0) {
- goto on_error;
- }
-
- pDeviceALSA->sampleRate = sampleRate;
- pDeviceALSA->channels = channels;
-
- periods = DR_AUDIO_DEFAULT_FRAGMENT_COUNT;
- dir = 1;
- if (snd_pcm_hw_params_set_periods_near(pDeviceALSA->deviceALSA, pHWParams, &periods, &dir) < 0) {
- //printf("Failed to set periods.\n");
- goto on_error;
- }
-
- pDeviceALSA->fragmentCount = periods;
-
- //printf("Periods: %d | Direction: %d\n", periods, dir);
-
-
- sampleRateInMilliseconds = pDeviceALSA->sampleRate / 1000;
- if (sampleRateInMilliseconds == 0) {
- sampleRateInMilliseconds = 1;
- }
-
-
- // According to the ALSA documentation, the value passed to snd_pcm_sw_params_set_avail_min() must be a power
- // of 2 on some hardware. The value passed to this function is the size in frames of a fragment. Thus, to be
- // as robust as possible the size of the hardware buffer should be sized based on the size of a closest power-
- // of-two fragment.
- //
- // To calculate the size of a fragment, the first step is to determine the initial proposed size. From that
- // it is dropped to the previous power of two. The reason for this is that, based on admittedly very basic
- // testing, ALSA seems to have good latency characteristics, and less latency is always preferable.
- proposedFramesPerFragment = sampleRateInMilliseconds * latencyInMilliseconds;
- framesPerFragment = dra_prev_power_of_2(proposedFramesPerFragment);
- if (framesPerFragment == 0) {
- framesPerFragment = 2;
- }
-
- pDeviceALSA->samplesPerFragment = framesPerFragment * pDeviceALSA->channels;
-
- if (snd_pcm_hw_params_set_buffer_size(pDeviceALSA->deviceALSA, pHWParams, framesPerFragment * pDeviceALSA->fragmentCount) < 0) {
- //printf("Failed to set buffer size.\n");
- goto on_error;
- }
-
-
- if (snd_pcm_hw_params(pDeviceALSA->deviceALSA, pHWParams) < 0) {
- goto on_error;
- }
-
- snd_pcm_hw_params_free(pHWParams);
-
-
-
- // Software params. There needs to be at least fragmentSize bytes in the hardware buffer before playing it, and there needs
- // be fragmentSize bytes available after every wait.
- pSWParams = NULL;
- if (snd_pcm_sw_params_malloc(&pSWParams) < 0) {
- goto on_error;
- }
-
- if (snd_pcm_sw_params_current(pDeviceALSA->deviceALSA, pSWParams) != 0) {
- goto on_error;
- }
-
- if (snd_pcm_sw_params_set_start_threshold(pDeviceALSA->deviceALSA, pSWParams, framesPerFragment) != 0) {
- goto on_error;
- }
- if (snd_pcm_sw_params_set_avail_min(pDeviceALSA->deviceALSA, pSWParams, framesPerFragment) != 0) {
- goto on_error;
- }
-
- if (snd_pcm_sw_params(pDeviceALSA->deviceALSA, pSWParams) != 0) {
- goto on_error;
- }
- snd_pcm_sw_params_free(pSWParams);
-
-
- // The intermediary buffer that will be used for mapping/unmapping.
- pDeviceALSA->isBufferMapped = DR_FALSE;
- pDeviceALSA->pIntermediaryBuffer = (float*)malloc(pDeviceALSA->samplesPerFragment * sizeof(float));
- if (pDeviceALSA->pIntermediaryBuffer == NULL) {
- goto on_error;
- }
-
- return (dra_backend_device*)pDeviceALSA;
-
-on_error:
- if (pHWParams) {
- snd_pcm_hw_params_free(pHWParams);
- }
-
- if (pDeviceALSA != NULL) {
- if (pDeviceALSA->deviceALSA != NULL) {
- snd_pcm_close(pDeviceALSA->deviceALSA);
- }
-
- free(pDeviceALSA->pIntermediaryBuffer);
- free(pDeviceALSA);
- }
-
- return NULL;
-}
-
-dra_backend_device* dra_backend_device_open_capture_alsa(dra_backend* pBackend, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds)
-{
- unsigned int periods;
- int dir;
- size_t sampleRateInMilliseconds;
- unsigned int proposedFramesPerFragment;
- unsigned int framesPerFragment;
- snd_pcm_sw_params_t* pSWParams;
-
- dra_backend_alsa* pBackendALSA = (dra_backend_alsa*)pBackend;
- if (pBackendALSA == NULL) {
- return NULL;
- }
-
- snd_pcm_hw_params_t* pHWParams = NULL;
-
- dra_backend_device_alsa* pDeviceALSA = (dra_backend_device_alsa*)calloc(1, sizeof(*pDeviceALSA));
- if (pDeviceALSA == NULL) {
- goto on_error;
- }
-
- pDeviceALSA->pBackend = pBackend;
- pDeviceALSA->type = dra_device_type_capture;
- pDeviceALSA->channels = channels;
- pDeviceALSA->sampleRate = sampleRate;
-
- char deviceName[1024];
- if (!dra_alsa__get_device_name_by_id(pBackend, deviceID, deviceName)) { // <-- This will return "default" if deviceID is 0.
- goto on_error;
- }
-
- if (snd_pcm_open(&pDeviceALSA->deviceALSA, deviceName, SND_PCM_STREAM_CAPTURE, 0) < 0) {
- goto on_error;
- }
-
-
- if (snd_pcm_hw_params_malloc(&pHWParams) < 0) {
- goto on_error;
- }
-
- if (snd_pcm_hw_params_any(pDeviceALSA->deviceALSA, pHWParams) < 0) {
- goto on_error;
- }
-
- if (snd_pcm_hw_params_set_access(pDeviceALSA->deviceALSA, pHWParams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
- goto on_error;
- }
-
- if (snd_pcm_hw_params_set_format(pDeviceALSA->deviceALSA, pHWParams, SND_PCM_FORMAT_FLOAT_LE) < 0) {
- goto on_error;
- }
-
-
- if (snd_pcm_hw_params_set_rate_near(pDeviceALSA->deviceALSA, pHWParams, &sampleRate, 0) < 0) {
- goto on_error;
- }
-
- if (snd_pcm_hw_params_set_channels_near(pDeviceALSA->deviceALSA, pHWParams, &channels) < 0) {
- goto on_error;
- }
-
- pDeviceALSA->sampleRate = sampleRate;
- pDeviceALSA->channels = channels;
-
- periods = DR_AUDIO_DEFAULT_FRAGMENT_COUNT;
- dir = 1;
- if (snd_pcm_hw_params_set_periods_near(pDeviceALSA->deviceALSA, pHWParams, &periods, &dir) < 0) {
- //printf("Failed to set periods.\n");
- goto on_error;
- }
-
- pDeviceALSA->fragmentCount = periods;
-
- //printf("Periods: %d | Direction: %d\n", periods, dir);
-
-
- sampleRateInMilliseconds = pDeviceALSA->sampleRate / 1000;
- if (sampleRateInMilliseconds == 0) {
- sampleRateInMilliseconds = 1;
- }
-
-
- // According to the ALSA documentation, the value passed to snd_pcm_sw_params_set_avail_min() must be a power
- // of 2 on some hardware. The value passed to this function is the size in frames of a fragment. Thus, to be
- // as robust as possible the size of the hardware buffer should be sized based on the size of a closest power-
- // of-two fragment.
- //
- // To calculate the size of a fragment, the first step is to determine the initial proposed size. From that
- // it is dropped to the previous power of two. The reason for this is that, based on admittedly very basic
- // testing, ALSA seems to have good latency characteristics, and less latency is always preferable.
- proposedFramesPerFragment = sampleRateInMilliseconds * latencyInMilliseconds;
- framesPerFragment = dra_prev_power_of_2(proposedFramesPerFragment);
- if (framesPerFragment == 0) {
- framesPerFragment = 2;
- }
-
- pDeviceALSA->samplesPerFragment = framesPerFragment * pDeviceALSA->channels;
-
- if (snd_pcm_hw_params_set_buffer_size(pDeviceALSA->deviceALSA, pHWParams, framesPerFragment * pDeviceALSA->fragmentCount) < 0) {
- //printf("Failed to set buffer size.\n");
- goto on_error;
- }
-
-
- if (snd_pcm_hw_params(pDeviceALSA->deviceALSA, pHWParams) < 0) {
- goto on_error;
- }
-
- snd_pcm_hw_params_free(pHWParams);
-
-
-
- // Software params. There needs to be at least fragmentSize bytes in the hardware buffer before playing it, and there needs
- // be fragmentSize bytes available after every wait.
- pSWParams = NULL;
- if (snd_pcm_sw_params_malloc(&pSWParams) < 0) {
- goto on_error;
- }
-
- if (snd_pcm_sw_params_current(pDeviceALSA->deviceALSA, pSWParams) != 0) {
- goto on_error;
- }
-
- if (snd_pcm_sw_params_set_start_threshold(pDeviceALSA->deviceALSA, pSWParams, framesPerFragment) != 0) {
- goto on_error;
- }
- if (snd_pcm_sw_params_set_avail_min(pDeviceALSA->deviceALSA, pSWParams, framesPerFragment) != 0) {
- goto on_error;
- }
-
- if (snd_pcm_sw_params(pDeviceALSA->deviceALSA, pSWParams) != 0) {
- goto on_error;
- }
- snd_pcm_sw_params_free(pSWParams);
-
- // The intermediary buffer that will be used for mapping/unmapping.
- pDeviceALSA->isBufferMapped = DR_FALSE;
- pDeviceALSA->pIntermediaryBuffer = (float*)malloc(pDeviceALSA->samplesPerFragment * sizeof(float));
- if (pDeviceALSA->pIntermediaryBuffer == NULL) {
- goto on_error;
- }
-
- return (dra_backend_device*)pDeviceALSA;
-
-on_error:
- dra_backend_device_close_alsa((dra_backend_device*)pDeviceALSA);
- return NULL;
-}
-
-dra_backend_device* dra_backend_device_open_alsa(dra_backend* pBackend, dra_device_type type, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds)
-{
- if (type == dra_device_type_playback) {
- return dra_backend_device_open_playback_alsa(pBackend, deviceID, channels, sampleRate, latencyInMilliseconds);
- } else {
- return dra_backend_device_open_capture_alsa(pBackend, deviceID, channels, sampleRate, latencyInMilliseconds);
- }
-}
-
-
-void dra_backend_device_play(dra_backend_device* pDevice)
-{
- dra_backend_device_alsa* pDeviceALSA = (dra_backend_device_alsa*)pDevice;
- if (pDeviceALSA == NULL) {
- return;
- }
-
- snd_pcm_prepare(pDeviceALSA->deviceALSA);
- pDeviceALSA->isPlaying = DR_TRUE;
-}
-
-void dra_backend_device_stop(dra_backend_device* pDevice)
-{
- dra_backend_device_alsa* pDeviceALSA = (dra_backend_device_alsa*)pDevice;
- if (pDeviceALSA == NULL) {
- return;
- }
-
- snd_pcm_drop(pDeviceALSA->deviceALSA);
- pDeviceALSA->isPlaying = DR_FALSE;
-}
-
-dr_bool32 dra_backend_device_wait(dra_backend_device* pDevice)
-{
- dra_backend_device_alsa* pDeviceALSA = (dra_backend_device_alsa*)pDevice;
- if (pDeviceALSA == NULL) {
- return DR_FALSE;
- }
-
- if (!pDeviceALSA->isPlaying) {
- return DR_FALSE;
- }
-
- if (pDevice->type == dra_device_type_playback) {
- int result = snd_pcm_wait(pDeviceALSA->deviceALSA, -1);
- if (result > 0) {
- return DR_TRUE;
- }
-
- if (result == -EPIPE) {
- // xrun. Prepare the device again and just return DR_TRUE.
- snd_pcm_prepare(pDeviceALSA->deviceALSA);
- return DR_TRUE;
- }
- } else {
- snd_pcm_uframes_t frameCount = pDeviceALSA->samplesPerFragment / pDeviceALSA->channels;
- snd_pcm_sframes_t framesRead = snd_pcm_readi(pDeviceALSA->deviceALSA, pDeviceALSA->pIntermediaryBuffer, frameCount);
- if (framesRead > 0) {
- return DR_TRUE;
- }
-
- if (framesRead == -EPIPE) {
- // xrun. Prepare the device again and just return DR_TRUE.
- snd_pcm_prepare(pDeviceALSA->deviceALSA);
- return DR_TRUE;
- }
- }
-
- return DR_FALSE;
-}
-
-void* dra_backend_device_map_next_fragment(dra_backend_device* pDevice, size_t* pSamplesInFragmentOut)
-{
- assert(pSamplesInFragmentOut != NULL);
-
- dra_backend_device_alsa* pDeviceALSA = (dra_backend_device_alsa*)pDevice;
- if (pDeviceALSA == NULL) {
- return NULL;
- }
-
- if (pDeviceALSA->isBufferMapped) {
- return NULL; // A fragment is already mapped. Can only have a single fragment mapped at a time.
- }
-
- //if (pDeviceALSA->type == dra_device_type_capture) {
- // snd_pcm_readi(pDeviceALSA->deviceALSA, pDeviceALSA->pIntermediaryBuffer, pDeviceALSA->samplesPerFragment / pDeviceALSA->channels);
- //}
-
- *pSamplesInFragmentOut = pDeviceALSA->samplesPerFragment;
- return pDeviceALSA->pIntermediaryBuffer;
-}
-
-void dra_backend_device_unmap_next_fragment(dra_backend_device* pDevice)
-{
- dra_backend_device_alsa* pDeviceALSA = (dra_backend_device_alsa*)pDevice;
- if (pDeviceALSA == NULL) {
- return;
- }
-
- if (pDeviceALSA->isBufferMapped) {
- return; // Nothing is mapped.
- }
-
- // Unammping is when the data is written to the device.
- if (pDeviceALSA->type == dra_device_type_playback) {
- snd_pcm_writei(pDeviceALSA->deviceALSA, pDeviceALSA->pIntermediaryBuffer, pDeviceALSA->samplesPerFragment / pDeviceALSA->channels);
- }
-}
-#endif // DR_AUDIO_NO_ALSA
-#endif // __linux__
-
-
-void dra_thread_wait_and_delete(dra_thread thread)
-{
- dra_thread_wait(thread);
- dra_thread_delete(thread);
-}
-
-
-dra_backend* dra_backend_create()
-{
- dra_backend* pBackend = NULL;
-
-#ifdef DR_AUDIO_ENABLE_DSOUND
- pBackend = dra_backend_create_dsound();
- if (pBackend != NULL) {
- return pBackend;
- }
-#endif
-
-#ifdef DR_AUDIO_ENABLE_ALSA
- pBackend = dra_backend_create_alsa();
- if (pBackend != NULL) {
- return pBackend;
- }
-#endif
-
- // If we get here it means we couldn't find a backend. Default to a NULL backend? Returning NULL makes it clearer that an error occured.
- return NULL;
-}
-
-void dra_backend_delete(dra_backend* pBackend)
-{
- if (pBackend == NULL) {
- return;
- }
-
-#ifdef DR_AUDIO_ENABLE_DSOUND
- if (pBackend->type == DR_AUDIO_BACKEND_TYPE_DSOUND) {
- dra_backend_delete_dsound(pBackend);
- return;
- }
-#endif
-
-#ifdef DR_AUDIO_ENABLE_ALSA
- if (pBackend->type == DR_AUDIO_BACKEND_TYPE_ALSA) {
- dra_backend_delete_alsa(pBackend);
- return;
- }
-#endif
-
- // Should never get here. If this assert is triggered it means you haven't plugged in the API in the list above.
- assert(DR_FALSE);
-}
-
-
-dra_backend_device* dra_backend_device_open(dra_backend* pBackend, dra_device_type type, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds)
-{
- if (pBackend == NULL) {
- return NULL;
- }
-
-#ifdef DR_AUDIO_ENABLE_DSOUND
- if (pBackend->type == DR_AUDIO_BACKEND_TYPE_DSOUND) {
- return dra_backend_device_open_dsound(pBackend, type, deviceID, channels, sampleRate, latencyInMilliseconds);
- }
-#endif
-
-#ifdef DR_AUDIO_ENABLE_ALSA
- if (pBackend->type == DR_AUDIO_BACKEND_TYPE_ALSA) {
- return dra_backend_device_open_alsa(pBackend, type, deviceID, channels, sampleRate, latencyInMilliseconds);
- }
-#endif
-
-
- // Should never get here. If this assert is triggered it means you haven't plugged in the API in the list above.
- assert(DR_FALSE);
- return NULL;
-}
-
-void dra_backend_device_close(dra_backend_device* pDevice)
-{
- if (pDevice == NULL) {
- return;
- }
-
- assert(pDevice->pBackend != NULL);
-
-#ifdef DR_AUDIO_ENABLE_DSOUND
- if (pDevice->pBackend->type == DR_AUDIO_BACKEND_TYPE_DSOUND) {
- dra_backend_device_close_dsound(pDevice);
- return;
- }
-#endif
-
-#ifdef DR_AUDIO_ENABLE_ALSA
- if (pDevice->pBackend->type == DR_AUDIO_BACKEND_TYPE_ALSA) {
- dra_backend_device_close_alsa(pDevice);
- return;
- }
-#endif
-}
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// Cross Platform
-//
-///////////////////////////////////////////////////////////////////////////////
-
-// Reads the next frame.
-//
-// Frames are retrieved with respect to the device the voice is attached to. What this basically means is
-// that any data conversions will be done within this function.
-//
-// The return value is a pointer to the voice containing the converted samples, always as floating point.
-//
-// If the voice is in the same format as the device (floating point, same sample rate and channels), then
-// this function will be on a fast path and will return almost immediately with a pointer that points to
-// the voice's actual data without any data conversion.
-//
-// If an error occurs, null is returned. Null will be returned if the end of the voice's buffer is reached
-// and it's non-looping. This will not return NULL if the voice is looping - it will just loop back to the
-// start as one would expect.
-//
-// This function is not thread safe, but can be called from multiple threads if you do your own
-// synchronization. Just keep in mind that the return value may point to the voice's actual internal data.
-float* dra_voice__next_frame(dra_voice* pVoice);
-
-// dra_voice__next_frames()
-size_t dra_voice__next_frames(dra_voice* pVoice, size_t frameCount, float* pSamplesOut);
-
-
-dra_result dra_context_init(dra_context* pContext)
-{
- if (pContext == NULL) return DRA_RESULT_INVALID_ARGS;
- memset(pContext, 0, sizeof(*pContext));
-
- // We need a backend first.
- pContext->pBackend = dra_backend_create();
- if (pContext->pBackend == NULL) {
- return DRA_RESULT_NO_BACKEND; // Failed to create a backend.
- }
-
- return DRA_RESULT_SUCCESS;
-}
-
-void dra_context_uninit(dra_context* pContext)
-{
- if (pContext == NULL || pContext->pBackend == NULL) return;
- dra_backend_delete(pContext->pBackend);
-}
-
-dra_result dra_context_create(dra_context** ppContext)
-{
- if (ppContext == NULL) return DRA_RESULT_INVALID_ARGS;
- *ppContext = NULL;
-
- dra_context* pContext = (dra_context*)malloc(sizeof(*pContext));
- if (pContext == NULL) {
- return DRA_RESULT_OUT_OF_MEMORY;
- }
-
- dra_result result = dra_context_init(pContext);
- if (result != DRA_RESULT_SUCCESS) {
- free(pContext);
- return result;
- }
-
- *ppContext = pContext;
- return DRA_RESULT_SUCCESS;
-}
-
-void dra_context_delete(dra_context* pContext)
-{
- if (pContext == NULL) return;
-
- dra_context_uninit(pContext);
- free(pContext);
-}
-
-
-void dra_event_queue__schedule_event(dra__event_queue* pQueue, dra__event* pEvent)
-{
- if (pQueue == NULL || pEvent == NULL) {
- return;
- }
-
- dra_mutex_lock(pQueue->lock);
- {
- if (pQueue->eventCount == pQueue->eventBufferSize)
- {
- // Ran out of room. Resize.
- size_t newEventBufferSize = (pQueue->eventBufferSize == 0) ? 16 : pQueue->eventBufferSize*2;
- dra__event* pNewEvents = (dra__event*)malloc(newEventBufferSize * sizeof(*pNewEvents));
- if (pNewEvents == NULL) {
- return;
- }
-
- for (size_t i = 0; i < pQueue->eventCount; ++i) {
- pQueue->pEvents[i] = pQueue->pEvents[(pQueue->firstEvent + i) % pQueue->eventBufferSize];
- }
-
- pQueue->firstEvent = 0;
- pQueue->eventBufferSize = newEventBufferSize;
- pQueue->pEvents = pNewEvents;
- }
-
- assert(pQueue->eventCount < pQueue->eventBufferSize);
-
- pQueue->pEvents[(pQueue->firstEvent + pQueue->eventCount) % pQueue->eventBufferSize] = *pEvent;
- pQueue->eventCount += 1;
- }
- dra_mutex_unlock(pQueue->lock);
-}
-
-void dra_event_queue__cancel_events_of_voice(dra__event_queue* pQueue, dra_voice* pVoice)
-{
- if (pQueue == NULL || pVoice == NULL) {
- return;
- }
-
- dra_mutex_lock(pQueue->lock);
- {
- // We don't actually remove anything from the queue, but instead zero out the event's data.
- for (size_t i = 0; i < pQueue->eventCount; ++i) {
- dra__event* pEvent = &pQueue->pEvents[(pQueue->firstEvent + i) % pQueue->eventBufferSize];
- if (pEvent->pVoice == pVoice) {
- pEvent->pVoice = NULL;
- pEvent->proc = NULL;
- }
- }
- }
- dra_mutex_unlock(pQueue->lock);
-}
-
-dr_bool32 dra_event_queue__next_event(dra__event_queue* pQueue, dra__event* pEventOut)
-{
- if (pQueue == NULL || pEventOut == NULL) {
- return DR_FALSE;
- }
-
- dr_bool32 result = DR_FALSE;
- dra_mutex_lock(pQueue->lock);
- {
- if (pQueue->eventCount > 0) {
- *pEventOut = pQueue->pEvents[pQueue->firstEvent];
- pQueue->firstEvent = (pQueue->firstEvent + 1) % pQueue->eventBufferSize;
- pQueue->eventCount -= 1;
- result = DR_TRUE;
- }
- }
- dra_mutex_unlock(pQueue->lock);
-
- return result;
-}
-
-void dra_event_queue__post_events(dra__event_queue* pQueue)
-{
- if (pQueue == NULL) {
- return;
- }
-
- dra__event nextEvent;
- while (dra_event_queue__next_event(pQueue, &nextEvent)) {
- if (nextEvent.proc) {
- nextEvent.proc(nextEvent.id, nextEvent.pUserData);
- }
- }
-}
-
-
-void dra_device__post_event(dra_device* pDevice, dra_thread_event_type type)
-{
- assert(pDevice != NULL);
-
- pDevice->nextThreadEventType = type;
- dra_semaphore_release(pDevice->threadEventSem);
-}
-
-void dra_device__lock(dra_device* pDevice)
-{
- assert(pDevice != NULL);
- dra_mutex_lock(pDevice->mutex);
-}
-
-void dra_device__unlock(dra_device* pDevice)
-{
- assert(pDevice != NULL);
- dra_mutex_unlock(pDevice->mutex);
-}
-
-dr_bool32 dra_device__is_playing_nolock(dra_device* pDevice)
-{
- assert(pDevice != NULL);
- return pDevice->isPlaying;
-}
-
-dr_bool32 dra_device__mix_next_fragment(dra_device* pDevice)
-{
- assert(pDevice != NULL);
-
- size_t samplesInFragment;
- void* pSampleData = dra_backend_device_map_next_fragment(pDevice->pBackendDevice, &samplesInFragment);
- if (pSampleData == NULL) {
- dra_backend_device_stop(pDevice->pBackendDevice);
- return DR_FALSE;
- }
-
- size_t framesInFragment = samplesInFragment / pDevice->channels;
- size_t framesMixed = dra_mixer_mix_next_frames(pDevice->pMasterMixer, framesInFragment);
-
- memcpy(pSampleData, pDevice->pMasterMixer->pStagingBuffer, (size_t)samplesInFragment * sizeof(float));
-
- if (pDevice->onSamplesProcessed) {
- pDevice->onSamplesProcessed(pDevice, framesMixed * pDevice->channels, (const float*)pSampleData, pDevice->pUserDataForOnSamplesProcessed);
- }
-
- dra_backend_device_unmap_next_fragment(pDevice->pBackendDevice);
-
- if (framesMixed == 0) {
- pDevice->stopOnNextFragment = DR_TRUE;
- }
-
- //printf("Mixed next fragment into %p\n", pSampleData);
- return DR_TRUE;
-}
-
-void dra_device__play(dra_device* pDevice)
-{
- assert(pDevice != NULL);
-
- dra_device__lock(pDevice);
- {
- // Don't do anything if the device is already playing.
- if (!dra_device__is_playing_nolock(pDevice))
- {
- assert(pDevice->pBackendDevice->type == dra_device_type_capture || pDevice->playingVoicesCount > 0);
-
- dra_device__post_event(pDevice, dra_thread_event_type_play);
- pDevice->isPlaying = DR_TRUE;
- pDevice->stopOnNextFragment = DR_FALSE;
- }
- }
- dra_device__unlock(pDevice);
-}
-
-void dra_device__stop(dra_device* pDevice)
-{
- assert(pDevice != NULL);
-
- dra_device__lock(pDevice);
- {
- // Don't do anything if the device is already stopped.
- if (dra_device__is_playing_nolock(pDevice))
- {
- //assert(pDevice->playingVoicesCount == 0);
-
- dra_backend_device_stop(pDevice->pBackendDevice);
- pDevice->isPlaying = DR_FALSE;
- }
- }
- dra_device__unlock(pDevice);
-}
-
-void dra_device__voice_playback_count_inc(dra_device* pDevice)
-{
- assert(pDevice != NULL);
-
- dra_device__lock(pDevice);
- {
- pDevice->playingVoicesCount += 1;
- pDevice->stopOnNextFragment = DR_FALSE;
- }
- dra_device__unlock(pDevice);
-}
-
-void dra_device__voice_playback_count_dec(dra_device* pDevice)
-{
- dra_device__lock(pDevice);
- {
- pDevice->playingVoicesCount -= 1;
- }
- dra_device__unlock(pDevice);
-}
-
-// The entry point signature is slightly different depending on whether or not we're using Win32 or POSIX threads.
-#ifdef _WIN32
-DWORD dra_device__thread_proc(LPVOID pData)
-#else
-void* dra_device__thread_proc(void* pData)
-#endif
-{
- dra_device* pDevice = (dra_device*)pData;
- assert(pDevice != NULL);
-
- // The thread is always open for the life of the device. The loop below will only terminate when a terminate message is received.
- for (;;)
- {
- // Wait for an event...
- dra_semaphore_wait(pDevice->threadEventSem);
-
- if (pDevice->nextThreadEventType == dra_thread_event_type_terminate) {
- //printf("Terminated!\n");
- break;
- }
-
- if (pDevice->nextThreadEventType == dra_thread_event_type_play)
- {
- if (pDevice->pBackendDevice->type == dra_device_type_playback) {
- // The backend device needs to start playing, but we first need to ensure it has an initial chunk of data available.
- dra_device__mix_next_fragment(pDevice);
- }
-
- // Start playing the backend device only after the initial fragment has been mixed, and only if it's a playback device.
- dra_backend_device_play(pDevice->pBackendDevice);
-
- // There could be "play" events needing to be posted.
- dra_event_queue__post_events(&pDevice->eventQueue);
-
-
- // Wait for the device to request more data...
- while (dra_backend_device_wait(pDevice->pBackendDevice)) {
- dra_event_queue__post_events(&pDevice->eventQueue);
-
- if (pDevice->stopOnNextFragment) {
- dra_device__stop(pDevice); // <-- Don't break from the loop here. Instead have dra_backend_device_wait() return naturally from the stop notification.
- } else {
- if (pDevice->pBackendDevice->type == dra_device_type_playback) {
- dra_device__mix_next_fragment(pDevice);
- } else {
- size_t sampleCount;
- void* pSampleData = dra_backend_device_map_next_fragment(pDevice->pBackendDevice, &sampleCount);
- if (pSampleData != NULL) {
- if (pDevice->onSamplesProcessed) {
- pDevice->onSamplesProcessed(pDevice, sampleCount, (const float*)pSampleData, pDevice->pUserDataForOnSamplesProcessed);
- }
-
- dra_backend_device_unmap_next_fragment(pDevice->pBackendDevice);
- }
- }
- }
- }
-
- // There could be some events needing to be posted.
- dra_event_queue__post_events(&pDevice->eventQueue);
- //printf("Stopped!\n");
-
- // Don't fall through.
- continue;
- }
- }
-
- return 0;
-}
-
-dra_result dra_device_init_ex(dra_context* pContext, dra_device_type type, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds, dra_device* pDevice)
-{
- if (pDevice == NULL) return DRA_RESULT_INVALID_ARGS;
-
- dr_bool32 ownsContext = DR_FALSE;
- if (pContext == NULL) {
- pContext = (dra_context*)malloc(sizeof(*pContext));
- if (pContext == NULL) {
- return DRA_RESULT_OUT_OF_MEMORY;
- }
-
- dra_result result = dra_context_init(pContext);
- if (result != DRA_RESULT_SUCCESS) {
- return result;
- }
-
- ownsContext = DR_TRUE;
- }
-
- if (sampleRate == 0) sampleRate = DR_AUDIO_DEFAULT_SAMPLE_RATE;
- if (latencyInMilliseconds == 0) latencyInMilliseconds = DR_AUDIO_DEFAULT_LATENCY;
-
-
- dra_result result = DRA_RESULT_SUCCESS;
-
- memset(pDevice, 0, sizeof(*pDevice));
- pDevice->pContext = pContext;
- pDevice->ownsContext = ownsContext;
-
- pDevice->pBackendDevice = dra_backend_device_open(pContext->pBackend, type, deviceID, channels, sampleRate, latencyInMilliseconds);
- if (pDevice->pBackendDevice == NULL) {
- result = DRA_RESULT_NO_BACKEND_DEVICE;
- goto on_error;
- }
-
- pDevice->channels = pDevice->pBackendDevice->channels;
- pDevice->sampleRate = pDevice->pBackendDevice->sampleRate;
-
-
- pDevice->mutex = dra_mutex_create();
- if (pDevice->mutex == NULL) {
- result = DRA_RESULT_UNKNOWN_ERROR; // TODO: Change this to the return value of dra_mutex_create().
- goto on_error;
- }
-
- pDevice->threadEventSem = dra_semaphore_create(0);
- if (pDevice->threadEventSem == NULL) {
- result = DRA_RESULT_UNKNOWN_ERROR; // TODO: Change this to the return value of dra_semaphore_create().
- goto on_error;
- }
-
- result = dra_mixer_create(pDevice, &pDevice->pMasterMixer);
- if (result != DRA_RESULT_SUCCESS) {
- goto on_error;
- }
-
-
- pDevice->eventQueue.lock = dra_mutex_create();
- if (pDevice->eventQueue.lock == NULL) {
- result = DRA_RESULT_UNKNOWN_ERROR; // TODO: Change this to the return value of dra_mutex_create().
- goto on_error;
- }
-
-
- // Create the thread last to ensure the device is in a valid state as soon as the entry procedure is run.
- pDevice->thread = dra_thread_create(dra_device__thread_proc, pDevice);
- if (pDevice->thread == NULL) {
- result = DRA_RESULT_UNKNOWN_ERROR; // TODO: Change this to the return value of dra_thread_create().
- goto on_error;
- }
-
- return result;
-
-on_error:
- if (pDevice != NULL) {
- if (pDevice->pMasterMixer != NULL) dra_mixer_delete(pDevice->pMasterMixer);
- if (pDevice->pBackendDevice != NULL) dra_backend_device_close(pDevice->pBackendDevice);
- if (pDevice->threadEventSem != NULL) dra_semaphore_delete(pDevice->threadEventSem);
- if (pDevice->mutex != NULL) dra_mutex_delete(pDevice->mutex);
-
- if (pDevice->ownsContext) {
- dra_context_uninit(pDevice->pContext);
- free(pDevice->pContext);
- }
- }
-
- return result;
-}
-
-dra_result dra_device_init(dra_context* pContext, dra_device_type type, dra_device* pDevice)
-{
- return dra_device_init_ex(pContext, type, 0, 0, DR_AUDIO_DEFAULT_SAMPLE_RATE, DR_AUDIO_DEFAULT_LATENCY, pDevice);
-}
-
-void dra_device_uninit(dra_device* pDevice)
-{
- if (pDevice == NULL || pDevice->pContext == NULL) return;
-
- // Mark the device as closed in order to prevent other threads from doing work after closing.
- dra_device__lock(pDevice);
- {
- pDevice->isClosed = DR_TRUE;
- }
- dra_device__unlock(pDevice);
-
- // Stop playback before doing anything else.
- dra_device__stop(pDevice);
-
- // The background thread needs to be terminated at this point.
- dra_device__post_event(pDevice, dra_thread_event_type_terminate);
- dra_thread_wait_and_delete(pDevice->thread);
-
-
- // At this point the device is marked as closed which should prevent voice's and mixers from being created and deleted. We now need
- // to delete the master mixer which in turn will delete all of the attached voices and submixers.
- if (pDevice->pMasterMixer != NULL) {
- dra_mixer_delete(pDevice->pMasterMixer);
- }
-
-
- if (pDevice->pBackendDevice != NULL) {
- dra_backend_device_close(pDevice->pBackendDevice);
- }
-
- if (pDevice->threadEventSem != NULL) {
- dra_semaphore_delete(pDevice->threadEventSem);
- }
-
- if (pDevice->mutex != NULL) {
- dra_mutex_delete(pDevice->mutex);
- }
-
-
- if (pDevice->eventQueue.pEvents) {
- free(pDevice->eventQueue.pEvents);
- }
-
-
- if (pDevice->ownsContext) {
- dra_context_uninit(pDevice->pContext);
- free(pDevice->pContext);
- }
-}
-
-
-dra_result dra_device_create_ex(dra_context* pContext, dra_device_type type, unsigned int deviceID, unsigned int channels, unsigned int sampleRate, unsigned int latencyInMilliseconds, dra_device** ppDevice)
-{
- if (ppDevice == NULL) return DRA_RESULT_INVALID_ARGS;
- *ppDevice = NULL;
-
- dra_device* pDevice = (dra_device*)malloc(sizeof(*pDevice));
- if (pDevice == NULL) {
- return DRA_RESULT_OUT_OF_MEMORY;
- }
-
- dra_result result = dra_device_init_ex(pContext, type, deviceID, channels, sampleRate, latencyInMilliseconds, pDevice);
- if (result != DRA_RESULT_SUCCESS) {
- free(pDevice);
- return result;
- }
-
- *ppDevice = pDevice;
- return DRA_RESULT_SUCCESS;
-}
-
-dra_result dra_device_create(dra_context* pContext, dra_device_type type, dra_device** ppDevice)
-{
- return dra_device_create_ex(pContext, type, 0, 0, DR_AUDIO_DEFAULT_SAMPLE_RATE, DR_AUDIO_DEFAULT_LATENCY, ppDevice);
-}
-
-void dra_device_delete(dra_device* pDevice)
-{
- if (pDevice == NULL) return;
-
- dra_device_uninit(pDevice);
- free(pDevice);
-}
-
-dra_result dra_device_start(dra_device* pDevice)
-{
- if (pDevice == NULL || pDevice->pBackendDevice->type == dra_device_type_playback) return DRA_RESULT_INVALID_ARGS;
-
- dra_device__play(pDevice);
- return DRA_RESULT_SUCCESS;
-}
-
-dra_result dra_device_stop(dra_device* pDevice)
-{
- if (pDevice == NULL || pDevice->pBackendDevice->type == dra_device_type_playback) return DRA_RESULT_INVALID_ARGS;
-
- dra_device__stop(pDevice);
- return DRA_RESULT_SUCCESS;
-}
-
-void dra_device_set_samples_processed_callback(dra_device* pDevice, dra_samples_processed_proc proc, void* pUserData)
-{
- if (pDevice == NULL) return;
- pDevice->onSamplesProcessed = proc;
- pDevice->pUserDataForOnSamplesProcessed = pUserData;
-}
-
-
-dra_result dra_mixer_create(dra_device* pDevice, dra_mixer** ppMixer)
-{
- if (ppMixer == NULL) return DRA_RESULT_INVALID_ARGS;
- *ppMixer = NULL;
-
- if (pDevice == NULL) return DRA_RESULT_INVALID_ARGS;
-
-
- // There needs to be two blocks of memory at the end of the mixer - one for the staging buffer and another for the buffer that
- // will store the float32 samples of the voice currently being mixed.
- size_t extraDataSize = (size_t)pDevice->pBackendDevice->samplesPerFragment * sizeof(float) * 2;
- dra_mixer* pMixer = (dra_mixer*)calloc(1, sizeof(*pMixer) + extraDataSize);
- if (pMixer == NULL) {
- return DRA_RESULT_OUT_OF_MEMORY;
- }
-
- pMixer->pDevice = pDevice;
- pMixer->linearVolume = 1;
-
- pMixer->pStagingBuffer = pMixer->pData;
- pMixer->pNextSamplesToMix = pMixer->pStagingBuffer + pDevice->pBackendDevice->samplesPerFragment;
-
- // Attach the mixer to the master mixer by default. If the master mixer is null it means we're creating the master mixer itself.
- if (pDevice->pMasterMixer != NULL) {
- dra_mixer_attach_submixer(pDevice->pMasterMixer, pMixer);
- }
-
- *ppMixer = pMixer;
- return DRA_RESULT_SUCCESS;
-}
-
-void dra_mixer_delete(dra_mixer* pMixer)
-{
- if (pMixer == NULL) return;
-
- dra_mixer_detach_all_submixers(pMixer);
- dra_mixer_detach_all_voices(pMixer);
-
- if (pMixer->pParentMixer != NULL) {
- dra_mixer_detach_submixer(pMixer->pParentMixer, pMixer);
- }
-
- free(pMixer);
-}
-
-void dra_mixer_attach_submixer(dra_mixer* pMixer, dra_mixer* pSubmixer)
-{
- if (pMixer == NULL || pSubmixer == NULL) {
- return;
- }
-
- if (pSubmixer->pParentMixer != NULL) {
- dra_mixer_detach_submixer(pSubmixer->pParentMixer, pSubmixer);
- }
-
-
- pSubmixer->pParentMixer = pMixer;
-
- if (pMixer->pFirstChildMixer == NULL) {
- pMixer->pFirstChildMixer = pSubmixer;
- pMixer->pLastChildMixer = pSubmixer;
- return;
- }
-
- assert(pMixer->pLastChildMixer != NULL);
- pMixer->pLastChildMixer->pNextSiblingMixer = pSubmixer;
- pSubmixer->pPrevSiblingMixer = pMixer->pLastChildMixer;
- pSubmixer->pNextSiblingMixer = NULL;
- pMixer->pLastChildMixer = pSubmixer;
-}
-
-void dra_mixer_detach_submixer(dra_mixer* pMixer, dra_mixer* pSubmixer)
-{
- if (pMixer == NULL || pSubmixer == NULL) {
- return;
- }
-
- if (pSubmixer->pParentMixer != pMixer) {
- return; // Doesn't have the same parent.
- }
-
-
- // Detach from parent.
- if (pSubmixer->pParentMixer->pFirstChildMixer == pSubmixer) {
- pSubmixer->pParentMixer->pFirstChildMixer = pSubmixer->pNextSiblingMixer;
- }
- if (pSubmixer->pParentMixer->pLastChildMixer == pSubmixer) {
- pSubmixer->pParentMixer->pLastChildMixer = pSubmixer->pPrevSiblingMixer;
- }
-
- pSubmixer->pParentMixer = NULL;
-
-
- // Detach from siblings.
- if (pSubmixer->pPrevSiblingMixer) {
- pSubmixer->pPrevSiblingMixer->pNextSiblingMixer = pSubmixer->pNextSiblingMixer;
- }
- if (pSubmixer->pNextSiblingMixer) {
- pSubmixer->pNextSiblingMixer->pPrevSiblingMixer = pSubmixer->pPrevSiblingMixer;
- }
-
- pSubmixer->pNextSiblingMixer = NULL;
- pSubmixer->pPrevSiblingMixer = NULL;
-}
-
-void dra_mixer_detach_all_submixers(dra_mixer* pMixer)
-{
- if (pMixer == NULL) {
- return;
- }
-
- while (pMixer->pFirstChildMixer != NULL) {
- dra_mixer_detach_submixer(pMixer, pMixer->pFirstChildMixer);
- }
-}
-
-void dra_mixer_attach_voice(dra_mixer* pMixer, dra_voice* pVoice)
-{
- if (pMixer == NULL || pVoice == NULL) {
- return;
- }
-
- if (pVoice->pMixer != NULL) {
- dra_mixer_detach_voice(pVoice->pMixer, pVoice);
- }
-
- pVoice->pMixer = pMixer;
-
- if (pMixer->pFirstVoice == NULL) {
- pMixer->pFirstVoice = pVoice;
- pMixer->pLastVoice = pVoice;
- return;
- }
-
- // Attach the voice to the end of the list.
- pVoice->pPrevVoice = pMixer->pLastVoice;
- pVoice->pNextVoice = NULL;
-
- pMixer->pLastVoice->pNextVoice = pVoice;
- pMixer->pLastVoice = pVoice;
-}
-
-void dra_mixer_detach_voice(dra_mixer* pMixer, dra_voice* pVoice)
-{
- if (pMixer == NULL || pVoice == NULL) {
- return;
- }
-
-
- // Detach from mixer.
- if (pMixer->pFirstVoice == pVoice) {
- pMixer->pFirstVoice = pMixer->pFirstVoice->pNextVoice;
- }
- if (pMixer->pLastVoice == pVoice) {
- pMixer->pLastVoice = pMixer->pLastVoice->pPrevVoice;
- }
-
- pVoice->pMixer = NULL;
-
-
- // Remove from list.
- if (pVoice->pNextVoice) {
- pVoice->pNextVoice->pPrevVoice = pVoice->pPrevVoice;
- }
- if (pVoice->pPrevVoice) {
- pVoice->pPrevVoice->pNextVoice = pVoice->pNextVoice;
- }
-
- pVoice->pNextVoice = NULL;
- pVoice->pPrevVoice = NULL;
-
-}
-
-void dra_mixer_detach_all_voices(dra_mixer* pMixer)
-{
- if (pMixer == NULL) {
- return;
- }
-
- while (pMixer->pFirstVoice) {
- dra_mixer_detach_voice(pMixer, pMixer->pFirstVoice);
- }
-}
-
-void dra_mixer_set_volume(dra_mixer* pMixer, float linearVolume)
-{
- if (pMixer == NULL) {
- return;
- }
-
- pMixer->linearVolume = linearVolume;
-}
-
-float dra_mixer_get_volume(dra_mixer* pMixer)
-{
- if (pMixer == NULL) {
- return 0;
- }
-
- return pMixer->linearVolume;
-}
-
-size_t dra_mixer_mix_next_frames(dra_mixer* pMixer, size_t frameCount)
-{
- if (pMixer == NULL) {
- return 0;
- }
-
- if (pMixer->pFirstVoice == NULL && pMixer->pFirstChildMixer == NULL) {
- return 0;
- }
-
- if (dra_mixer_is_paused(pMixer)) {
- return 0;
- }
-
-
- size_t framesMixed = 0;
-
- // Mixing works by simply adding together the sample data of each voice and submixer. We just start at 0 and then
- // just accumulate each one.
- memset(pMixer->pStagingBuffer, 0, frameCount * pMixer->pDevice->channels * sizeof(float));
-
- // Voices first. Doesn't really matter if we do voices or submixers first.
- for (dra_voice* pVoice = pMixer->pFirstVoice; pVoice != NULL; pVoice = pVoice->pNextVoice)
- {
- if (pVoice->isPlaying) {
- size_t framesJustRead = dra_voice__next_frames(pVoice, frameCount, pMixer->pNextSamplesToMix);
- for (size_t i = 0; i < framesJustRead * pMixer->pDevice->channels; ++i) {
- pMixer->pStagingBuffer[i] += (pMixer->pNextSamplesToMix[i] * pVoice->linearVolume);
- }
-
- // Has the end of the voice's buffer been reached?
- if (framesJustRead < frameCount)
- {
- // We'll get here if the end of the voice's buffer has been reached. The voice needs to be forcefully stopped to
- // ensure the device is aware of it and is able to put itself into a dormant state if necessary. Also note that
- // the playback position is moved back to start. The rationale for this is that it's a little bit more useful than
- // just leaving the playback position sitting on the end. Also it allows an application to restart playback with
- // a single call to dra_voice_play() without having to explicitly set the playback position.
- pVoice->currentReadPos = 0;
- dra_voice_stop(pVoice);
- }
-
- if (framesMixed < framesJustRead) {
- framesMixed = framesJustRead;
- }
- }
- }
-
- // Submixers.
- for (dra_mixer* pSubmixer = pMixer->pFirstChildMixer; pSubmixer != NULL; pSubmixer = pSubmixer->pNextSiblingMixer)
- {
- size_t framesJustMixed = dra_mixer_mix_next_frames(pSubmixer, frameCount);
- for (size_t i = 0; i < framesJustMixed * pMixer->pDevice->channels; ++i) {
- pMixer->pStagingBuffer[i] += pSubmixer->pStagingBuffer[i];
- }
-
- if (framesMixed < framesJustMixed) {
- framesMixed = framesJustMixed;
- }
- }
-
-
- // At this point the mixer's effects and volume need to be applied to each sample.
- size_t samplesMixed = framesMixed * pMixer->pDevice->channels;
- for (size_t i = 0; i < samplesMixed; ++i) {
- pMixer->pStagingBuffer[i] *= pMixer->linearVolume;
- }
-
-
- // Finally we need to ensure every samples is clamped to -1 to 1. There are two easy ways to do this: clamp or normalize. For now I'm just
- // clamping to keep it simple, but it might be valuable to make this configurable.
- for (size_t i = 0; i < framesMixed * pMixer->pDevice->channels; ++i)
- {
- // TODO: Investigate using SSE here (MINPS/MAXPS)
- // TODO: Investigate if the backends clamp the samples themselves, thus making this redundant.
- if (pMixer->pStagingBuffer[i] < -1) {
- pMixer->pStagingBuffer[i] = -1;
- } else if (pMixer->pStagingBuffer[i] > 1) {
- pMixer->pStagingBuffer[i] = 1;
- }
- }
-
- return framesMixed;
-}
-
-size_t dra_mixer_count_attached_voices(dra_mixer* pMixer)
-{
- if (pMixer == NULL) {
- return 0;
- }
-
- size_t count = 0;
- for (dra_voice* pVoice = pMixer->pFirstVoice; pVoice != NULL; pVoice = pVoice->pNextVoice) {
- count += 1;
- }
-
- return count;
-}
-
-size_t dra_mixer_count_attached_voices_recursive(dra_mixer* pMixer)
-{
- if (pMixer == NULL) {
- return 0;
- }
-
- size_t count = dra_mixer_count_attached_voices(pMixer);
-
- // Children.
- for (dra_mixer* pChildMixer = pMixer->pFirstChildMixer; pChildMixer != NULL; pChildMixer = pChildMixer->pNextSiblingMixer) {
- count += dra_mixer_count_attached_voices_recursive(pChildMixer);
- }
-
- return count;
-}
-
-size_t dra_mixer_gather_attached_voices(dra_mixer* pMixer, dra_voice** ppVoicesOut)
-{
- if (pMixer == NULL) {
- return 0;
- }
-
- if (ppVoicesOut == NULL) {
- return dra_mixer_count_attached_voices(pMixer);
- }
-
- size_t count = 0;
- for (dra_voice* pVoice = pMixer->pFirstVoice; pVoice != NULL; pVoice = pVoice->pNextVoice) {
- ppVoicesOut[count] = pVoice;
- count += 1;
- }
-
- return count;
-}
-
-size_t dra_mixer_gather_attached_voices_recursive(dra_mixer* pMixer, dra_voice** ppVoicesOut)
-{
- if (pMixer == NULL) {
- return 0;
- }
-
- if (ppVoicesOut == NULL) {
- return dra_mixer_count_attached_voices_recursive(pMixer);
- }
-
- size_t count = dra_mixer_gather_attached_voices(pMixer, ppVoicesOut);
-
- // Children.
- for (dra_mixer* pChildMixer = pMixer->pFirstChildMixer; pChildMixer != NULL; pChildMixer = pChildMixer->pNextSiblingMixer) {
- count += dra_mixer_gather_attached_voices_recursive(pChildMixer, ppVoicesOut + count);
- }
-
- return count;
-}
-
-
-void dra_mixer_pause(dra_mixer* pMixer)
-{
- if (pMixer == NULL) return;
- pMixer->flags |= DRA_MIXER_FLAG_PAUSED;
-}
-
-void dra_mixer_resume(dra_mixer* pMixer)
-{
- if (pMixer == NULL) return;
- pMixer->flags &= ~DRA_MIXER_FLAG_PAUSED;
-
- // When the mixer was paused it may have resulted in no audio being played which means dr_audio will have stopped the device
- // to save CPU usage. We need to make sure we wake up the device.
- dra_device__play(pMixer->pDevice);
-}
-
-dr_bool32 dra_mixer_is_paused(dra_mixer* pMixer)
-{
- if (pMixer == NULL) return DR_FALSE;
- return (pMixer->flags & DRA_MIXER_FLAG_PAUSED) != 0;
-}
-
-
-
-dra_result dra_voice_create(dra_device* pDevice, dra_format format, unsigned int channels, unsigned int sampleRate, size_t sizeInBytes, const void* pInitialData, dra_voice** ppVoice)
-{
- if (ppVoice == NULL) return DRA_RESULT_INVALID_ARGS;
- *ppVoice = NULL;
-
- if (pDevice == NULL || sizeInBytes == 0) return DRA_RESULT_INVALID_ARGS;
-
-
- // The number of bytes must be a multiple of the size of a frame.
- size_t bytesPerSample = dra_get_bytes_per_sample_by_format(format);
- if ((sizeInBytes % (bytesPerSample * channels)) != 0) {
- return DRA_RESULT_INVALID_ARGS;
- }
-
-
- dra_voice* pVoice = (dra_voice*)calloc(1, sizeof(*pVoice) + sizeInBytes);
- if (pVoice == NULL) {
- return DRA_RESULT_OUT_OF_MEMORY;
- }
-
- pVoice->pDevice = pDevice;
- pVoice->pMixer = NULL;
- pVoice->format = format;
- pVoice->channels = channels;
- pVoice->sampleRate = sampleRate;
- pVoice->linearVolume = 1;
- pVoice->isPlaying = DR_FALSE;
- pVoice->isLooping = DR_FALSE;
- pVoice->frameCount = sizeInBytes / (bytesPerSample * channels);
- pVoice->currentReadPos = 0;
- pVoice->sizeInBytes = sizeInBytes;
- pVoice->pNextVoice = NULL;
- pVoice->pPrevVoice = NULL;
-
- if (pInitialData != NULL) {
- memcpy(pVoice->pData, pInitialData, sizeInBytes);
- } else {
- //memset(pVoice->pData, 0, sizeInBytes); // <-- This is already zeroed by the calloc() above, but leaving this comment here for emphasis.
- }
-
-
- // Sample rate conversion.
- if (sampleRate == pDevice->sampleRate) {
- pVoice->src.algorithm = dra_src_algorithm_none;
- } else {
- pVoice->src.algorithm = dra_src_algorithm_linear;
- }
-
-
- // Attach the voice to the master mixer by default.
- if (pDevice->pMasterMixer != NULL) {
- dra_mixer_attach_voice(pDevice->pMasterMixer, pVoice);
- }
-
- *ppVoice = pVoice;
- return DRA_RESULT_SUCCESS;
-}
-
-dra_result dra_voice_create_compatible(dra_device* pDevice, size_t sizeInBytes, const void* pInitialData, dra_voice** ppVoice)
-{
- return dra_voice_create(pDevice, dra_format_f32, pDevice->channels, pDevice->sampleRate, sizeInBytes, pInitialData, ppVoice);
-}
-
-void dra_voice_delete(dra_voice* pVoice)
-{
- if (pVoice == NULL) return;
-
- // The voice needs to be stopped...
- dra_voice_stop(pVoice);
-
- // ... and all pending events need to be cancelled to ensure the application isn't notified of an event of a deleted voice.
- dra_event_queue__cancel_events_of_voice(&pVoice->pDevice->eventQueue, pVoice);
-
- if (pVoice->pMixer != NULL) {
- dra_mixer_detach_voice(pVoice->pMixer, pVoice);
- }
-
- free(pVoice);
-}
-
-void dra_voice_play(dra_voice* pVoice, dr_bool32 loop)
-{
- if (pVoice == NULL) {
- return;
- }
-
- if (!dra_voice_is_playing(pVoice)) {
- dra_device__voice_playback_count_inc(pVoice->pDevice);
- } else {
- if (dra_voice_is_looping(pVoice) == loop) {
- return; // Nothing has changed - don't need to do anything.
- }
- }
-
- pVoice->isPlaying = DR_TRUE;
- pVoice->isLooping = loop;
-
- dra_event_queue__schedule_event(&pVoice->pDevice->eventQueue, &pVoice->playEvent);
-
- // When playing a voice we need to ensure the backend device is playing.
- dra_device__play(pVoice->pDevice);
-}
-
-void dra_voice_stop(dra_voice* pVoice)
-{
- if (pVoice == NULL) {
- return;
- }
-
- if (!dra_voice_is_playing(pVoice)) {
- return; // The voice is already stopped.
- }
-
- dra_device__voice_playback_count_dec(pVoice->pDevice);
-
- pVoice->isPlaying = DR_FALSE;
- pVoice->isLooping = DR_FALSE;
-
- dra_event_queue__schedule_event(&pVoice->pDevice->eventQueue, &pVoice->stopEvent);
-}
-
-dr_bool32 dra_voice_is_playing(dra_voice* pVoice)
-{
- if (pVoice == NULL) {
- return DR_FALSE;
- }
-
- return pVoice->isPlaying;
-}
-
-dr_bool32 dra_voice_is_looping(dra_voice* pVoice)
-{
- if (pVoice == NULL) {
- return DR_FALSE;
- }
-
- return pVoice->isLooping;
-}
-
-
-void dra_voice_set_volume(dra_voice* pVoice, float linearVolume)
-{
- if (pVoice == NULL) {
- return;
- }
-
- pVoice->linearVolume = linearVolume;
-}
-
-float dra_voice_get_volume(dra_voice* pVoice)
-{
- if (pVoice == NULL) {
- return 0;
- }
-
- return pVoice->linearVolume;
-}
-
-
-void dra_f32_to_f32(float* pOut, const float* pIn, size_t sampleCount)
-{
- memcpy(pOut, pIn, sampleCount * sizeof(float));
-}
-
-void dra_s32_to_f32(float* pOut, const dr_int32* pIn, size_t sampleCount)
-{
- // TODO: Try SSE-ifying this.
- for (size_t i = 0; i < sampleCount; ++i) {
- pOut[i] = pIn[i] / 2147483648.0f;
- }
-}
-
-void dra_s24_to_f32(float* pOut, const dr_uint8* pIn, size_t sampleCount)
-{
- // TODO: Try SSE-ifying this.
- for (size_t i = 0; i < sampleCount; ++i) {
- dr_uint8 s0 = pIn[i*3 + 0];
- dr_uint8 s1 = pIn[i*3 + 1];
- dr_uint8 s2 = pIn[i*3 + 2];
-
- dr_int32 sample32 = (dr_int32)((s0 << 8) | (s1 << 16) | (s2 << 24));
- pOut[i] = sample32 / 2147483648.0f;
- }
-}
-
-void dra_s16_to_f32(float* pOut, const dr_int16* pIn, size_t sampleCount)
-{
- // TODO: Try SSE-ifying this.
- for (size_t i = 0; i < sampleCount; ++i) {
- pOut[i] = pIn[i] / 32768.0f;
- }
-}
-
-void dra_u8_to_f32(float* pOut, const dr_uint8* pIn, size_t sampleCount)
-{
- // TODO: Try SSE-ifying this.
- for (size_t i = 0; i < sampleCount; ++i) {
- pOut[i] = (pIn[i] / 127.5f) - 1;
- }
-}
-
-
-// Generic sample format conversion function. To add basic, unoptimized support for a new format, just add it to this function.
-// Format-specific optimizations need to be implemented specifically for each format, at a higher level.
-void dra_to_f32(float* pOut, const void* pIn, size_t sampleCount, dra_format format)
-{
- switch (format)
- {
- case dra_format_f32:
- {
- dra_f32_to_f32(pOut, (float*)pIn, sampleCount);
- } break;
-
- case dra_format_s32:
- {
- dra_s32_to_f32(pOut, (dr_int32*)pIn, sampleCount);
- } break;
-
- case dra_format_s24:
- {
- dra_s24_to_f32(pOut, (dr_uint8*)pIn, sampleCount);
- } break;
-
- case dra_format_s16:
- {
- dra_s16_to_f32(pOut, (dr_int16*)pIn, sampleCount);
- } break;
-
- case dra_format_u8:
- {
- dra_u8_to_f32(pOut, (dr_uint8*)pIn, sampleCount);
- } break;
-
- default: break; // Unknown or unsupported format.
- }
-}
-
-
-// Notes on channel shuffling.
-//
-// Channels are shuffled frame-by-frame by first normalizing everything to floats. Then, a shuffling function is called to
-// shuffle the channels in a particular way depending on the destination and source channel assignments.
-void dra_shuffle_channels__generic_inc(float* pOut, const float* pIn, unsigned int channelsOut, unsigned int channelsIn)
-{
- // This is the generic function for taking a frame with a smaller number of channels and expanding it to a frame with
- // a greater number of channels. This just copies the first channelsIn samples to the output and silences the remaing
- // channels.
- assert(channelsOut > channelsIn);
-
- for (unsigned int i = 0; i < channelsIn; ++i) {
- pOut[i] = pIn[i];
- }
-
- // Silence the left over.
- for (unsigned int i = channelsIn; i < channelsOut; ++i) {
- pOut[i] = 0;
- }
-}
-
-void dra_shuffle_channels__generic_dec(float* pOut, const float* pIn, unsigned int channelsOut, unsigned int channelsIn)
-{
- // This is the opposite of dra_shuffle_channels__generic_inc() - it decreases the number of channels in the input stream
- // by simply stripping the excess channels.
- assert(channelsOut < channelsIn);
- (void)channelsIn;
-
- // Just copy the first channelsOut.
- for (unsigned int i = 0; i < channelsOut; ++i) {
- pOut[i] = pIn[i];
- }
-}
-
-void dra_shuffle_channels(float* pOut, const float* pIn, unsigned int channelsOut, unsigned int channelsIn)
-{
- assert(channelsOut != 0);
- assert(channelsIn != 0);
-
- if (channelsOut == channelsIn) {
- for (unsigned int i = 0; i < channelsOut; ++i) {
- pOut[i] = pIn[i];
- }
- } else {
- switch (channelsIn)
- {
- case 1:
- {
- // Mono input. This is a simple case - just copy the value of the mono channel to every output channel.
- for (unsigned int i = 0; i < channelsOut; ++i) {
- pOut[i] = pIn[0];
- }
- } break;
-
- case 2:
- {
- // Stereo input.
- if (channelsOut == 1)
- {
- // For mono output, just average.
- pOut[0] = (pIn[0] + pIn[1]) * 0.5f;
- }
- else
- {
- // TODO: Do a specialized implementation for all major formats, in particluar 5.1.
- dra_shuffle_channels__generic_inc(pOut, pIn, channelsOut, channelsIn);
- }
- } break;
-
- default:
- {
- if (channelsOut == 1)
- {
- // For mono output, just average each sample.
- float total = 0;
- for (unsigned int i = 0; i < channelsIn; ++i) {
- total += pIn[i];
- }
-
- pOut[0] = total / channelsIn;
- }
- else
- {
- if (channelsOut > channelsIn) {
- dra_shuffle_channels__generic_inc(pOut, pIn, channelsOut, channelsIn);
- } else {
- dra_shuffle_channels__generic_dec(pOut, pIn, channelsOut, channelsIn);
- }
- }
- } break;
- }
- }
-}
-
-float dra_voice__get_sample_rate_factor(dra_voice* pVoice)
-{
- if (pVoice == NULL) {
- return 1;
- }
-
- return pVoice->pDevice->sampleRate / (float)pVoice->sampleRate;
-}
-
-void dra_voice__unsignal_playback_events(dra_voice* pVoice)
-{
- // This function will be called when the voice has looped back to the start. In this case the playback notification events need
- // to be marked as unsignaled so that they're able to be fired again.
- for (size_t i = 0; i < pVoice->playbackEventCount; ++i) {
- pVoice->playbackEvents[i].hasBeenSignaled = DR_FALSE;
- }
-}
-
-float* dra_voice__next_frame(dra_voice* pVoice)
-{
- if (pVoice == NULL) {
- return NULL;
- }
-
-
- if (pVoice->format == dra_format_f32 && pVoice->sampleRate == pVoice->pDevice->sampleRate && pVoice->channels == pVoice->pDevice->channels)
- {
- // Fast path.
- if (!pVoice->isLooping && pVoice->currentReadPos == pVoice->frameCount) {
- return NULL; // At the end of a non-looping voice.
- }
-
- float* pOut = (float*)pVoice->pData + (pVoice->currentReadPos * pVoice->channels);
-
- pVoice->currentReadPos += 1;
- if (pVoice->currentReadPos == pVoice->frameCount && pVoice->isLooping) {
- pVoice->currentReadPos = 0;
- dra_voice__unsignal_playback_events(pVoice);
- }
-
- return pOut;
- }
- else
- {
- size_t bytesPerSample = dra_get_bytes_per_sample_by_format(pVoice->format);
-
- if (pVoice->sampleRate == pVoice->pDevice->sampleRate)
- {
- // Same sample rate. This path isn't ideal, but it's not too bad since there is no need for sample rate conversion.
- if (!pVoice->isLooping && pVoice->currentReadPos == pVoice->frameCount) {
- return NULL; // At the end of a non-looping voice.
- }
-
- float* pOut = pVoice->convertedFrame;
-
- unsigned int channelsIn = pVoice->channels;
- unsigned int channelsOut = pVoice->pDevice->channels;
- float tempFrame[DR_AUDIO_MAX_CHANNEL_COUNT];
- dr_uint64 sampleOffset = pVoice->currentReadPos * channelsIn;
-
- // The conversion is done differently depending on the format of the voice.
- if (pVoice->format == dra_format_f32) {
- dra_shuffle_channels(pOut, (float*)pVoice->pData + sampleOffset, channelsOut, channelsIn);
- } else {
- sampleOffset = pVoice->currentReadPos * (channelsIn * bytesPerSample);
- dra_to_f32(tempFrame, (dr_uint8*)pVoice->pData + sampleOffset, channelsIn, pVoice->format);
- dra_shuffle_channels(pOut, tempFrame, channelsOut, channelsIn);
- }
-
- pVoice->currentReadPos += 1;
- if (pVoice->currentReadPos == pVoice->frameCount && pVoice->isLooping) {
- pVoice->currentReadPos = 0;
- dra_voice__unsignal_playback_events(pVoice);
- }
-
- return pOut;
- }
- else
- {
- // Different sample rate. This is the truly slow path.
- unsigned int sampleRateIn = pVoice->sampleRate;
- unsigned int sampleRateOut = pVoice->pDevice->sampleRate;
- unsigned int channelsIn = pVoice->channels;
- unsigned int channelsOut = pVoice->pDevice->channels;
-
- float factor = (float)sampleRateOut / (float)sampleRateIn;
- float invfactor = 1 / factor;
-
- if (!pVoice->isLooping && pVoice->currentReadPos >= (pVoice->frameCount * factor)) {
- return NULL; // At the end of a non-looping voice.
- }
-
- float* pOut = pVoice->convertedFrame;
-
- if (pVoice->src.algorithm == dra_src_algorithm_linear) {
- // Linear filtering.
- float timeIn = pVoice->currentReadPos * invfactor;
- dr_uint64 prevFrameIndexIn = (dr_uint64)(timeIn);
- dr_uint64 nextFrameIndexIn = prevFrameIndexIn + 1;
- if (nextFrameIndexIn >= pVoice->frameCount) {
- nextFrameIndexIn = pVoice->frameCount-1;
- }
-
- if (prevFrameIndexIn != pVoice->src.data.linear.prevFrameIndex)
- {
- dr_uint64 sampleOffset = prevFrameIndexIn * (channelsIn * bytesPerSample);
- dra_to_f32(pVoice->src.data.linear.prevFrame, (dr_uint8*)pVoice->pData + sampleOffset, channelsIn, pVoice->format);
-
- sampleOffset = nextFrameIndexIn * (channelsIn * bytesPerSample);
- dra_to_f32(pVoice->src.data.linear.nextFrame, (dr_uint8*)pVoice->pData + sampleOffset, channelsIn, pVoice->format);
-
- pVoice->src.data.linear.prevFrameIndex = prevFrameIndexIn;
- }
-
- float alpha = timeIn - prevFrameIndexIn;
- float frame[DR_AUDIO_MAX_CHANNEL_COUNT];
- for (unsigned int i = 0; i < pVoice->channels; ++i) {
- frame[i] = dra_mixf(pVoice->src.data.linear.prevFrame[i], pVoice->src.data.linear.nextFrame[i], alpha);
- }
-
- dra_shuffle_channels(pOut, frame, channelsOut, channelsIn);
- }
-
-
- pVoice->currentReadPos += 1;
- if (pVoice->currentReadPos >= (pVoice->frameCount * factor) && pVoice->isLooping) {
- pVoice->currentReadPos = 0;
- dra_voice__unsignal_playback_events(pVoice);
- }
-
- return pOut;
- }
- }
-}
-
-size_t dra_voice__next_frames(dra_voice* pVoice, size_t frameCount, float* pSamplesOut)
-{
- // TODO: Check for the fast path and do a bulk copy rather than frame-by-frame. Don't forget playback event handling.
-
- size_t framesRead = 0;
-
- dr_uint64 prevReadPosLocal = pVoice->currentReadPos * pVoice->channels;
-
- float* pNextFrame = NULL;
- while ((framesRead < frameCount) && (pNextFrame = dra_voice__next_frame(pVoice)) != NULL) {
- memcpy(pSamplesOut, pNextFrame, pVoice->pDevice->channels * sizeof(float));
- pSamplesOut += pVoice->pDevice->channels;
- framesRead += 1;
- }
-
- float sampleRateFactor = dra_voice__get_sample_rate_factor(pVoice);
- dr_uint64 totalSampleCount = (dr_uint64)((pVoice->frameCount * pVoice->channels) * sampleRateFactor);
-
- // Now we need to check if we've got past any notification events and post events for them if so.
- dr_uint64 currentReadPosLocal = (prevReadPosLocal + (framesRead * pVoice->channels)) % totalSampleCount;
- for (size_t i = 0; i < pVoice->playbackEventCount; ++i) {
- dra__event* pEvent = &pVoice->playbackEvents[i];
- if (!pEvent->hasBeenSignaled && pEvent->sampleIndex*sampleRateFactor <= currentReadPosLocal) {
- dra_event_queue__schedule_event(&pVoice->pDevice->eventQueue, pEvent); // <-- TODO: Check that this really needs to be scheduled. Can probably call it directly and avoid a mutex lock/unlock.
- pEvent->hasBeenSignaled = DR_TRUE;
- }
- }
-
- return framesRead;
-}
-
-
-void dra_voice_set_on_stop(dra_voice* pVoice, dra_event_proc proc, void* pUserData)
-{
- if (pVoice == NULL) {
- return;
- }
-
- pVoice->stopEvent.id = DR_AUDIO_EVENT_ID_STOP;
- pVoice->stopEvent.pUserData = pUserData;
- pVoice->stopEvent.sampleIndex = 0;
- pVoice->stopEvent.proc = proc;
- pVoice->stopEvent.pVoice = pVoice;
-}
-
-void dra_voice_set_on_play(dra_voice* pVoice, dra_event_proc proc, void* pUserData)
-{
- if (pVoice == NULL) {
- return;
- }
-
- pVoice->playEvent.id = DR_AUDIO_EVENT_ID_PLAY;
- pVoice->playEvent.pUserData = pUserData;
- pVoice->playEvent.sampleIndex = 0;
- pVoice->playEvent.proc = proc;
- pVoice->playEvent.pVoice = pVoice;
-}
-
-dr_bool32 dra_voice_add_playback_event(dra_voice* pVoice, dr_uint64 sampleIndex, dr_uint64 eventID, dra_event_proc proc, void* pUserData)
-{
- if (pVoice == NULL) {
- return DR_FALSE;
- }
-
- if (pVoice->playbackEventCount >= DR_AUDIO_MAX_EVENT_COUNT) {
- return DR_FALSE;
- }
-
- pVoice->playbackEvents[pVoice->playbackEventCount].id = eventID;
- pVoice->playbackEvents[pVoice->playbackEventCount].pUserData = pUserData;
- pVoice->playbackEvents[pVoice->playbackEventCount].sampleIndex = sampleIndex;
- pVoice->playbackEvents[pVoice->playbackEventCount].proc = proc;
- pVoice->playbackEvents[pVoice->playbackEventCount].pVoice = pVoice;
-
- pVoice->playbackEventCount += 1;
- return DR_TRUE;
-}
-
-void dra_voice_remove_playback_event(dra_voice* pVoice, dr_uint64 eventID)
-{
- if (pVoice == NULL) {
- return;
- }
-
- for (size_t i = 0; i < pVoice->playbackEventCount; /* DO NOTHING */) {
- if (pVoice->playbackEvents[i].id == eventID) {
- memmove(&pVoice->playbackEvents[i], &pVoice->playbackEvents[i + 1], (pVoice->playbackEventCount - (i+1)) * sizeof(dra__event));
- pVoice->playbackEventCount -= 1;
- } else {
- i += 1;
- }
- }
-}
-
-
-dr_uint64 dra_voice_get_playback_position(dra_voice* pVoice)
-{
- if (pVoice == NULL) {
- return 0;
- }
-
- return (dr_uint64)((pVoice->currentReadPos * pVoice->channels) / dra_voice__get_sample_rate_factor(pVoice));
-}
-
-void dra_voice_set_playback_position(dra_voice* pVoice, dr_uint64 sampleIndex)
-{
- if (pVoice == NULL) {
- return;
- }
-
- // When setting the playback position it's important to consider sample-rate conversion. Sample rate conversion will often depend on
- // previous and next frames in order to calculate the next frame. Therefore, depending on the type of SRC we're using, we'll need to
- // seek a few frames earlier and then re-fill the delay-line buffer used for a particular SRC algorithm.
- dr_uint64 localFramePos = sampleIndex / pVoice->channels;
- pVoice->currentReadPos = (dr_uint64)(localFramePos * dra_voice__get_sample_rate_factor(pVoice));
-
- if (pVoice->sampleRate != pVoice->pDevice->sampleRate) {
- if (pVoice->src.algorithm == dra_src_algorithm_linear) {
- // Linear filtering just requires the previous frame. However, this is handled at mixing time for linear SRC so all we need to
- // do is ensure the mixing function is aware that the previous frame need to be re-read. This is done by simply resetting the
- // variable the mixer uses to determine whether or not the previous frame needs to be re-read.
- pVoice->src.data.linear.prevFrameIndex = 0;
- }
- }
-
- // TODO: Normalize the hasBeenSignaled properties of events.
-}
-
-
-void* dra_voice_get_buffer_ptr_by_sample(dra_voice* pVoice, dr_uint64 sample)
-{
- if (pVoice == NULL) {
- return NULL;
- }
-
- dr_uint64 totalSampleCount = pVoice->frameCount * pVoice->channels;
- if (sample > totalSampleCount) {
- return NULL;
- }
-
- return pVoice->pData + (sample * dra_get_bytes_per_sample_by_format(pVoice->format));
-}
-
-void dra_voice_write_silence(dra_voice* pVoice, dr_uint64 sampleOffset, dr_uint64 sampleCount)
-{
- void* pData = dra_voice_get_buffer_ptr_by_sample(pVoice, sampleOffset);
- if (pData == NULL) {
- return;
- }
-
- dr_uint64 totalSamplesRemaining = (pVoice->frameCount * pVoice->channels) - sampleOffset;
- if (sampleCount > totalSamplesRemaining) {
- sampleCount = totalSamplesRemaining;
- }
-
- memset(pData, 0, (size_t)(sampleCount * dra_get_bytes_per_sample_by_format(pVoice->format)));
-}
-
-
-
-
-
-
-//// Other APIs ////
-
-void dra_free(void* p)
-{
- free(p);
-}
-
-unsigned int dra_get_bits_per_sample_by_format(dra_format format)
-{
- unsigned int lookup[] = {
- 8, // dra_format_u8
- 16, // dra_format_s16
- 24, // dra_format_s24
- 32, // dra_format_s32
- 32 // dra_format_f32
- };
-
- return lookup[format];
-}
-
-unsigned int dra_get_bytes_per_sample_by_format(dra_format format)
-{
- return dra_get_bits_per_sample_by_format(format) / 8;
-}
-
-
-//// STDIO ////
-
-#ifndef DR_AUDIO_NO_STDIO
-static FILE* dra__fopen(const char* filePath)
-{
- FILE* pFile;
-#ifdef _MSC_VER
- if (fopen_s(&pFile, filePath, "rb") != 0) {
- return NULL;
- }
-#else
- pFile = fopen(filePath, "rb");
- if (pFile == NULL) {
- return NULL;
- }
-#endif
-
- return (FILE*)pFile;
-}
-#endif //DR_AUDIO_NO_STDIO
-
-
-//// Decoder APIs ////
-
-#ifdef DR_AUDIO_HAS_WAV
-size_t dra_decoder_on_read__wav(void* pUserData, void* pDataOut, size_t bytesToRead)
-{
- dra_decoder* pDecoder = (dra_decoder*)pUserData;
- assert(pDecoder != NULL);
- assert(pDecoder->onRead != NULL);
-
- return pDecoder->onRead(pDecoder->pUserData, pDataOut, bytesToRead);
-}
-drwav_bool32 dra_decoder_on_seek__wav(void* pUserData, int offset, drwav_seek_origin origin)
-{
- dra_decoder* pDecoder = (dra_decoder*)pUserData;
- assert(pDecoder != NULL);
- assert(pDecoder->onSeek != NULL);
-
- return pDecoder->onSeek(pDecoder->pUserData, offset, (origin == drwav_seek_origin_start) ? dra_seek_origin_start : dra_seek_origin_current);
-}
-
-void dra_decoder_on_delete__wav(void* pBackendDecoder)
-{
- drwav* pWav = (drwav*)pBackendDecoder;
- assert(pWav != NULL);
-
- drwav_close(pWav);
-}
-
-dr_uint64 dra_decoder_on_read_samples__wav(void* pBackendDecoder, dr_uint64 samplesToRead, float* pSamplesOut)
-{
- drwav* pWav = (drwav*)pBackendDecoder;
- assert(pWav != NULL);
-
- return drwav_read_f32(pWav, samplesToRead, pSamplesOut);
-}
-
-dr_bool32 dra_decoder_on_seek_samples__wav(void* pBackendDecoder, dr_uint64 sample)
-{
- drwav* pWav = (drwav*)pBackendDecoder;
- assert(pWav != NULL);
-
- return drwav_seek_to_sample(pWav, sample);
-}
-
-
-void dra_decoder_init__wav(dra_decoder* pDecoder, drwav* pWav)
-{
- assert(pDecoder != NULL);
- assert(pWav != NULL);
-
- pDecoder->channels = pWav->channels;
- pDecoder->sampleRate = pWav->sampleRate;
- pDecoder->totalSampleCount = pWav->totalSampleCount;
-
- pDecoder->pBackendDecoder = pWav;
- pDecoder->onDelete = dra_decoder_on_delete__wav;
- pDecoder->onReadSamples = dra_decoder_on_read_samples__wav;
- pDecoder->onSeekSamples = dra_decoder_on_seek_samples__wav;
-}
-
-dr_bool32 dra_decoder_open__wav(dra_decoder* pDecoder)
-{
- drwav* pWav = drwav_open(dra_decoder_on_read__wav, dra_decoder_on_seek__wav, pDecoder);
- if (pWav == NULL) {
- return DR_FALSE;
- }
-
- dra_decoder_init__wav(pDecoder, pWav);
- return DR_TRUE;
-}
-
-dr_bool32 dra_decoder_open_memory__wav(dra_decoder* pDecoder, const void* pData, size_t dataSize)
-{
- drwav* pWav = drwav_open_memory(pData, dataSize);
- if (pWav == NULL) {
- return DR_FALSE;
- }
-
- dra_decoder_init__wav(pDecoder, pWav);
- return DR_TRUE;
-}
-
-#ifdef DR_AUDIO_HAS_WAV_STDIO
-dr_bool32 dra_decoder_open_file__wav(dra_decoder* pDecoder, const char* filePath)
-{
- drwav* pWav = drwav_open_file(filePath);
- if (pWav == NULL) {
- return DR_FALSE;
- }
-
- dra_decoder_init__wav(pDecoder, pWav);
- return DR_TRUE;
-}
-#endif
-#endif //WAV
-
-#ifdef DR_AUDIO_HAS_FLAC
-size_t dra_decoder_on_read__flac(void* pUserData, void* pDataOut, size_t bytesToRead)
-{
- dra_decoder* pDecoder = (dra_decoder*)pUserData;
- assert(pDecoder != NULL);
- assert(pDecoder->onRead != NULL);
-
- return pDecoder->onRead(pDecoder->pUserData, pDataOut, bytesToRead);
-}
-drflac_bool32 dra_decoder_on_seek__flac(void* pUserData, int offset, drflac_seek_origin origin)
-{
- dra_decoder* pDecoder = (dra_decoder*)pUserData;
- assert(pDecoder != NULL);
- assert(pDecoder->onSeek != NULL);
-
- return pDecoder->onSeek(pDecoder->pUserData, offset, (origin == drflac_seek_origin_start) ? dra_seek_origin_start : dra_seek_origin_current);
-}
-
-void dra_decoder_on_delete__flac(void* pBackendDecoder)
-{
- drflac* pFlac = (drflac*)pBackendDecoder;
- assert(pFlac != NULL);
-
- drflac_close(pFlac);
-}
-
-dr_uint64 dra_decoder_on_read_samples__flac(void* pBackendDecoder, dr_uint64 samplesToRead, float* pSamplesOut)
-{
- drflac* pFlac = (drflac*)pBackendDecoder;
- assert(pFlac != NULL);
-
- dr_uint64 samplesRead = drflac_read_s32(pFlac, samplesToRead, (dr_int32*)pSamplesOut);
-
- dra_s32_to_f32(pSamplesOut, (dr_int32*)pSamplesOut, (size_t)samplesRead);
- return samplesRead;
-}
-
-dr_bool32 dra_decoder_on_seek_samples__flac(void* pBackendDecoder, dr_uint64 sample)
-{
- drflac* pFlac = (drflac*)pBackendDecoder;
- assert(pFlac != NULL);
-
- return drflac_seek_to_sample(pFlac, sample);
-}
-
-
-void dra_decoder_init__flac(dra_decoder* pDecoder, drflac* pFlac)
-{
- assert(pDecoder != NULL);
- assert(pFlac != NULL);
-
- pDecoder->channels = pFlac->channels;
- pDecoder->sampleRate = pFlac->sampleRate;
- pDecoder->totalSampleCount = pFlac->totalSampleCount;
-
- pDecoder->pBackendDecoder = pFlac;
- pDecoder->onDelete = dra_decoder_on_delete__flac;
- pDecoder->onReadSamples = dra_decoder_on_read_samples__flac;
- pDecoder->onSeekSamples = dra_decoder_on_seek_samples__flac;
-}
-
-dr_bool32 dra_decoder_open__flac(dra_decoder* pDecoder)
-{
- drflac* pFlac = drflac_open(dra_decoder_on_read__flac, dra_decoder_on_seek__flac, pDecoder);
- if (pFlac == NULL) {
- return DR_FALSE;
- }
-
- dra_decoder_init__flac(pDecoder, pFlac);
- return DR_TRUE;
-}
-
-dr_bool32 dra_decoder_open_memory__flac(dra_decoder* pDecoder, const void* pData, size_t dataSize)
-{
- drflac* pFlac = drflac_open_memory(pData, dataSize);
- if (pFlac == NULL) {
- return DR_FALSE;
- }
-
- dra_decoder_init__flac(pDecoder, pFlac);
- return DR_TRUE;
-}
-
-#ifdef DR_AUDIO_HAS_FLAC_STDIO
-dr_bool32 dra_decoder_open_file__flac(dra_decoder* pDecoder, const char* filePath)
-{
- drflac* pFlac = drflac_open_file(filePath);
- if (pFlac == NULL) {
- return DR_FALSE;
- }
-
- dra_decoder_init__flac(pDecoder, pFlac);
- return DR_TRUE;
-}
-#endif
-#endif //FLAC
-
-#ifdef DR_AUDIO_HAS_VORBIS
-void dra_decoder_on_delete__vorbis(void* pBackendDecoder)
-{
- stb_vorbis* pVorbis = (stb_vorbis*)pBackendDecoder;
- assert(pVorbis != NULL);
-
- stb_vorbis_close(pVorbis);
-}
-
-dr_uint64 dra_decoder_on_read_samples__vorbis(void* pBackendDecoder, dr_uint64 samplesToRead, float* pSamplesOut)
-{
- stb_vorbis* pVorbis = (stb_vorbis*)pBackendDecoder;
- assert(pVorbis != NULL);
-
- stb_vorbis_info info = stb_vorbis_get_info(pVorbis);
- return (dr_uint64)stb_vorbis_get_samples_float_interleaved(pVorbis, info.channels, pSamplesOut, (int)samplesToRead) * info.channels;
-}
-
-dr_bool32 dra_decoder_on_seek_samples__vorbis(void* pBackendDecoder, dr_uint64 sample)
-{
- stb_vorbis* pVorbis = (stb_vorbis*)pBackendDecoder;
- assert(pVorbis != NULL);
-
- return stb_vorbis_seek(pVorbis, (unsigned int)sample) != 0;
-}
-
-
-void dra_decoder_init__vorbis(dra_decoder* pDecoder, stb_vorbis* pVorbis)
-{
- assert(pDecoder != NULL);
- assert(pVorbis != NULL);
-
- stb_vorbis_info info = stb_vorbis_get_info(pVorbis);
-
- pDecoder->channels = info.channels;
- pDecoder->sampleRate = info.sample_rate;
- pDecoder->totalSampleCount = stb_vorbis_stream_length_in_samples(pVorbis) * info.channels;
-
- pDecoder->pBackendDecoder = pVorbis;
- pDecoder->onDelete = dra_decoder_on_delete__vorbis;
- pDecoder->onReadSamples = dra_decoder_on_read_samples__vorbis;
- pDecoder->onSeekSamples = dra_decoder_on_seek_samples__vorbis;
-}
-
-dr_bool32 dra_decoder_open__vorbis(dra_decoder* pDecoder)
-{
- // TODO: Add support for the push API.
-
- // Not currently supporting callback based decoding.
- (void)pDecoder;
- return DR_FALSE;
-}
-
-dr_bool32 dra_decoder_open_memory__vorbis(dra_decoder* pDecoder, const void* pData, size_t dataSize)
-{
- stb_vorbis* pVorbis = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL);
- if (pVorbis == NULL) {
- return DR_FALSE;
- }
-
- dra_decoder_init__vorbis(pDecoder, pVorbis);
- return DR_TRUE;
-}
-
-#ifdef DR_AUDIO_HAS_VORBIS_STDIO
-dr_bool32 dra_decoder_open_file__vorbis(dra_decoder* pDecoder, const char* filePath)
-{
- stb_vorbis* pVorbis = stb_vorbis_open_filename(filePath, NULL, NULL);
- if (pVorbis == NULL) {
- return DR_FALSE;
- }
-
- dra_decoder_init__vorbis(pDecoder, pVorbis);
- return DR_TRUE;
-}
-#endif
-#endif //Vorbis
-
-dra_result dra_decoder_open(dra_decoder* pDecoder, dra_decoder_on_read_proc onRead, dra_decoder_on_seek_proc onSeek, void* pUserData)
-{
- if (pDecoder == NULL) return DRA_RESULT_INVALID_ARGS;
- memset(pDecoder, 0, sizeof(*pDecoder));
-
- if (onRead == NULL || onSeek == NULL) return DRA_RESULT_INVALID_ARGS;
-
- pDecoder->onRead = onRead;
- pDecoder->onSeek = onSeek;
- pDecoder->pUserData = pUserData;
-
-#ifdef DR_AUDIO_HAS_WAV_STDIO
- if (dra_decoder_open__wav(pDecoder)) {
- return DRA_RESULT_SUCCESS;
- }
- onSeek(pUserData, 0, dra_seek_origin_start);
-#endif
-#ifdef DR_AUDIO_HAS_FLAC_STDIO
- if (dra_decoder_open__flac(pDecoder)) {
- return DRA_RESULT_SUCCESS;
- }
- onSeek(pUserData, 0, dra_seek_origin_start);
-#endif
-#ifdef DR_AUDIO_HAS_VORBIS_STDIO
- if (dra_decoder_open__vorbis(pDecoder)) {
- return DRA_RESULT_SUCCESS;
- }
- onSeek(pUserData, 0, dra_seek_origin_start);
-#endif
-
- // If we get here it means we were unable to open a decoder.
- return DRA_RESULT_NO_DECODER;
-}
-
-
-size_t dra_decoder__on_read_memory(void* pUserData, void* pDataOut, size_t bytesToRead)
-{
- dra__memory_stream* memoryStream = (dra__memory_stream*)pUserData;
- assert(memoryStream != NULL);
- assert(memoryStream->dataSize >= memoryStream->currentReadPos);
-
- size_t bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos;
- if (bytesToRead > bytesRemaining) {
- bytesToRead = bytesRemaining;
- }
-
- if (bytesToRead > 0) {
- memcpy(pDataOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead);
- memoryStream->currentReadPos += bytesToRead;
- }
-
- return bytesToRead;
-}
-dr_bool32 dra_decoder__on_seek_memory(void* pUserData, int offset, dra_seek_origin origin)
-{
- dra__memory_stream* memoryStream = (dra__memory_stream*)pUserData;
- assert(memoryStream != NULL);
- assert(offset > 0 || (offset == 0 && origin == dra_seek_origin_start));
-
- if (origin == dra_seek_origin_current) {
- if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) {
- memoryStream->currentReadPos += offset;
- } else {
- memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward.
- }
- } else {
- if ((dr_uint32)offset <= memoryStream->dataSize) {
- memoryStream->currentReadPos = offset;
- } else {
- memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward.
- }
- }
-
- return DR_TRUE;
-}
-
-dra_result dra_decoder_open_memory(dra_decoder* pDecoder, const void* pData, size_t dataSize)
-{
- if (pDecoder == NULL) return DRA_RESULT_INVALID_ARGS;
- memset(pDecoder, 0, sizeof(*pDecoder));
-
- if (pData == NULL || dataSize == 0) return DRA_RESULT_INVALID_ARGS;
-
- // Prefer the backend's native APIs.
-#if defined(DR_AUDIO_HAS_WAV)
- if (dra_decoder_open_memory__wav(pDecoder, pData, dataSize)) {
- return DRA_RESULT_SUCCESS;
- }
-#endif
-#if defined(DR_AUDIO_HAS_FLAC)
- if (dra_decoder_open_memory__flac(pDecoder, pData, dataSize)) {
- return DRA_RESULT_SUCCESS;
- }
-#endif
-#if defined(DR_AUDIO_HAS_VORBIS)
- if (dra_decoder_open_memory__vorbis(pDecoder, pData, dataSize)) {
- return DRA_RESULT_SUCCESS;
- }
-#endif
-
- // If we get here it means the backend does not have a native memory loader so we'll need to it ourselves.
- dra__memory_stream memoryStream;
- memoryStream.data = (const unsigned char*)pData;
- memoryStream.dataSize = dataSize;
- memoryStream.currentReadPos = 0;
- dra_result result = dra_decoder_open(pDecoder, dra_decoder__on_read_memory, dra_decoder__on_seek_memory, &memoryStream);
- if (result != DRA_RESULT_SUCCESS) {
- return result;
- }
-
- pDecoder->memoryStream = memoryStream;
- pDecoder->pUserData = &pDecoder->memoryStream;
-
- return DRA_RESULT_SUCCESS;
-}
-
-
-#ifndef DR_AUDIO_NO_STDIO
-size_t dra_decoder__on_read_stdio(void* pUserData, void* pDataOut, size_t bytesToRead)
-{
- return fread(pDataOut, 1, bytesToRead, (FILE*)pUserData);
-}
-dr_bool32 dra_decoder__on_seek_stdio(void* pUserData, int offset, dra_seek_origin origin)
-{
- return fseek((FILE*)pUserData, offset, (origin == dra_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
-}
-
-dra_result dra_decoder_open_file(dra_decoder* pDecoder, const char* filePath)
-{
- if (pDecoder == NULL) return DRA_RESULT_INVALID_ARGS;
- memset(pDecoder, 0, sizeof(*pDecoder));
-
- if (filePath == NULL) return DR_FALSE;
-
- // When opening a decoder from a file it's preferrable to use the backend's native file IO APIs if it has them.
-#if defined(DR_AUDIO_HAS_WAV) && defined(DR_AUDIO_HAS_WAV_STDIO)
- if (dra_decoder_open_file__wav(pDecoder, filePath)) {
- return DRA_RESULT_SUCCESS;
- }
-#endif
-#if defined(DR_AUDIO_HAS_FLAC) && defined(DR_AUDIO_HAS_FLAC_STDIO)
- if (dra_decoder_open_file__flac(pDecoder, filePath)) {
- return DRA_RESULT_SUCCESS;
- }
-#endif
-#if defined(DR_AUDIO_HAS_VORBIS) && defined(DR_AUDIO_HAS_VORBIS_STDIO)
- if (dra_decoder_open_file__vorbis(pDecoder, filePath)) {
- return DRA_RESULT_SUCCESS;
- }
-#endif
-
- // If we get here it means we were unable to open the decoder using any of the backends' native file IO
- // APIs. In this case we fall back to a generic method.
- FILE* pFile = dra__fopen(filePath);
- if (pFile == NULL) {
- return DRA_RESULT_FAILED_TO_OPEN_FILE;
- }
-
- dra_result result = dra_decoder_open(pDecoder, dra_decoder__on_read_stdio, dra_decoder__on_seek_stdio, pFile);
- if (result != DRA_RESULT_SUCCESS) {
- fclose(pFile);
- return result;
- }
-
- return DRA_RESULT_SUCCESS;
-}
-#endif
-
-void dra_decoder_close(dra_decoder* pDecoder)
-{
- if (pDecoder == NULL) {
- return;
- }
-
- if (pDecoder->onDelete) {
- pDecoder->onDelete(pDecoder->pBackendDecoder);
- }
-
-#ifndef DR_AUDIO_NO_STDIO
- if (pDecoder->onRead == dra_decoder__on_read_stdio) {
- fclose((FILE*)pDecoder->pUserData);
- }
-#endif
-}
-
-dr_uint64 dra_decoder_read_f32(dra_decoder* pDecoder, dr_uint64 samplesToRead, float* pSamplesOut)
-{
- if (pDecoder == NULL || pSamplesOut == NULL) {
- return 0;
- }
-
- return pDecoder->onReadSamples(pDecoder->pBackendDecoder, samplesToRead, pSamplesOut);
-}
-
-dr_bool32 dra_decoder_seek_to_sample(dra_decoder* pDecoder, dr_uint64 sample)
-{
- if (pDecoder == NULL) {
- return DR_FALSE;
- }
-
- return pDecoder->onSeekSamples(pDecoder->pBackendDecoder, sample);
-}
-
-
-float* dra_decoder__full_decode_and_close(dra_decoder* pDecoder, unsigned int* channelsOut, unsigned int* sampleRateOut, dr_uint64* totalSampleCountOut)
-{
- assert(pDecoder != NULL);
-
- float* pSampleData = NULL;
- dr_uint64 totalSampleCount = pDecoder->totalSampleCount;
-
- if (totalSampleCount == 0)
- {
- float buffer[4096];
-
- size_t sampleDataBufferSize = sizeof(buffer);
- pSampleData = (float*)malloc(sampleDataBufferSize);
- if (pSampleData == NULL) {
- goto on_error;
- }
-
- dr_uint64 samplesRead;
- while ((samplesRead = (dr_uint64)dra_decoder_read_f32(pDecoder, sizeof(buffer)/sizeof(buffer[0]), buffer)) > 0)
- {
- if (((totalSampleCount + samplesRead) * sizeof(float)) > sampleDataBufferSize) {
- sampleDataBufferSize *= 2;
- float* pNewSampleData = (float*)realloc(pSampleData, sampleDataBufferSize);
- if (pNewSampleData == NULL) {
- free(pSampleData);
- goto on_error;
- }
-
- pSampleData = pNewSampleData;
- }
-
- memcpy(pSampleData + totalSampleCount, buffer, (size_t)(samplesRead*sizeof(float)));
- totalSampleCount += samplesRead;
- }
-
- // At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to
- // protect those ears from random noise!
- memset(pSampleData + totalSampleCount, 0, (size_t)(sampleDataBufferSize - totalSampleCount*sizeof(float)));
- }
- else
- {
- dr_uint64 dataSize = totalSampleCount * sizeof(float);
- if (dataSize > SIZE_MAX) {
- goto on_error; // The decoded data is too big.
- }
-
- pSampleData = (float*)malloc((size_t)dataSize); // <-- Safe cast as per the check above.
- if (pSampleData == NULL) {
- goto on_error;
- }
-
- dr_uint64 samplesDecoded = dra_decoder_read_f32(pDecoder, pDecoder->totalSampleCount, pSampleData);
- if (samplesDecoded != pDecoder->totalSampleCount) {
- free(pSampleData);
- goto on_error; // Something went wrong when decoding the FLAC stream.
- }
- }
-
-
- if (channelsOut) *channelsOut = pDecoder->channels;
- if (sampleRateOut) *sampleRateOut = pDecoder->sampleRate;
- if (totalSampleCountOut) *totalSampleCountOut = totalSampleCount;
-
- dra_decoder_close(pDecoder);
- return pSampleData;
-
-on_error:
- dra_decoder_close(pDecoder);
- return NULL;
-}
-
-float* dra_decoder_open_and_decode_f32(dra_decoder_on_read_proc onRead, dra_decoder_on_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, dr_uint64* totalSampleCount)
-{
- // Safety.
- if (channels) *channels = 0;
- if (sampleRate) *sampleRate = 0;
- if (totalSampleCount) *totalSampleCount = 0;
-
- dra_decoder decoder;
- dra_result result = dra_decoder_open(&decoder, onRead, onSeek, pUserData);
- if (result != DRA_RESULT_SUCCESS) {
- return NULL;
- }
-
- return dra_decoder__full_decode_and_close(&decoder, channels, sampleRate, totalSampleCount);
-}
-
-float* dra_decoder_open_and_decode_memory_f32(const void* pData, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, dr_uint64* totalSampleCount)
-{
- // Safety.
- if (channels) *channels = 0;
- if (sampleRate) *sampleRate = 0;
- if (totalSampleCount) *totalSampleCount = 0;
-
- dra_decoder decoder;
- dra_result result = dra_decoder_open_memory(&decoder, pData, dataSize);
- if (result != DRA_RESULT_SUCCESS) {
- return NULL;
- }
-
- return dra_decoder__full_decode_and_close(&decoder, channels, sampleRate, totalSampleCount);
-}
-
-#ifndef DR_AUDIO_NO_STDIO
-float* dra_decoder_open_and_decode_file_f32(const char* filePath, unsigned int* channels, unsigned int* sampleRate, dr_uint64* totalSampleCount)
-{
- // Safety.
- if (channels) *channels = 0;
- if (sampleRate) *sampleRate = 0;
- if (totalSampleCount) *totalSampleCount = 0;
-
- dra_decoder decoder;
- dra_result result = dra_decoder_open_file(&decoder, filePath);
- if (result != DRA_RESULT_SUCCESS) {
- return NULL;
- }
-
- return dra_decoder__full_decode_and_close(&decoder, channels, sampleRate, totalSampleCount);
-}
-#endif
-
-
-
-//// High Level Helper APIs ////
-
-#ifndef DR_AUDIO_NO_STDIO
-dra_result dra_voice_create_from_file(dra_device* pDevice, const char* filePath, dra_voice** ppVoice)
-{
- if (pDevice == NULL || filePath == NULL) return DRA_RESULT_INVALID_ARGS;
-
- unsigned int channels;
- unsigned int sampleRate;
- dr_uint64 totalSampleCount;
- float* pSampleData = dra_decoder_open_and_decode_file_f32(filePath, &channels, &sampleRate, &totalSampleCount);
- if (pSampleData == NULL) {
- return DRA_RESULT_UNKNOWN_ERROR;
- }
-
- dra_result result = dra_voice_create(pDevice, dra_format_f32, channels, sampleRate, (size_t)totalSampleCount * sizeof(float), pSampleData, ppVoice);
-
- free(pSampleData);
- return result;
-}
-#endif
-
-
-//// High Level World APIs ////
-
-dra_sound_world* dra_sound_world_create(dra_device* pPlaybackDevice)
-{
- dra_sound_world* pWorld = (dra_sound_world*)calloc(1, sizeof(*pWorld));
- if (pWorld == NULL) {
- goto on_error;
- }
-
- pWorld->pPlaybackDevice = pPlaybackDevice;
- if (pWorld->pPlaybackDevice == NULL) {
- dra_result result = dra_device_create(NULL, dra_device_type_playback, &pWorld->pPlaybackDevice);
- if (result != DRA_RESULT_SUCCESS) {
- return NULL;
- }
-
- pWorld->ownsPlaybackDevice = DR_TRUE;
- }
-
-
-
-
- return pWorld;
-
-
-on_error:
- dra_sound_world_delete(pWorld);
- return NULL;
-}
-
-void dra_sound_world_delete(dra_sound_world* pWorld)
-{
- if (pWorld == NULL) {
- return;
- }
-
- if (pWorld->ownsPlaybackDevice) {
- dra_device_delete(pWorld->pPlaybackDevice);
- }
-
- free(pWorld);
-}
-
-
-void dra_sound_world__on_inline_sound_stop(dr_uint64 eventID, void* pUserData)
-{
- (void)eventID;
-
- dra_sound* pSound = (dra_sound*)pUserData;
- assert(pSound != NULL);
-
- dra_sound_delete(pSound);
-}
-
-void dra_sound_world_play_inline(dra_sound_world* pWorld, dra_sound_desc* pDesc, dra_mixer* pMixer)
-{
- if (pWorld == NULL || pDesc == NULL) {
- return;
- }
-
- // An inline sound is just like any other, except it never loops and it's deleted automatically when it stops playing. Therefore what
- // we need to do is attach an event handler to the voice's stop callback which is where the sound will be deleted.
- dra_sound* pSound = dra_sound_create(pWorld, pDesc);
- if (pSound == NULL) {
- return;
- }
-
- if (pMixer != NULL) {
- dra_sound_attach_to_mixer(pSound, pMixer);
- }
-
- dra_voice_set_on_stop(pSound->pVoice, dra_sound_world__on_inline_sound_stop, pSound);
- dra_sound_play(pSound, DR_FALSE);
-}
-
-void dra_sound_world_play_inline_3f(dra_sound_world* pWorld, dra_sound_desc* pDesc, dra_mixer* pMixer, float xPos, float yPos, float zPos)
-{
- if (pWorld == NULL || pDesc == NULL) {
- return;
- }
-
- // TODO: Implement 3D positioning once the effects framework is in.
- (void)xPos;
- (void)yPos;
- (void)zPos;
- dra_sound_world_play_inline(pWorld, pDesc, pMixer);
-}
-
-void dra_sound_world_stop_all_sounds(dra_sound_world* pWorld)
-{
- if (pWorld == NULL) {
- return;
- }
-
- // When sounds are stopped the on_stop event handler will be fired. It is possible for the implementation of this event handler to
- // delete the sound, so we'll first need to gather the sounds into a separate list.
- size_t voiceCount = dra_mixer_count_attached_voices_recursive(pWorld->pPlaybackDevice->pMasterMixer);
- if (voiceCount == 0) {
- return;
- }
-
- dra_voice** ppVoices = (dra_voice**)malloc(voiceCount * sizeof(*ppVoices));
- if (ppVoices == NULL) {
- return;
- }
-
- voiceCount = dra_mixer_gather_attached_voices_recursive(pWorld->pPlaybackDevice->pMasterMixer, ppVoices);
- assert(voiceCount != 0);
-
- for (size_t iVoice = 0; iVoice < voiceCount; ++iVoice) {
- dra_voice_stop(ppVoices[iVoice]);
- }
-
- free(ppVoices);
-}
-
-void dra_sound_world_set_listener_position(dra_sound_world* pWorld, float xPos, float yPos, float zPos)
-{
- if (pWorld == NULL) {
- return;
- }
-
- // TODO: Implement me.
- (void)xPos;
- (void)yPos;
- (void)zPos;
-}
-
-void dra_sound_world_set_listener_orientation(dra_sound_world* pWorld, float xForward, float yForward, float zForward, float xUp, float yUp, float zUp)
-{
- if (pWorld == NULL) {
- return;
- }
-
- // TODO: Implement me.
- (void)xForward;
- (void)yForward;
- (void)zForward;
- (void)xUp;
- (void)yUp;
- (void)zUp;
-}
-
-
-dr_bool32 dra_sound__is_streaming(dra_sound* pSound)
-{
- assert(pSound != NULL);
- return pSound->desc.dataSize == 0 || pSound->desc.pData == NULL;
-}
-
-dr_bool32 dra_sound__read_next_chunk(dra_sound* pSound, dr_uint64 outputSampleOffset)
-{
- assert(pSound != NULL);
- if (pSound->desc.onRead == NULL) {
- return DR_FALSE;
- }
-
- dr_uint64 chunkSizeInSamples = (pSound->pVoice->frameCount * pSound->pVoice->channels) / 2;
- assert(chunkSizeInSamples > 0);
-
- dr_uint64 samplesRead = pSound->desc.onRead(pSound, chunkSizeInSamples, dra_voice_get_buffer_ptr_by_sample(pSound->pVoice, outputSampleOffset));
- if (samplesRead == 0 && !pSound->isLooping) {
- dra_voice_write_silence(pSound->pVoice, outputSampleOffset, chunkSizeInSamples);
- return DR_FALSE; // Ran out of samples in a non-looping buffer.
- }
-
- if (samplesRead == chunkSizeInSamples) {
- return DR_TRUE;
- }
-
- assert(samplesRead > 0);
- assert(samplesRead < chunkSizeInSamples);
-
- // Ran out of samples. If the sound is not looping it simply means the end of the data has been reached. The remaining samples need
- // to be zeroed out to create silence.
- if (!pSound->isLooping) {
- dra_voice_write_silence(pSound->pVoice, outputSampleOffset + samplesRead, chunkSizeInSamples - samplesRead);
- return DR_TRUE;
- }
-
- // At this point the sound will not be looping. We want to continuously loop back to the start and keep reading samples until the
- // chunk is filled.
- while (samplesRead < chunkSizeInSamples) {
- if (!pSound->desc.onSeek(pSound, 0)) {
- return DR_FALSE;
- }
-
- dr_uint64 samplesRemaining = chunkSizeInSamples - samplesRead;
- dr_uint64 samplesJustRead = pSound->desc.onRead(pSound, samplesRemaining, dra_voice_get_buffer_ptr_by_sample(pSound->pVoice, outputSampleOffset + samplesRead));
- if (samplesJustRead == 0) {
- return DR_FALSE;
- }
-
- samplesRead += samplesJustRead;
- }
-
- return DR_TRUE;
-}
-
-void dra_sound__on_read_next_chunk(dr_uint64 eventID, void* pUserData)
-{
- dra_sound* pSound = (dra_sound*)pUserData;
- assert(pSound != NULL);
-
- if (pSound->stopOnNextChunk) {
- pSound->stopOnNextChunk = DR_FALSE;
- dra_sound_stop(pSound);
- return;
- }
-
- // The event ID is the index of the sample to write to.
- dr_uint64 sampleOffset = eventID;
- if (!dra_sound__read_next_chunk(pSound, sampleOffset)) {
- pSound->stopOnNextChunk = DR_TRUE;
- }
-}
-
-
-dra_sound* dra_sound_create(dra_sound_world* pWorld, dra_sound_desc* pDesc)
-{
- if (pWorld == NULL) {
- return NULL;
- }
-
- dr_bool32 isStreaming = DR_FALSE;
- dra_result result = DRA_RESULT_SUCCESS;
-
- dra_sound* pSound = (dra_sound*)calloc(1, sizeof(*pSound));
- if (pSound == NULL) {
- result = DRA_RESULT_OUT_OF_MEMORY;
- goto on_error;
- }
-
- pSound->pWorld = pWorld;
- pSound->desc = *pDesc;
-
- isStreaming = dra_sound__is_streaming(pSound);
- if (!isStreaming) {
- result = dra_voice_create(pWorld->pPlaybackDevice, pDesc->format, pDesc->channels, pDesc->sampleRate, pDesc->dataSize, pDesc->pData, &pSound->pVoice);
- } else {
- size_t streamingBufferSize = (pDesc->sampleRate * pDesc->channels) * 2; // 2 seconds total, 1 second chunks. Keep total an even number and a multiple of the channel count.
- result = dra_voice_create(pWorld->pPlaybackDevice, pDesc->format, pDesc->channels, pDesc->sampleRate, streamingBufferSize * dra_get_bytes_per_sample_by_format(pDesc->format), NULL, &pSound->pVoice);
-
- // Streaming buffers require 2 playback events. As one is being played, the other is filled. The event ID is set to the sample
- // index of the next chunk that needs updating and is used in determining where to place new data.
- dra_voice_add_playback_event(pSound->pVoice, 0, streamingBufferSize/2, dra_sound__on_read_next_chunk, pSound);
- dra_voice_add_playback_event(pSound->pVoice, streamingBufferSize/2, 0, dra_sound__on_read_next_chunk, pSound);
- }
-
- if (result != DRA_RESULT_SUCCESS) {
- goto on_error;
- }
-
- pSound->pVoice->pUserData = pSound;
-
-
- // Streaming buffers need to have an initial chunk of data loaded before returning. This ensures the internal buffer contains valid audio data in
- // preparation for being played for the first time.
- if (isStreaming) {
- if (!dra_sound__read_next_chunk(pSound, 0)) {
- goto on_error;
- }
- }
-
- return pSound;
-
-on_error:
- dra_sound_delete(pSound);
- return NULL;
-}
-
-
-
-void dra_sound__on_delete_decoder(dra_sound* pSound)
-{
- dra_decoder* pDecoder = (dra_decoder*)pSound->desc.pUserData;
- assert(pDecoder != NULL);
-
- dra_decoder_close(pDecoder);
- free(pDecoder);
-}
-
-dr_uint64 dra_sound__on_read_decoder(dra_sound* pSound, dr_uint64 samplesToRead, void* pSamplesOut)
-{
- dra_decoder* pDecoder = (dra_decoder*)pSound->desc.pUserData;
- assert(pDecoder != NULL);
-
- return dra_decoder_read_f32(pDecoder, samplesToRead, (float*)pSamplesOut);
-}
-
-dr_bool32 dra_sound__on_seek_decoder(dra_sound* pSound, dr_uint64 sample)
-{
- dra_decoder* pDecoder = (dra_decoder*)pSound->desc.pUserData;
- assert(pDecoder != NULL);
-
- return dra_decoder_seek_to_sample(pDecoder, sample);
-}
-
-#ifndef DR_AUDIO_NO_STDIO
-dra_sound* dra_sound_create_from_file(dra_sound_world* pWorld, const char* filePath)
-{
- if (pWorld == NULL || filePath == NULL) {
- return NULL;
- }
-
- dra_decoder* pDecoder = (dra_decoder*)malloc(sizeof(*pDecoder));
- if (pDecoder == NULL) {
- return NULL;
- }
-
- dra_result result = dra_decoder_open_file(pDecoder, filePath);
- if (result != DRA_RESULT_SUCCESS) {
- free(pDecoder);
- return NULL;
- }
-
- dra_sound_desc desc;
- desc.format = dra_format_f32;
- desc.channels = pDecoder->channels;
- desc.sampleRate = pDecoder->sampleRate;
- desc.dataSize = 0;
- desc.pData = NULL;
- desc.onDelete = dra_sound__on_delete_decoder;
- desc.onRead = dra_sound__on_read_decoder;
- desc.onSeek = dra_sound__on_seek_decoder;
- desc.pUserData = pDecoder;
-
- dra_sound* pSound = dra_sound_create(pWorld, &desc);
-
- // After creating the sound, the audio data of a non-streaming voice can be deleted.
- if (desc.pData != NULL) {
- free(desc.pData);
- }
-
- return pSound;
-}
-#endif
-
-void dra_sound_delete(dra_sound* pSound)
-{
- if (pSound == NULL) {
- return;
- }
-
- if (pSound->pVoice != NULL) {
- dra_voice_delete(pSound->pVoice);
- }
-
- if (pSound->desc.onDelete) {
- pSound->desc.onDelete(pSound);
- }
-
- free(pSound);
-}
-
-
-void dra_sound_play(dra_sound* pSound, dr_bool32 loop)
-{
- if (pSound == NULL) {
- return;
- }
-
- // The voice is always set to loop for streaming sounds.
- if (dra_sound__is_streaming(pSound)) {
- dra_voice_play(pSound->pVoice, DR_TRUE);
- } else {
- dra_voice_play(pSound->pVoice, loop);
- }
-
- pSound->isLooping = loop;
-}
-
-void dra_sound_stop(dra_sound* pSound)
-{
- if (pSound == NULL) {
- return;
- }
-
- dra_voice_stop(pSound->pVoice);
-}
-
-
-void dra_sound_attach_to_mixer(dra_sound* pSound, dra_mixer* pMixer)
-{
- if (pSound == NULL) {
- return;
- }
-
- if (pMixer == NULL) {
- pMixer = pSound->pWorld->pPlaybackDevice->pMasterMixer;
- }
-
- dra_mixer_attach_voice(pMixer, pSound->pVoice);
-}
-
-
-void dra_sound_set_on_stop(dra_sound* pSound, dra_event_proc proc, void* pUserData)
-{
- dra_voice_set_on_stop(pSound->pVoice, proc, pUserData);
-}
-
-void dra_sound_set_on_play(dra_sound* pSound, dra_event_proc proc, void* pUserData)
-{
- dra_voice_set_on_play(pSound->pVoice, proc, pUserData);
-}
-
-#endif //DR_AUDIO_IMPLEMENTATION
-
-
-// TODO
-//
-// - Forward declare every backend function and document them.
-// - Add support for the push API in stb_vorbis.
-
-// DEVELOPMENT NOTES AND BRAINSTORMING
-//
-// This is just random brainstorming and is likely very out of date and often just outright incorrect.
-//
-//
-// Latency
-//
-// When a device is created it'll create a "hardware buffer" which is basically the buffer that the hardware
-// device will read from when it needs to play audio. The hardware buffer is divided into two halves. As the
-// buffer plays, it moves the playback pointer forward through the buffer and loops. When it hits the half
-// way point it notifies the application that it needs more data to continue playing. Once one half starts
-// playing the data within it is committed and cannot be changed. The size of each half determines the latency
-// of the device.
-//
-// It sounds tempting to set this to something small like 1ms, but making it too small will
-// increase the chance that the CPU won't be able to keep filling it with fresh data. In addition it will
-// incrase overall CPU usage because operating system's scheduler will need to wake up the thread more often.
-// Increasing the latency will increase memory usage and make playback of new sound sources take longer to
-// begin playing. For example, if the latency was set to something like 1 second, a sound effect in a game
-// may take up to a whole second to start playing. A balance needs to be made when setting the latency, and
-// it can be configured when the device is created.
-//
-// (mention the fragments system to help avoiding the CPU running out of time to fill new audio data)
-//
-//
-// Mixing
-//
-// Mixing is done via dra_mixer objects. Buffers can be attached to a mixer, but not more than one at a time.
-// By default buffers are attached to a master mixer. Effects like volume can be applied on a per-buffer and
-// per-mixer basis.
-//
-// Mixers can be chained together in a hierarchial manner. Child mixers will be mixed first with the result
-// then passed on to higher level mixing. The master mixer is always the top level mixer.
-//
-// An obvious use case for mixers in games is to have separate volume controls for different categories of
-// sounds, such as music, voices and sounds effects. Another example may be in music production where you may
-// want to have separate mixers for vocal tracks, percussion tracks, etc.
-//
-// A mixer can be thought of as a complex buffer - it can be played/stopped/paused and have effects such as
-// volume apllied to it. All of this affects all attached buffers and sub-mixers. You can, for example, pause
-// every buffer attached to the mixer by simply pausing the mixer. This is an efficient and easy way to pause
-// a group of audio buffers at the same time, such as when the user hits the pause button in a game.
-//
-// Every device includes a master mixer which is the one that buffers are automatically attached to. This one
-// is intentionally hidden from the public facing API in order to keep it simpler. For basic audio playback
-// using the master mixer will work just fine, however for more complex sound interactions you'll want to use
-// your own mixers. Mixers, like buffers, are attached to the master mixer by default
-//
-//
-// Thread Safety
-//
-// Everything in dr_audio should be thread-safe.
-//
-// Backends are implemented as if they are always run from a single thread. It's up to the cross-platform
-// section to ensure thread-safety. This is an important property because if each backend is responsible for
-// their own thread safety it increases the likelyhood of subtle backend-specific bugs.
-//
-// Every device has their own thread for asynchronous playback. This thread is created when the device is
-// created, and deleted when the device is deleted.
-//
-// (Note edge cases when thread-safety may be an issue)
-
-
-/*
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-For more information, please refer to
-*/
diff --git a/source/engine/thirdparty/dr_libs/old/dr_audio_ancient.h b/source/engine/thirdparty/dr_libs/old/dr_audio_ancient.h
deleted file mode 100644
index 4cb62f3..0000000
--- a/source/engine/thirdparty/dr_libs/old/dr_audio_ancient.h
+++ /dev/null
@@ -1,4310 +0,0 @@
-// Public domain. See "unlicense" statement at the end of this file.
-
-//
-// QUICK NOTES
-//
-// If you've stumbled across this library, be aware that this is very, very early in development. A lot of APIs
-// are very temporary, in particular the effects API which at the moment are tied very closely to DirectSound.
-//
-// Currently, the only backend available is DirectSound while I figure out the API.
-//
-// General
-// - This library is NOT thread safe. Functions can be called from any thread, but it is up to the host application
-// to do synchronization.
-// - This library is only concerned with playback and recording of raw audio data. It does not load audio files
-// such as WAV, OGG and MP3.
-// - Before you can create an output (playback) or input (recording) device you need to first create a context.
-// - Each backend (DirectSound, ALSA, etc.) has it's own context. Using draudio_create_context() will find
-// a backend implementation based on the platform dr_audio has been compiled for.
-// - A context for a specific backend can be created as well. For example, draudio_create_context_dsound() will
-// create a context which uses DirectSound as it's backend.
-// - Currently, devices are enumerated once when the context is created. Thus, when a device is plugged in or
-// unplugged it will not be detected by dr_audio and the context will need to be deleted and re-created.
-// - Currently, dr_audio will only consider the first DRAUDIO_MAX_DEVICE_COUNT output and input devices, which
-// is currently set to 16 and should be plenty for the vast majority of cases. Feel free to increase (or decrease)
-// this number to suit your own requirements.
-//
-// Events
-// - Events are handled via callbacks. The different types of events include stop, pause, play and markers.
-// - The Stop event is fired when an output buffer is stopped, either from finishing it's playback or if it was
-// stopped manually.
-// - The Pause event is fired when the output buffer is paused.
-// - The Play event is fired when the output buffer begins being played from either a stopped or paused state.
-// - A Marker event is fired when the playback position of an output buffer reaches a certain point within the
-// buffer. This is useful for streaming audio data because it can tell you when a particular section of the
-// buffer can be filled with new data.
-// - Due to the inherent multi-threaded nature of audio playback, events can be fired from any thread. It is up
-// to the application to ensure events are handled safely.
-// - Currently, the maximum number of markers is set by DRAUDIO_MAX_MARKER_COUNT which is set to 4 by default. This
-// can be increased, however doing so increases memory usage for each sound buffer.
-//
-// Performance Considerations
-// - Creating and deleting buffers should be considered an expensive operation because there is quite a bit of thread
-// management being performed under the hood. Prefer caching sound buffers.
-//
-
-//
-// OPTIONS
-//
-// #define DRAUDIO_NO_DIRECTSOUND
-// Disables support for the DirectSound backend. Note that at the moment this is the only backend available for
-// Windows platforms, so you will likely not want to set this. DirectSound will only be compiled on Win32 builds.
-//
-
-//
-// TODO
-//
-// - DirectSound: Consider using Win32 critical sections instead of events where possible.
-// - DirectSound: Remove the semaphore and replace with an auto-reset event.
-// - Implement a better error handling API.
-// - Implement effects
-// - Implement velocity
-// - Implement cones
-// - Implement attenuation min/max distances
-//
-
-#ifndef dr_audio_h
-#define dr_audio_h
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include
-#include
-
-
-#define DRAUDIO_MAX_DEVICE_COUNT 16
-#define DRAUDIO_MAX_MARKER_COUNT 4
-#define DRAUDIO_MAX_MESSAGE_QUEUE_SIZE 1024 // The maximum number of messages that can be cached in the internal message queues.
-
-
-#if defined(_WIN32) && !defined(DRAUDIO_NO_DIRECTSOUND)
-#define DRAUDIO_BUILD_DSOUND
-#endif
-
-
-#define DRAUDIO_EVENT_ID_STOP 0xFFFFFFFF
-#define DRAUDIO_EVENT_ID_PAUSE 0xFFFFFFFE
-#define DRAUDIO_EVENT_ID_PLAY 0xFFFFFFFD
-#define DRAUDIO_EVENT_ID_MARKER 0
-
-#define DRAUDIO_ENABLE_3D (1 << 0)
-#define DRAUDIO_RELATIVE_3D (1 << 1) // <-- Uses relative 3D positioning by default instead of absolute. Only used if DRAUDIO_ENABLE_3D is also specified.
-
-
-// Data formats.
-typedef enum
-{
- draudio_format_pcm,
- draudio_format_float
-
-} draudio_format;
-
-// Playback states.
-typedef enum
-{
- draudio_stopped,
- draudio_paused,
- draudio_playing
-
-} draudio_playback_state;
-
-// Effect types.
-typedef enum
-{
- draudio_effect_type_none,
- draudio_effect_type_i3dl2reverb,
- draudio_effect_type_chorus,
- draudio_effect_type_compressor,
- draudio_effect_type_distortion,
- draudio_effect_type_echo,
- draudio_effect_type_flanger
-
-} draudio_effect_type;
-
-// 3D processing modes.
-typedef enum
-{
- draudio_3d_mode_absolute,
- draudio_3d_mode_relative,
- draudio_3d_mode_disabled
-
-} draudio_3d_mode;
-
-
-typedef struct draudio_context draudio_context;
-typedef struct draudio_device draudio_device;
-typedef struct draudio_buffer draudio_buffer;
-
-typedef void (* draudio_event_callback_proc)(draudio_buffer* pBuffer, unsigned int eventID, void *pUserData);
-
-typedef struct
-{
- /// The callback function.
- draudio_event_callback_proc callback;
-
- /// The user data.
- void* pUserData;
-
-} draudio_event_callback;
-
-typedef struct
-{
- /// The description of the device.
- char description[256];
-
-} draudio_device_info;
-
-typedef struct
-{
- /// Boolean flags.
- /// DRAUDIO_ENABLE_3D: Enable 3D positioning
- unsigned int flags;
-
- /// The data format.
- draudio_format format;
-
- /// The number of channels. This should be 1 for mono, 2 for stereo.
- unsigned int channels;
-
- /// The sample rate.
- unsigned int sampleRate;
-
- /// The number of bits per sample.
- unsigned int bitsPerSample;
-
- /// The size in bytes of the data.
- size_t sizeInBytes;
-
- /// A pointer to the initial data.
- void* pData;
-
-} draudio_buffer_desc;
-
-typedef struct
-{
- /// The effect type.
- draudio_effect_type type;
-
- struct
- {
- float room;
- float roomHF;
- float roomRolloffFactor;
- float decayTime;
- float reflections;
- float reflectionsDelay;
- float reverb;
- float reverbDelay;
- float diffusion;
- float density;
- float hfReference;
- } i3dl2reverb;
-
- struct
- {
- int waveform;
- int phase;
- float depth;
- float feedback;
- float frequency;
- float delay;
- } chorus;
-
- struct
- {
- float gain;
- float attack;
- float release;
- float threshold;
- float ratio;
- float predelay;
- } compressor;
-
- struct
- {
- float gain;
- float edge;
- float postEQCenterFrequency;
- float postEQBandwidth;
- float preLowpassCutoff;
- } distortion;
-
- struct
- {
- float wetDryMix;
- float feedback;
- float leftDelay;
- float rightDelay;
- float panDelay;
- } echo;
-
- struct
- {
- float wetDryMix;
- float depth;
- float feedback;
- float frequency;
- float waveform;
- float delay;
- float phase;
- } flanger;
-
-} draudio_effect;
-
-
-/// Creates a context which chooses an appropriate backend based on the given platform.
-draudio_context* draudio_create_context();
-
-#ifdef DRAUDIO_BUILD_DSOUND
-/// Creates a context that uses DirectSound for it's backend.
-draudio_context* draudio_create_context_dsound();
-#endif
-
-/// Deletes the given context.
-void draudio_delete_context(draudio_context* pContext);
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-//
-// OUTPUT / PLAYBACK
-//
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-/// Retrieves the number of output devices that were enumerated when the context was created.
-unsigned int draudio_get_output_device_count(draudio_context* pContext);
-
-/// Retrieves information about the device at the given index.
-bool draudio_get_output_device_info(draudio_context* pContext, unsigned int deviceIndex, draudio_device_info* pInfoOut);
-
-
-/// Creates a output device.
-///
-/// @param pContext [in] A pointer to the main context.
-/// @param deviceIndex [in] The index of the device.
-///
-/// @remarks
-/// Use a device index of 0 to use the default output device.
-draudio_device* draudio_create_output_device(draudio_context* pContext, unsigned int deviceIndex);
-
-/// Deletes the given output device.
-void draudio_delete_output_device(draudio_device* pDevice);
-
-
-/// Create a buffer.
-///
-/// @remarks
-/// This will fail if 3D positioning is requested when the sound has more than 1 channel.
-draudio_buffer* draudio_create_buffer(draudio_device* pDevice, draudio_buffer_desc* pBufferDesc, size_t extraDataSize);
-
-/// Deletes the given buffer.
-void draudio_delete_buffer(draudio_buffer* pBuffer);
-
-
-/// Retrieves the size in bytes of the given buffer's extra data.
-unsigned int draudio_get_buffer_extra_data_size(draudio_buffer* pBuffer);
-
-/// Retrieves a pointer to the given buffer's extra data.
-void* draudio_get_buffer_extra_data(draudio_buffer* pBuffer);
-
-
-/// Sets the audio data of the given buffer.
-void draudio_set_buffer_data(draudio_buffer* pBuffer, size_t offset, const void* pData, size_t dataSizeInBytes);
-
-
-/// Begins or resumes playing the given buffer.
-///
-/// @remarks
-/// If the sound is already playing, it will continue to play, but the \c loop setting will be replaced with that specified
-/// by the most recent call.
-void draudio_play(draudio_buffer* pBuffer, bool loop);
-
-/// Pauses playback of the given buffer.
-void draudio_pause(draudio_buffer* pBuffer);
-
-/// Stops playback of the given buffer.
-void draudio_stop(draudio_buffer* pBuffer);
-
-/// Retrieves the playback state of the given buffer.
-draudio_playback_state draudio_get_playback_state(draudio_buffer* pBuffer);
-
-/// Determines whether or not the given audio buffer is looping.
-bool draudio_is_looping(draudio_buffer* pBuffer);
-
-
-/// Sets the playback position for the given buffer.
-void draudio_set_playback_position(draudio_buffer* pBuffer, unsigned int position);
-
-/// Retrieves hte playback position of the given buffer.
-unsigned int draudio_get_playback_position(draudio_buffer* pBuffer);
-
-
-/// Sets the pan of the given buffer.
-///
-/// @remarks
-/// This does nothing for 3D sounds.
-void draudio_set_pan(draudio_buffer* pBuffer, float pan);
-
-/// Retrieves the pan of the given buffer.
-float draudio_get_pan(draudio_buffer* pBuffer);
-
-
-/// Sets the volume of the given buffer.
-///
-/// @param volume [in] The new volume.
-///
-/// @remarks
-/// Amplificiation is not currently supported, so the maximum value is 1. A value of 1 represents the volume of the original
-/// data.
-void draudio_set_volume(draudio_buffer* pBuffer, float volume);
-
-/// Retrieves the volume of the sound.
-float draudio_get_volume(draudio_buffer* pBuffer);
-
-
-/// Removes every marker.
-void draudio_remove_markers(draudio_buffer* pBuffer);
-
-/// Registers the callback to fire when the playback position hits a certain position in the given buffer.
-///
-/// @param eventID [in] The event ID that will be passed to the callback and can be used to identify a specific marker.
-///
-/// @remarks
-/// This will fail if the buffer is not in a stopped state.
-/// @par
-/// Set the event ID to DRAUDIO_EVENT_ID_MARKER + n, where "n" is your own application-specific identifier.
-bool draudio_register_marker_callback(draudio_buffer* pBuffer, size_t offsetInBytes, draudio_event_callback_proc callback, unsigned int eventID, void* pUserData);
-
-/// Registers the callback to fire when the buffer stops playing.
-///
-/// @remarks
-/// This will fail if the buffer is not in a stopped state and the callback is non-null. It is fine to call this
-/// with a null callback while the buffer is in the middle of playback in which case the callback will be cleared.
-/// @par
-/// The will replace any previous callback.
-bool draudio_register_stop_callback(draudio_buffer* pBuffer, draudio_event_callback_proc callback, void* pUserData);
-
-/// Registers the callback to fire when the buffer is paused.
-///
-/// @remarks
-/// This will fail if the buffer is not in a stopped state and the callback is non-null. It is fine to call this
-/// with a null callback while the buffer is in the middle of playback in which case the callback will be cleared.
-/// @par
-/// The will replace any previous callback.
-bool draudio_register_pause_callback(draudio_buffer* pBuffer, draudio_event_callback_proc callback, void* pUserData);
-
-/// Registers the callback to fire when the buffer begins playing from either a stopped or paused state.
-///
-/// @remarks
-/// This will fail if the buffer is not in a stopped state and the callback is non-null. It is fine to call this
-/// with a null callback while the buffer is in the middle of playback in which case the callback will be cleared.
-/// @par
-/// The will replace any previous callback.
-bool draudio_register_play_callback(draudio_buffer* pBuffer, draudio_event_callback_proc callback, void* pUserData);
-
-
-/// Retrieves the callback that is currently set for the stop event.
-draudio_event_callback draudio_get_stop_callback(draudio_buffer* pBuffer);
-
-/// Retrieves the callback that is currently set for the pause event.
-draudio_event_callback draudio_get_pause_callback(draudio_buffer* pBuffer);
-
-/// Retrieves the callback that is currently set for the play event.
-draudio_event_callback draudio_get_play_callback(draudio_buffer* pBuffer);
-
-
-/// Sets the position of the given buffer in 3D space.
-///
-/// @remarks
-/// This does nothing for buffers that do not support 3D positioning.
-void draudio_set_position(draudio_buffer* pBuffer, float x, float y, float z);
-
-/// Retrieves the position of the given buffer in 3D space.
-void draudio_get_position(draudio_buffer* pBuffer, float* pPosOut);
-
-/// Sets the position of the listener for the given output device.
-void draudio_set_listener_position(draudio_device* pDevice, float x, float y, float z);
-
-/// Retrieves the position of the listner for the given output device.
-void draudio_get_listener_position(draudio_device* pDevice, float* pPosOut);
-
-/// Sets the orientation of the listener for the given output device.
-void draudio_set_listener_orientation(draudio_device* pDevice, float forwardX, float forwardY, float forwardZ, float upX, float upY, float upZ);
-
-/// Retrieves the orientation of the listener for the given output device.
-void draudio_get_listener_orientation(draudio_device* pDevice, float* pForwardOut, float* pUpOut);
-
-
-/// Sets the 3D processing mode (absolute, relative or disabled).
-///
-/// @remarks
-/// This applies to position, orientation and velocity.
-void draudio_set_3d_mode(draudio_buffer* pBuffer, draudio_3d_mode mode);
-
-/// Retrieves the 3D processing mode (absolute, relative or disabled).
-draudio_3d_mode draudio_get_3d_mode(draudio_buffer* pBuffer);
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-//
-// INPUT / RECORDING
-//
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-//
-// HIGH-LEVEL API
-//
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-//// SYNCHRONIZATION ////
-
-typedef void* draudio_mutex;
-
-/// Creates a mutex object.
-draudio_mutex draudio_create_mutex();
-
-/// Deletes a mutex object.
-void draudio_delete_mutex(draudio_mutex mutex);
-
-/// Locks the given mutex.
-void draudio_lock_mutex(draudio_mutex mutex);
-
-/// Unlocks the given mutex.
-void draudio_unlock_mutex(draudio_mutex mutex);
-
-
-
-
-//// STREAMING ////
-typedef int draudio_bool;
-
-typedef draudio_bool (* draudio_stream_read_proc)(void* pUserData, void* pDataOut, size_t bytesToRead, size_t* bytesReadOut);
-typedef draudio_bool (* draudio_stream_seek_proc)(void* pUserData, size_t offsetInBytesFromStart);
-
-typedef struct
-{
- /// A pointer to the user data to pass to each callback.
- void* pUserData;
-
- /// A pointer to the function to call when more data needs to be read.
- draudio_stream_read_proc read;
-
- /// Seeks source data from the beginning of the file.
- draudio_stream_seek_proc seek;
-
-} draudio_streaming_callbacks;
-
-
-/// Creates a buffer that's pre-configured for use for streaming audio data.
-///
-/// @remarks
-/// This function is just a high-level convenience wrapper. The returned buffer is just a regular buffer with pre-configured
-/// markers attached to the buffer. This will attach 3 markers in total which means there is only DRAUDIO_MAX_MARKER_COUNT - 3
-/// marker slots available to the application.
-/// @par
-/// You must play the buffer with draudio_play_streaming_buffer() because the underlying buffer management is slightly different
-/// to a regular buffer.
-/// @par
-/// Looping and stop callbacks may be inaccurate by up to half a second.
-/// @par
-/// Callback functions use bytes to determine how much data to process. This is always a multiple of samples * channels, however.
-/// @par
-/// The first chunk of data is not loaded until the buffer is played with draudio_play_streaming_buffer().
-draudio_buffer* draudio_create_streaming_buffer(draudio_device* pDevice, draudio_buffer_desc* pBufferDesc, draudio_streaming_callbacks callbacks, unsigned int extraDataSize);
-
-/// Retrieves the size of the extra data of the given streaming buffer..
-size_t draudio_get_streaming_buffer_extra_data_size(draudio_buffer* pBuffer);
-
-/// Retrieves a pointer to the extra data of the given streaming buffer.
-void* draudio_get_streaming_buffer_extra_data(draudio_buffer* pBuffer);
-
-/// Begins playing the given streaming buffer.
-bool draudio_play_streaming_buffer(draudio_buffer* pBuffer, bool loop);
-
-/// Determines whether or not the given streaming buffer is looping.
-bool draudio_is_streaming_buffer_looping(draudio_buffer* pBuffer);
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// Sound World
-//
-// When a sound is created, whether or not it will be streamed is determined by
-// whether or not a pointer to some initial data is specified when creating the
-// sound. When initial data is specified, the sound data will be loaded into
-// buffer once at creation time. If no data is specified, sound data will be
-// loaded dynamically at playback time.
-//
-///////////////////////////////////////////////////////////////////////////////
-
-typedef struct draudio_sound draudio_sound;
-typedef struct draudio_world draudio_world;
-
-typedef void (* draudio_on_sound_delete_proc) (draudio_sound* pSound);
-typedef bool (* draudio_on_sound_read_data_proc)(draudio_sound* pSound, void* pDataOut, size_t bytesToRead, size_t* bytesReadOut);
-typedef bool (* draudio_on_sound_seek_data_proc)(draudio_sound* pSound, size_t offsetInBytesFromStart);
-
-/// The structure that is used for creating a sound object.
-typedef struct
-{
- /// Boolean flags.
- /// DRAUDIO_ENABLE_3D: Enable 3D positioning
- unsigned int flags;
-
- /// The data format.
- draudio_format format;
-
- /// The number of channels. This should be 1 for mono, 2 for stereo.
- unsigned int channels;
-
- /// The sample rate.
- unsigned int sampleRate;
-
- /// The number of bits per sample.
- unsigned int bitsPerSample;
-
- /// The size in bytes of the data. When this is non-zero, and pInitialData is non-null, the onRead and onSeek streaming
- /// callbacks are not used, and instead the sound's audio data is made up exclusively with this data.
- size_t sizeInBytes;
-
- /// A pointer to the initial data. Can be null, in which case the audio data is streamed with the onRead and onSeek
- /// callbacks below. It is an error for this to be null in addition to onRead and onSeek.
- void* pData;
-
-
- /// A pointer to the function to call when the sound is being deleted. This gives the application the opportunity
- /// to delete internal objects that are used for streaming or whatnot.
- draudio_on_sound_delete_proc onDelete;
-
- /// A pointer to the function to call when dr_audio needs to request a chunk of audio data. This is only used when
- /// streaming data.
- draudio_on_sound_read_data_proc onRead;
-
- /// A pointer to the function to call when dr_audio needs to seek the audio data. This is only used when streaming
- /// data.
- draudio_on_sound_seek_data_proc onSeek;
-
-
- /// The size of the extra data to associate with the sound. Extra data is how an application can link custom data to the
- /// sound object.
- unsigned int extraDataSize;
-
- /// A pointer to a buffer containing the initial extra data. This buffer is copied when the sound is initially created,
- /// and can be null.
- const void* pExtraData;
-
-} draudio_sound_desc;
-
-struct draudio_sound
-{
- /// A pointer to the world that owns the sound.
- draudio_world* pWorld;
-
- /// A pointer to the audio buffer for playback.
- draudio_buffer* pBuffer;
-
-
- /// [Internal Use Only] The state of the buffer's playback at the time the associated world overwrote it.
- draudio_playback_state prevPlaybackState;
-
- /// [Internal Use Only] A pointer to the next sound in the local list.
- draudio_sound* pNextSound;
-
- /// [Internal Use Only] A pointer ot the previous sound in the local list.
- draudio_sound* pPrevSound;
-
- /// [Internal Use Only] Keeps track of whether or not a streaming buffer is being used.
- bool isUsingStreamingBuffer;
-
- /// [Internal Use Only] Keeps track of whether or not the sound has been marked for deletion. This is used to
- /// ensure onRead and onSeek are never called after the sound has been deleted. This scenario is possible because
- /// these functions are called in response to the sound buffer hitting markers which can be slightly delayed
- /// due to multi-threading synchronization.
- bool markedForDeletion;
-
- /// [Internal Use Only] the onDelete function. Can be null.
- draudio_on_sound_delete_proc onDelete;
-
- /// [Internal Use Only] The onRead streaming function. Can be null, in which case streaming will not be used.
- draudio_on_sound_read_data_proc onRead;
-
- /// [Internal Use Only] The onSeek streaming function. Can be null, in which case streaming will not be used.
- draudio_on_sound_seek_data_proc onSeek;
-};
-
-struct draudio_world
-{
- /// A pointer to the dr_audio device to output audio to.
- draudio_device* pDevice;
-
- /// The global playback state of the world.
- draudio_playback_state playbackState;
-
- /// A pointer to the first sound in the local list of sounds.
- draudio_sound* pFirstSound;
-
- /// Mutex for thread-safety.
- draudio_mutex lock;
-};
-
-
-/// Creates a new sound world which will output audio from the given device.
-draudio_world* draudio_create_world(draudio_device* pDevice);
-
-/// Deletes a sound world that was previously created with draudio_create_world().
-void draudio_delete_world(draudio_world* pWorld);
-
-
-/// Creates a sound in 3D space.
-draudio_sound* draudio_create_sound(draudio_world* pWorld, draudio_sound_desc desc);
-
-/// Deletes a sound that was previously created with draudio_create_sound().
-void draudio_delete_sound(draudio_sound* pSound);
-
-/// Deletes every sound from the given world.
-void draudio_delete_all_sounds(draudio_world* pWorld);
-
-
-/// Retrieves the size in bytes of the given sound's extra data.
-size_t draudio_get_sound_extra_data_size(draudio_sound* pSound);
-
-/// Retrieves a pointer to the buffer containing the given sound's extra data.
-void* draudio_get_sound_extra_data(draudio_sound* pSound);
-
-
-/// Plays or resumes the given sound.
-void draudio_play_sound(draudio_sound* pSound, bool loop);
-
-/// Pauses playback the given sound.
-void draudio_pause_sound(draudio_sound* pSound);
-
-/// Stops playback of the given sound.
-void draudio_stop_sound(draudio_sound* pSound);
-
-/// Retrieves the playback state of the given sound.
-draudio_playback_state draudio_get_sound_playback_state(draudio_sound* pSound);
-
-/// Determines if the given sound is looping.
-bool draudio_is_sound_looping(draudio_sound* pSound);
-
-
-/// Begins playing a sound using the given streaming callbacks.
-void draudio_play_inline_sound(draudio_world* pWorld, draudio_sound_desc desc);
-
-/// Begins playing the given sound at the given position.
-void draudio_play_inline_sound_3f(draudio_world* pWorld, draudio_sound_desc desc, float posX, float posY, float posZ);
-
-
-/// Stops playback of all sounds in the given world.
-void draudio_stop_all_sounds(draudio_world* pWorld);
-
-/// Pauses playback of all sounds in the given world.
-void draudio_pause_all_sounds(draudio_world* pWorld);
-
-/// Resumes playback of all sounds in the given world.
-void draudio_resume_all_sounds(draudio_world* pWorld);
-
-
-/// Sets the callback for the stop event for the given sound.
-void draudio_set_sound_stop_callback(draudio_sound* pSound, draudio_event_callback_proc callback, void* pUserData);
-
-/// Sets the callback for the pause event for the given sound.
-void draudio_set_sound_pause_callback(draudio_sound* pSound, draudio_event_callback_proc callback, void* pUserData);
-
-/// Sets the callback for the play event for the given sound.
-void draudio_set_sound_play_callback(draudio_sound* pSound, draudio_event_callback_proc callback, void* pUserData);
-
-
-/// Sets the position of the given sound.
-void draudio_set_sound_position(draudio_sound* pSound, float posX, float posY, float posZ);
-
-
-/// Sets the 3D mode of the given sound (absolute positioning, relative positioning, no positioning).
-void draudio_set_sound_3d_mode(draudio_sound* pSound, draudio_3d_mode mode);
-
-/// Retrieves the 3D mode of the given sound.
-draudio_3d_mode draudio_get_sound_3d_mode(draudio_sound* pSound);
-
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif //dr_audio_h
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// IMPLEMENTATION
-//
-///////////////////////////////////////////////////////////////////////////////
-#ifdef DR_AUDIO_IMPLEMENTATION
-#include
-#include
-#include
-#include
-#include
-
-
-// Annotations
-#ifndef DRAUDIO_PRIVATE
-#define DRAUDIO_PRIVATE
-#endif
-
-
-////////////////////////////////////////////////////////
-// Utilities
-
-// strcpy()
-int draudio_strcpy(char* dst, size_t dstSizeInBytes, const char* src)
-{
-#if defined(_MSC_VER)
- return strcpy_s(dst, dstSizeInBytes, src);
-#else
- if (dst == 0) {
- return EINVAL;
- }
- if (dstSizeInBytes == 0) {
- return ERANGE;
- }
- if (src == 0) {
- dst[0] = '\0';
- return EINVAL;
- }
-
- char* iDst = dst;
- const char* iSrc = src;
- size_t remainingSizeInBytes = dstSizeInBytes;
- while (remainingSizeInBytes > 0 && iSrc[0] != '\0')
- {
- iDst[0] = iSrc[0];
-
- iDst += 1;
- iSrc += 1;
- remainingSizeInBytes -= 1;
- }
-
- if (remainingSizeInBytes > 0) {
- iDst[0] = '\0';
- } else {
- dst[0] = '\0';
- return ERANGE;
- }
-
- return 0;
-#endif
-}
-
-
-typedef void (* draudio_delete_context_proc)(draudio_context* pContext);
-typedef draudio_device* (* draudio_create_output_device_proc)(draudio_context* pContext, unsigned int deviceIndex);
-typedef void (* draudio_delete_output_device_proc)(draudio_device* pDevice);
-typedef unsigned int (* draudio_get_output_device_count_proc)(draudio_context* pContext);
-typedef bool (* draudio_get_output_device_info_proc)(draudio_context* pContext, unsigned int deviceIndex, draudio_device_info* pInfoOut);
-typedef draudio_buffer* (* draudio_create_buffer_proc)(draudio_device* pDevice, draudio_buffer_desc* pBufferDesc, size_t extraDataSize);
-typedef void (* draudio_delete_buffer_proc)(draudio_buffer* pBuffer);
-typedef unsigned int (* draudio_get_buffer_extra_data_size_proc)(draudio_buffer* pBuffer);
-typedef void* (* draudio_get_buffer_extra_data_proc)(draudio_buffer* pBuffer);
-typedef void (* draudio_set_buffer_data_proc)(draudio_buffer* pBuffer, size_t offset, const void* pData, size_t dataSizeInBytes);
-typedef void (* draudio_play_proc)(draudio_buffer* pBuffer, bool loop);
-typedef void (* draudio_pause_proc)(draudio_buffer* pBuffer);
-typedef void (* draudio_stop_proc)(draudio_buffer* pBuffer);
-typedef draudio_playback_state (* draudio_get_playback_state_proc)(draudio_buffer* pBuffer);
-typedef void (* draudio_set_playback_position_proc)(draudio_buffer* pBuffer, unsigned int position);
-typedef unsigned int (* draudio_get_playback_position_proc)(draudio_buffer* pBuffer);
-typedef void (* draudio_set_pan_proc)(draudio_buffer* pBuffer, float pan);
-typedef float (* draudio_get_pan_proc)(draudio_buffer* pBuffer);
-typedef void (* draudio_set_volume_proc)(draudio_buffer* pBuffer, float volume);
-typedef float (* draudio_get_volume_proc)(draudio_buffer* pBuffer);
-typedef void (* draudio_remove_markers_proc)(draudio_buffer* pBuffer);
-typedef bool (* draudio_register_marker_callback_proc)(draudio_buffer* pBuffer, size_t offsetInBytes, draudio_event_callback_proc callback, unsigned int eventID, void* pUserData);
-typedef bool (* draudio_register_stop_callback_proc)(draudio_buffer* pBuffer, draudio_event_callback_proc callback, void* pUserData);
-typedef bool (* draudio_register_pause_callback_proc)(draudio_buffer* pBuffer, draudio_event_callback_proc callback, void* pUserData);
-typedef bool (* draudio_register_play_callback_proc)(draudio_buffer* pBuffer, draudio_event_callback_proc callback, void* pUserData);
-typedef void (* draudio_set_position_proc)(draudio_buffer* pBuffer, float x, float y, float z);
-typedef void (* draudio_get_position_proc)(draudio_buffer* pBuffer, float* pPosOut);
-typedef void (* draudio_set_listener_position_proc)(draudio_device* pDevice, float x, float y, float z);
-typedef void (* draudio_get_listener_position_proc)(draudio_device* pDevice, float* pPosOut);
-typedef void (* draudio_set_listener_orientation_proc)(draudio_device* pDevice, float forwardX, float forwardY, float forwardZ, float upX, float upY, float upZ);
-typedef void (* draudio_get_listener_orientation_proc)(draudio_device* pDevice, float* pForwardOut, float* pUpOut);
-typedef void (* draudio_set_3d_mode_proc)(draudio_buffer* pBuffer, draudio_3d_mode mode);
-typedef draudio_3d_mode (* draudio_get_3d_mode_proc)(draudio_buffer* pBuffer);
-
-struct draudio_context
-{
- // Callbacks.
- draudio_delete_context_proc delete_context;
- draudio_create_output_device_proc create_output_device;
- draudio_delete_output_device_proc delete_output_device;
- draudio_get_output_device_count_proc get_output_device_count;
- draudio_get_output_device_info_proc get_output_device_info;
- draudio_create_buffer_proc create_buffer;
- draudio_delete_buffer_proc delete_buffer;
- draudio_get_buffer_extra_data_size_proc get_buffer_extra_data_size;
- draudio_get_buffer_extra_data_proc get_buffer_extra_data;
- draudio_set_buffer_data_proc set_buffer_data;
- draudio_play_proc play;
- draudio_pause_proc pause;
- draudio_stop_proc stop;
- draudio_get_playback_state_proc get_playback_state;
- draudio_set_playback_position_proc set_playback_position;
- draudio_get_playback_position_proc get_playback_position;
- draudio_set_pan_proc set_pan;
- draudio_get_pan_proc get_pan;
- draudio_set_volume_proc set_volume;
- draudio_get_volume_proc get_volume;
- draudio_remove_markers_proc remove_markers;
- draudio_register_marker_callback_proc register_marker_callback;
- draudio_register_stop_callback_proc register_stop_callback;
- draudio_register_pause_callback_proc register_pause_callback;
- draudio_register_play_callback_proc register_play_callback;
- draudio_set_position_proc set_position;
- draudio_get_position_proc get_position;
- draudio_set_listener_position_proc set_listener_position;
- draudio_get_listener_position_proc get_listener_position;
- draudio_set_listener_orientation_proc set_listener_orientation;
- draudio_get_listener_orientation_proc get_listener_orientation;
- draudio_set_3d_mode_proc set_3d_mode;
- draudio_get_3d_mode_proc get_3d_mode;
-};
-
-struct draudio_device
-{
- /// The context that owns this device.
- draudio_context* pContext;
-
- /// Whether or not the device is marked for deletion. A device is not always deleted straight away, so we use this
- /// to determine whether or not it has been marked for deletion.
- bool markedForDeletion;
-};
-
-struct draudio_buffer
-{
- /// The device that owns this buffer.
- draudio_device* pDevice;
-
- /// The stop callback.
- draudio_event_callback stopCallback;
-
- /// The pause callback.
- draudio_event_callback pauseCallback;
-
- /// The play callback.
- draudio_event_callback playCallback;
-
- /// Whether or not playback is looping.
- bool isLooping;
-
- /// Whether or not the buffer is marked for deletion. A buffer is not always deleted straight away, so we use this
- /// to determine whether or not it has been marked for deletion.
- bool markedForDeletion;
-};
-
-
-draudio_context* draudio_create_context()
-{
- draudio_context* pContext = NULL;
-
-#ifdef DRAUDIO_BUILD_DSOUND
- pContext = draudio_create_context_dsound();
- if (pContext != NULL) {
- return pContext;
- }
-#endif
-
-
- // If we get here it means we weren't able to create a context, so return NULL. Later on we're going to have a null implementation so that
- // we don't ever need to return NULL here.
- assert(pContext == NULL);
- return pContext;
-}
-
-void draudio_delete_context(draudio_context* pContext)
-{
- if (pContext == NULL) {
- return;
- }
-
- pContext->delete_context(pContext);
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-//
-// OUTPUT
-//
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-unsigned int draudio_get_output_device_count(draudio_context* pContext)
-{
- if (pContext == NULL) {
- return 0;
- }
-
- return pContext->get_output_device_count(pContext);
-}
-
-bool draudio_get_output_device_info(draudio_context* pContext, unsigned int deviceIndex, draudio_device_info* pInfoOut)
-{
- if (pContext == NULL) {
- return false;
- }
-
- if (pInfoOut == NULL) {
- return false;
- }
-
- return pContext->get_output_device_info(pContext, deviceIndex, pInfoOut);
-}
-
-
-draudio_device* draudio_create_output_device(draudio_context* pContext, unsigned int deviceIndex)
-{
- if (pContext == NULL) {
- return NULL;
- }
-
- draudio_device* pDevice = pContext->create_output_device(pContext, deviceIndex);
- if (pDevice != NULL)
- {
- pDevice->markedForDeletion = false;
- }
-
- return pDevice;
-}
-
-void draudio_delete_output_device(draudio_device* pDevice)
-{
- if (pDevice == NULL) {
- return;
- }
-
- // If the device is already marked for deletion we just return straight away. However, this is an erroneous case so we trigger a failed assertion in this case.
- if (pDevice->markedForDeletion) {
- assert(false);
- return;
- }
-
- pDevice->markedForDeletion = true;
-
- assert(pDevice->pContext != NULL);
- pDevice->pContext->delete_output_device(pDevice);
-}
-
-
-draudio_buffer* draudio_create_buffer(draudio_device* pDevice, draudio_buffer_desc* pBufferDesc, size_t extraDataSize)
-{
- if (pDevice == NULL) {
- return NULL;
- }
-
- if (pBufferDesc == NULL) {
- return NULL;
- }
-
- assert(pDevice->pContext != NULL);
-
- draudio_buffer* pBuffer = pDevice->pContext->create_buffer(pDevice, pBufferDesc, extraDataSize);
- if (pBuffer != NULL)
- {
- draudio_event_callback nullcb = {NULL, NULL};
-
- pBuffer->pDevice = pDevice;
- pBuffer->stopCallback = nullcb;
- pBuffer->pauseCallback = nullcb;
- pBuffer->playCallback = nullcb;
- pBuffer->isLooping = false;
- pBuffer->markedForDeletion = false;
- }
-
- return pBuffer;
-}
-
-void draudio_delete_buffer(draudio_buffer* pBuffer)
-{
- if (pBuffer == NULL) {
- return;
- }
-
- // We don't want to do anything if the buffer is marked for deletion.
- if (pBuffer->markedForDeletion) {
- assert(false);
- return;
- }
-
- pBuffer->markedForDeletion = true;
-
-
- // The sound needs to be stopped first.
- draudio_stop(pBuffer);
-
- // Now we need to remove every event.
- draudio_remove_markers(pBuffer);
- draudio_register_stop_callback(pBuffer, NULL, NULL);
- draudio_register_pause_callback(pBuffer, NULL, NULL);
- draudio_register_play_callback(pBuffer, NULL, NULL);
-
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- pBuffer->pDevice->pContext->delete_buffer(pBuffer);
-}
-
-
-unsigned int draudio_get_buffer_extra_data_size(draudio_buffer* pBuffer)
-{
- if (pBuffer == NULL) {
- return 0;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- return pBuffer->pDevice->pContext->get_buffer_extra_data_size(pBuffer);
-}
-
-void* draudio_get_buffer_extra_data(draudio_buffer* pBuffer)
-{
- if (pBuffer == NULL) {
- return NULL;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- return pBuffer->pDevice->pContext->get_buffer_extra_data(pBuffer);
-}
-
-
-void draudio_set_buffer_data(draudio_buffer* pBuffer, size_t offset, const void* pData, size_t dataSizeInBytes)
-{
- if (pBuffer == NULL) {
- return;
- }
-
- if (pData == NULL) {
- return;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- pBuffer->pDevice->pContext->set_buffer_data(pBuffer, offset, pData, dataSizeInBytes);
-}
-
-void draudio_play(draudio_buffer* pBuffer, bool loop)
-{
- if (pBuffer == NULL) {
- return;
- }
-
- pBuffer->isLooping = loop;
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- pBuffer->pDevice->pContext->play(pBuffer, loop);
-}
-
-void draudio_pause(draudio_buffer* pBuffer)
-{
- if (pBuffer == NULL) {
- return;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- pBuffer->pDevice->pContext->pause(pBuffer);
-}
-
-void draudio_stop(draudio_buffer* pBuffer)
-{
- if (pBuffer == NULL) {
- return;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- pBuffer->pDevice->pContext->stop(pBuffer);
-}
-
-draudio_playback_state draudio_get_playback_state(draudio_buffer* pBuffer)
-{
- if (pBuffer == NULL) {
- return draudio_stopped;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- return pBuffer->pDevice->pContext->get_playback_state(pBuffer);
-}
-
-bool draudio_is_looping(draudio_buffer* pBuffer)
-{
- if (pBuffer == NULL) {
- return false;
- }
-
- return pBuffer->isLooping;
-}
-
-
-void draudio_set_playback_position(draudio_buffer* pBuffer, unsigned int position)
-{
- if (pBuffer == NULL) {
- return;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- pBuffer->pDevice->pContext->set_playback_position(pBuffer, position);
-}
-
-unsigned int draudio_get_playback_position(draudio_buffer* pBuffer)
-{
- if (pBuffer == NULL) {
- return 0;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- return pBuffer->pDevice->pContext->get_playback_position(pBuffer);
-}
-
-
-void draudio_set_pan(draudio_buffer* pBuffer, float pan)
-{
- if (pBuffer == NULL) {
- return;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- pBuffer->pDevice->pContext->set_pan(pBuffer, pan);
-}
-
-float draudio_get_pan(draudio_buffer* pBuffer)
-{
- if (pBuffer == NULL) {
- return 0.0f;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- return pBuffer->pDevice->pContext->get_pan(pBuffer);
-}
-
-
-void draudio_set_volume(draudio_buffer* pBuffer, float volume)
-{
- if (pBuffer == NULL) {
- return;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- pBuffer->pDevice->pContext->set_volume(pBuffer, volume);
-}
-
-float draudio_get_volume(draudio_buffer* pBuffer)
-{
- if (pBuffer == NULL) {
- return 1.0f;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- return pBuffer->pDevice->pContext->get_volume(pBuffer);
-}
-
-
-void draudio_remove_markers(draudio_buffer* pBuffer)
-{
- if (pBuffer == NULL) {
- return;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- pBuffer->pDevice->pContext->remove_markers(pBuffer);
-}
-
-bool draudio_register_marker_callback(draudio_buffer* pBuffer, size_t offsetInBytes, draudio_event_callback_proc callback, unsigned int eventID, void* pUserData)
-{
- if (pBuffer == NULL) {
- return false;
- }
-
- if (eventID == DRAUDIO_EVENT_ID_STOP ||
- eventID == DRAUDIO_EVENT_ID_PAUSE ||
- eventID == DRAUDIO_EVENT_ID_PLAY)
- {
- return false;
- }
-
- if (draudio_get_playback_state(pBuffer) != draudio_stopped) {
- return false;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- return pBuffer->pDevice->pContext->register_marker_callback(pBuffer, offsetInBytes, callback, eventID, pUserData);
-}
-
-bool draudio_register_stop_callback(draudio_buffer* pBuffer, draudio_event_callback_proc callback, void* pUserData)
-{
- if (pBuffer == NULL) {
- return false;
- }
-
- if (callback != NULL && draudio_get_playback_state(pBuffer) != draudio_stopped) {
- return false;
- }
-
- pBuffer->stopCallback.callback = callback;
- pBuffer->stopCallback.pUserData = pUserData;
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- return pBuffer->pDevice->pContext->register_stop_callback(pBuffer, callback, pUserData);
-}
-
-bool draudio_register_pause_callback(draudio_buffer* pBuffer, draudio_event_callback_proc callback, void* pUserData)
-{
- if (pBuffer == NULL) {
- return false;
- }
-
- if (callback != NULL && draudio_get_playback_state(pBuffer) != draudio_stopped) {
- return false;
- }
-
- pBuffer->pauseCallback.callback = callback;
- pBuffer->pauseCallback.pUserData = pUserData;
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- return pBuffer->pDevice->pContext->register_pause_callback(pBuffer, callback, pUserData);
-}
-
-bool draudio_register_play_callback(draudio_buffer* pBuffer, draudio_event_callback_proc callback, void* pUserData)
-{
- if (pBuffer == NULL) {
- return false;
- }
-
- if (callback != NULL && draudio_get_playback_state(pBuffer) != draudio_stopped) {
- return false;
- }
-
- pBuffer->playCallback.callback = callback;
- pBuffer->playCallback.pUserData = pUserData;
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- return pBuffer->pDevice->pContext->register_play_callback(pBuffer, callback, pUserData);
-}
-
-
-draudio_event_callback draudio_get_stop_callback(draudio_buffer* pBuffer)
-{
- if (pBuffer != NULL) {
- return pBuffer->stopCallback;
- } else {
- draudio_event_callback result = {NULL, NULL};
- return result;
- }
-}
-
-draudio_event_callback draudio_get_pause_callback(draudio_buffer* pBuffer)
-{
- if (pBuffer != NULL) {
- return pBuffer->pauseCallback;
- } else {
- draudio_event_callback result = {NULL, NULL};
- return result;
- }
-}
-
-draudio_event_callback draudio_get_play_callback(draudio_buffer* pBuffer)
-{
- if (pBuffer != NULL) {
- return pBuffer->playCallback;
- } else {
- draudio_event_callback result = {NULL, NULL};
- return result;
- }
-}
-
-
-void draudio_set_position(draudio_buffer* pBuffer, float x, float y, float z)
-{
- if (pBuffer == NULL) {
- return;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- pBuffer->pDevice->pContext->set_position(pBuffer, x, y, z);
-}
-
-void draudio_get_position(draudio_buffer* pBuffer, float* pPosOut)
-{
- if (pBuffer == NULL) {
- return;
- }
-
- if (pPosOut == NULL) {
- return;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- pBuffer->pDevice->pContext->get_position(pBuffer, pPosOut);
-}
-
-
-void draudio_set_listener_position(draudio_device* pDevice, float x, float y, float z)
-{
- if (pDevice == NULL) {
- return;
- }
-
- pDevice->pContext->set_listener_position(pDevice, x, y, z);
-}
-
-void draudio_get_listener_position(draudio_device* pDevice, float* pPosOut)
-{
- if (pDevice == NULL || pPosOut == NULL) {
- return;
- }
-
- pDevice->pContext->get_listener_position(pDevice, pPosOut);
-}
-
-void draudio_set_listener_orientation(draudio_device* pDevice, float forwardX, float forwardY, float forwardZ, float upX, float upY, float upZ)
-{
- if (pDevice == NULL) {
- return;
- }
-
- pDevice->pContext->set_listener_orientation(pDevice, forwardX, forwardY, forwardZ, upX, upY, upZ);
-}
-
-void draudio_get_listener_orientation(draudio_device* pDevice, float* pForwardOut, float* pUpOut)
-{
- if (pDevice == NULL) {
- return;
- }
-
- pDevice->pContext->get_listener_orientation(pDevice, pForwardOut, pUpOut);
-}
-
-
-void draudio_set_3d_mode(draudio_buffer* pBuffer, draudio_3d_mode mode)
-{
- if (pBuffer == NULL) {
- return;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- pBuffer->pDevice->pContext->set_3d_mode(pBuffer, mode);
-}
-
-draudio_3d_mode draudio_get_3d_mode(draudio_buffer* pBuffer)
-{
- if (pBuffer == NULL) {
- return draudio_3d_mode_disabled;
- }
-
- assert(pBuffer->pDevice != NULL);
- assert(pBuffer->pDevice->pContext != NULL);
- return pBuffer->pDevice->pContext->get_3d_mode(pBuffer);
-}
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-//
-// INPUT
-//
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-
-
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-//
-// HIGH-LEVEL API
-//
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-//// SYNCHRONIZATION ////
-
-#if defined(_WIN32)
-#include
-
-draudio_mutex draudio_create_mutex()
-{
- draudio_mutex mutex = malloc(sizeof(CRITICAL_SECTION));
- if (mutex != NULL)
- {
- InitializeCriticalSection((LPCRITICAL_SECTION)mutex);
- }
-
- return mutex;
-}
-
-void draudio_delete_mutex(draudio_mutex mutex)
-{
- DeleteCriticalSection((LPCRITICAL_SECTION)mutex);
- free(mutex);
-}
-
-void draudio_lock_mutex(draudio_mutex mutex)
-{
- EnterCriticalSection((LPCRITICAL_SECTION)mutex);
-}
-
-void draudio_unlock_mutex(draudio_mutex mutex)
-{
- LeaveCriticalSection((LPCRITICAL_SECTION)mutex);
-}
-#else
-#include
-
-draudio_mutex draudio_create_mutex()
-{
- pthread_mutex_t* mutex = malloc(sizeof(pthread_mutex_t));
- if (pthread_mutex_init(mutex, NULL) != 0) {
- free(mutex);
- mutex = NULL;
- }
-
- return mutex;
-}
-
-void draudio_delete_mutex(draudio_mutex mutex)
-{
- pthread_mutex_destroy(mutex);
-}
-
-void draudio_lock_mutex(draudio_mutex mutex)
-{
- pthread_mutex_lock(mutex);
-}
-
-void draudio_unlock_mutex(draudio_mutex mutex)
-{
- pthread_mutex_unlock(mutex);
-}
-#endif
-
-
-
-//// STREAMING ////
-
-#define DRAUDIO_STREAMING_MARKER_0 DRAUDIO_EVENT_ID_MARKER + 0
-#define DRAUDIO_STREAMING_MARKER_1 DRAUDIO_EVENT_ID_MARKER + 1
-
-#define DRAUDIO_STREAMING_CHUNK_INVALID 0
-
-typedef struct
-{
- /// The steaming buffer callbacks.
- draudio_streaming_callbacks callbacks;
-
- /// Keeps track of whether or not we are at the start of the playback.
- bool atStart;
-
- /// Keeps track of whether or not we should stop at the end of the next chunk.
- bool stopAtEndOfCurrentChunk;
-
- /// Keeps track of whether or not the sound should loop.
- bool isLoopingEnabled;
-
- /// The size of the extra data.
- size_t extraDataSize;
-
- /// The size of an individual chunk. A chunk is half the size of the buffer.
- size_t chunkSize;
-
- /// A pointer to the temporary buffer for loading chunk data.
- unsigned char pTempChunkData[1];
-
-} ea_streaming_buffer_data;
-
-
-bool ea_streaming_buffer_load_next_chunk(draudio_buffer* pBuffer, ea_streaming_buffer_data* pStreamingData, size_t offset, size_t chunkSize)
-{
- assert(pStreamingData != NULL);
- assert(pStreamingData->callbacks.read != NULL);
- assert(pStreamingData->callbacks.seek != NULL);
- assert(pStreamingData->chunkSize >= chunkSize);
-
- // A chunk size of 0 is valid, but we just return immediately.
- if (chunkSize == 0) {
- return true;
- }
-
- size_t bytesRead;
- if (!pStreamingData->callbacks.read(pStreamingData->callbacks.pUserData, pStreamingData->pTempChunkData, chunkSize, &bytesRead))
- {
- // There was an error reading the data. We might have run out of data.
- return false;
- }
-
-
- pStreamingData->stopAtEndOfCurrentChunk = false;
-
- draudio_set_buffer_data(pBuffer, offset, pStreamingData->pTempChunkData, bytesRead);
-
- if (chunkSize > bytesRead)
- {
- // The number of bytes read is less than our chunk size. This is our cue that we've reached the end of the steam. If we're looping, we
- // just seek back to the start and read more data. There is a chance the data total size of the streaming data is smaller than our
- // chunk, so we actually want to do this recursively.
- //
- // If we're not looping, we fill the remaining data with silence.
- if (pStreamingData->isLoopingEnabled)
- {
- pStreamingData->callbacks.seek(pStreamingData->callbacks.pUserData, 0);
- return ea_streaming_buffer_load_next_chunk(pBuffer, pStreamingData, offset + bytesRead, chunkSize - bytesRead);
- }
- else
- {
- memset(pStreamingData->pTempChunkData + bytesRead, 0, chunkSize - bytesRead);
- draudio_set_buffer_data(pBuffer, offset + bytesRead, pStreamingData->pTempChunkData + bytesRead, chunkSize - bytesRead);
-
- pStreamingData->stopAtEndOfCurrentChunk = true;
- }
- }
-
- return true;
-}
-
-void ea_steaming_buffer_marker_callback(draudio_buffer* pBuffer, unsigned int eventID, void *pUserData)
-{
- ea_streaming_buffer_data* pStreamingData = (ea_streaming_buffer_data*)pUserData;
- assert(pStreamingData != NULL);
-
- size_t offset = 0;
- if (eventID == DRAUDIO_STREAMING_MARKER_0) {
- offset = pStreamingData->chunkSize;
- }
-
- if (pStreamingData->stopAtEndOfCurrentChunk)
- {
- if (!pStreamingData->atStart) {
- draudio_stop(pBuffer);
- }
- }
- else
- {
- ea_streaming_buffer_load_next_chunk(pBuffer, pStreamingData, offset, pStreamingData->chunkSize);
- }
-
- pStreamingData->atStart = false;
-}
-
-
-draudio_buffer* draudio_create_streaming_buffer(draudio_device* pDevice, draudio_buffer_desc* pBufferDesc, draudio_streaming_callbacks callbacks, unsigned int extraDataSize)
-{
- if (callbacks.read == NULL) {
- return NULL;
- }
-
- if (pBufferDesc == NULL) {
- return NULL;
- }
-
-
- // We are determining for ourselves what the size of the buffer should be. We need to create our own copy rather than modify the input descriptor.
- draudio_buffer_desc bufferDesc = *pBufferDesc;
- bufferDesc.sizeInBytes = pBufferDesc->sampleRate * pBufferDesc->channels * (pBufferDesc->bitsPerSample / 8);
- bufferDesc.pData = NULL;
-
- size_t chunkSize = bufferDesc.sizeInBytes / 2;
-
- draudio_buffer* pBuffer = draudio_create_buffer(pDevice, &bufferDesc, sizeof(ea_streaming_buffer_data) - sizeof(unsigned char) + chunkSize + extraDataSize);
- if (pBuffer == NULL) {
- return NULL;
- }
-
-
- ea_streaming_buffer_data* pStreamingData = (ea_streaming_buffer_data*)draudio_get_buffer_extra_data(pBuffer);
- assert(pStreamingData != NULL);
-
- pStreamingData->callbacks = callbacks;
- pStreamingData->atStart = true;
- pStreamingData->stopAtEndOfCurrentChunk = false;
- pStreamingData->isLoopingEnabled = false;
- pStreamingData->chunkSize = chunkSize;
-
- // Register two markers - one for the first half and another for the second half. When a half is finished playing we need to
- // replace it with new data.
- draudio_register_marker_callback(pBuffer, 0, ea_steaming_buffer_marker_callback, DRAUDIO_STREAMING_MARKER_0, pStreamingData);
- draudio_register_marker_callback(pBuffer, chunkSize, ea_steaming_buffer_marker_callback, DRAUDIO_STREAMING_MARKER_1, pStreamingData);
-
-
- return pBuffer;
-}
-
-
-size_t draudio_get_streaming_buffer_extra_data_size(draudio_buffer* pBuffer)
-{
- if (pBuffer == NULL) {
- return 0;
- }
-
- ea_streaming_buffer_data* pStreamingData = (ea_streaming_buffer_data*)draudio_get_buffer_extra_data(pBuffer);
- assert(pStreamingData != NULL);
-
- return pStreamingData->extraDataSize;
-}
-
-void* draudio_get_streaming_buffer_extra_data(draudio_buffer* pBuffer)
-{
- if (pBuffer == NULL) {
- return NULL;
- }
-
- ea_streaming_buffer_data* pStreamingData = (ea_streaming_buffer_data*)draudio_get_buffer_extra_data(pBuffer);
- assert(pStreamingData != NULL);
-
- return ((char*)pStreamingData->pTempChunkData) + pStreamingData->chunkSize;
-}
-
-
-bool draudio_play_streaming_buffer(draudio_buffer* pBuffer, bool loop)
-{
- if (pBuffer == NULL) {
- return false;
- }
-
-
- ea_streaming_buffer_data* pStreamingData = (ea_streaming_buffer_data*)draudio_get_buffer_extra_data(pBuffer);
- assert(pStreamingData != NULL);
-
- // If the buffer was previously in a paused state, we just play like normal. If it was in a stopped state we need to start from the beginning.
- if (draudio_get_playback_state(pBuffer) == draudio_stopped)
- {
- // We need to load some initial data into the first chunk.
- pStreamingData->atStart = true;
- pStreamingData->callbacks.seek(pStreamingData->callbacks.pUserData, 0);
-
- if (!ea_streaming_buffer_load_next_chunk(pBuffer, pStreamingData, 0, pStreamingData->chunkSize))
- {
- // There was an error loading the initial data.
- return false;
- }
- }
-
-
- pStreamingData->isLoopingEnabled = loop;
- draudio_play(pBuffer, true); // <-- Always loop on a streaming buffer. Actual looping is done a bit differently for streaming buffers.
-
- return true;
-}
-
-bool draudio_is_streaming_buffer_looping(draudio_buffer* pBuffer)
-{
- if (pBuffer == NULL) {
- return false;
- }
-
- ea_streaming_buffer_data* pStreamingData = (ea_streaming_buffer_data*)draudio_get_buffer_extra_data(pBuffer);
- assert(pStreamingData != NULL);
-
- return pStreamingData->isLoopingEnabled;
-}
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// Sound World
-//
-///////////////////////////////////////////////////////////////////////////////
-
-DRAUDIO_PRIVATE draudio_bool draudio_on_sound_read_callback(void* pUserData, void* pDataOut, size_t bytesToRead, size_t* bytesReadOut)
-{
- draudio_sound* pSound = (draudio_sound*)pUserData;
- assert(pSound != NULL);
- assert(pSound->onRead != NULL);
-
- bool result = false;
- draudio_lock_mutex(pSound->pWorld->lock);
- {
- if (!pSound->markedForDeletion) {
- result = pSound->onRead(pSound, pDataOut, bytesToRead, bytesReadOut);
- }
- }
- draudio_unlock_mutex(pSound->pWorld->lock);
-
- return result;
-}
-
-DRAUDIO_PRIVATE static draudio_bool draudio_on_sound_seek_callback(void* pUserData, size_t offsetInBytesFromStart)
-{
- draudio_sound* pSound = (draudio_sound*)pUserData;
- assert(pSound != NULL);
- assert(pSound->onRead != NULL);
-
- bool result = false;
- draudio_lock_mutex(pSound->pWorld->lock);
- {
- if (!pSound->markedForDeletion) {
- result = pSound->onSeek(pSound, offsetInBytesFromStart);
- }
- }
- draudio_unlock_mutex(pSound->pWorld->lock);
-
- return result;
-}
-
-
-DRAUDIO_PRIVATE static void draudio_inline_sound_stop_callback(draudio_buffer* pBuffer, unsigned int eventID, void *pUserData)
-{
- (void)pBuffer;
- (void)eventID;
-
- assert(pBuffer != NULL);
- assert(eventID == DRAUDIO_EVENT_ID_STOP);
- assert(pUserData != NULL);
-
- draudio_sound* pSound = (draudio_sound*)pUserData;
- draudio_delete_sound(pSound);
-}
-
-
-DRAUDIO_PRIVATE void draudio_prepend_sound(draudio_sound* pSound)
-{
- assert(pSound != NULL);
- assert(pSound->pWorld != NULL);
- assert(pSound->pPrevSound == NULL);
-
- draudio_lock_mutex(pSound->pWorld->lock);
- {
- pSound->pNextSound = pSound->pWorld->pFirstSound;
-
- if (pSound->pNextSound != NULL) {
- pSound->pNextSound->pPrevSound = pSound;
- }
-
- pSound->pWorld->pFirstSound = pSound;
- }
- draudio_unlock_mutex(pSound->pWorld->lock);
-}
-
-DRAUDIO_PRIVATE void draudio_remove_sound_nolock(draudio_sound* pSound)
-{
- assert(pSound != NULL);
- assert(pSound->pWorld != NULL);
-
- if (pSound == pSound->pWorld->pFirstSound) {
- pSound->pWorld->pFirstSound = pSound->pNextSound;
- }
-
- if (pSound->pNextSound != NULL) {
- pSound->pNextSound->pPrevSound = pSound->pPrevSound;
- }
-
- if (pSound->pPrevSound != NULL) {
- pSound->pPrevSound->pNextSound = pSound->pNextSound;
- }
-}
-
-
-DRAUDIO_PRIVATE bool draudio_is_inline_sound(draudio_sound* pSound)
-{
- assert(pSound != NULL);
- return draudio_get_stop_callback(pSound->pBuffer).callback == draudio_inline_sound_stop_callback;
-}
-
-
-draudio_world* draudio_create_world(draudio_device* pDevice)
-{
- draudio_world* pWorld = (draudio_world*)malloc(sizeof(*pWorld));
- if (pWorld != NULL)
- {
- pWorld->pDevice = pDevice;
- pWorld->playbackState = draudio_playing;
- pWorld->pFirstSound = NULL;
- pWorld->lock = draudio_create_mutex();
- }
-
- return pWorld;
-}
-
-void draudio_delete_world(draudio_world* pWorld)
-{
- if (pWorld == NULL) {
- return;
- }
-
- // Delete every sound first.
- draudio_delete_all_sounds(pWorld);
-
- // Delete the lock after deleting every sound because we still need thread-safety at this point.
- draudio_delete_mutex(pWorld->lock);
-
- // Free the world last.
- free(pWorld);
-}
-
-
-draudio_sound* draudio_create_sound(draudio_world* pWorld, draudio_sound_desc desc)
-{
- if (pWorld == NULL) {
- return NULL;
- }
-
- if ((desc.pData == NULL || desc.sizeInBytes == 0) && (desc.onRead == NULL || desc.onSeek == NULL)) {
- // When streaming is not being used, the initial data must be valid at creation time.
- return NULL;
- }
-
- draudio_sound* pSound = (draudio_sound*)malloc(sizeof(*pSound));
- if (pSound == NULL) {
- return NULL;
- }
-
- pSound->pWorld = pWorld;
- pSound->prevPlaybackState = draudio_stopped;
- pSound->pNextSound = NULL;
- pSound->pPrevSound = NULL;
- pSound->isUsingStreamingBuffer = desc.sizeInBytes == 0 || desc.pData == NULL;
- pSound->markedForDeletion = false;
- pSound->onDelete = desc.onDelete;
- pSound->onRead = desc.onRead;
- pSound->onSeek = desc.onSeek;
-
- draudio_buffer_desc bufferDesc;
- bufferDesc.flags = desc.flags;
- bufferDesc.format = desc.format;
- bufferDesc.channels = desc.channels;
- bufferDesc.sampleRate = desc.sampleRate;
- bufferDesc.bitsPerSample = desc.bitsPerSample;
- bufferDesc.sizeInBytes = desc.sizeInBytes;
- bufferDesc.pData = desc.pData;
-
- if (pSound->isUsingStreamingBuffer)
- {
- draudio_streaming_callbacks streamingCallbacks;
- streamingCallbacks.pUserData = pSound;
- streamingCallbacks.read = draudio_on_sound_read_callback;
- streamingCallbacks.seek = draudio_on_sound_seek_callback;
-
- pSound->pBuffer = draudio_create_streaming_buffer(pWorld->pDevice, &bufferDesc, streamingCallbacks, desc.extraDataSize);
- if (pSound->pBuffer != NULL && desc.pExtraData != NULL)
- {
- memcpy(draudio_get_streaming_buffer_extra_data(pSound->pBuffer), desc.pExtraData, desc.extraDataSize);
- }
- }
- else
- {
- pSound->pBuffer = draudio_create_buffer(pWorld->pDevice, &bufferDesc, desc.extraDataSize);
- if (pSound->pBuffer != NULL && desc.pExtraData != NULL)
- {
- memcpy(draudio_get_buffer_extra_data(pSound->pBuffer), desc.pExtraData, desc.extraDataSize);
- }
- }
-
-
- // Return NULL if we failed to create the internal audio buffer.
- if (pSound->pBuffer == NULL) {
- free(pSound);
- return NULL;
- }
-
-
- // Only attach the sound to the internal list at the end when we know everything has worked.
- draudio_prepend_sound(pSound);
-
- return pSound;
-}
-
-void draudio_delete_sound(draudio_sound* pSound)
-{
- if (pSound == NULL) {
- return;
- }
-
-
- draudio_lock_mutex(pSound->pWorld->lock);
- {
- if (pSound->markedForDeletion) {
- assert(false);
- return;
- }
-
- pSound->markedForDeletion = true;
-
-
- // Remove the sound from the internal list first.
- draudio_remove_sound_nolock(pSound);
-
-
- // If we're deleting an inline sound, we want to remove the stop event callback. If we don't do this, we'll end up trying to delete
- // the sound twice.
- if (draudio_is_inline_sound(pSound)) {
- draudio_register_stop_callback(pSound->pBuffer, NULL, NULL);
- }
-
-
- // Let the application know that the sound is being deleted. We want to do this after removing the stop event just to be sure the
- // application doesn't try to explicitly stop the sound in this callback - that would be a problem for inlined sounds because they
- // are configured to delete themselves upon stopping which we are already in the process of doing.
- if (pSound->onDelete != NULL) {
- pSound->onDelete(pSound);
- }
-
-
- // Delete the internal audio buffer before letting the host application know about the deletion.
- draudio_delete_buffer(pSound->pBuffer);
- }
- draudio_unlock_mutex(pSound->pWorld->lock);
-
-
- // Only free the sound after the application has been made aware the sound is being deleted.
- free(pSound);
-}
-
-void draudio_delete_all_sounds(draudio_world* pWorld)
-{
- if (pWorld == NULL) {
- return;
- }
-
- while (pWorld->pFirstSound != NULL) {
- draudio_delete_sound(pWorld->pFirstSound);
- }
-}
-
-
-size_t draudio_get_sound_extra_data_size(draudio_sound* pSound)
-{
- if (pSound == NULL) {
- return 0;
- }
-
- if (pSound->isUsingStreamingBuffer) {
- return draudio_get_streaming_buffer_extra_data_size(pSound->pBuffer);
- } else {
- return draudio_get_buffer_extra_data_size(pSound->pBuffer);
- }
-}
-
-void* draudio_get_sound_extra_data(draudio_sound* pSound)
-{
- if (pSound == NULL) {
- return NULL;
- }
-
- if (pSound->isUsingStreamingBuffer) {
- return draudio_get_streaming_buffer_extra_data(pSound->pBuffer);
- } else {
- return draudio_get_buffer_extra_data(pSound->pBuffer);
- }
-}
-
-
-void draudio_play_sound(draudio_sound* pSound, bool loop)
-{
- if (pSound != NULL) {
- if (pSound->isUsingStreamingBuffer) {
- draudio_play_streaming_buffer(pSound->pBuffer, loop);
- } else {
- draudio_play(pSound->pBuffer, loop);
- }
- }
-}
-
-void draudio_pause_sound(draudio_sound* pSound)
-{
- if (pSound != NULL) {
- draudio_pause(pSound->pBuffer);
- }
-}
-
-void draudio_stop_sound(draudio_sound* pSound)
-{
- if (pSound != NULL) {
- draudio_stop(pSound->pBuffer);
- }
-}
-
-draudio_playback_state draudio_get_sound_playback_state(draudio_sound* pSound)
-{
- if (pSound == NULL) {
- return draudio_stopped;
- }
-
- return draudio_get_playback_state(pSound->pBuffer);
-}
-
-bool draudio_is_sound_looping(draudio_sound* pSound)
-{
- if (pSound == NULL) {
- return false;
- }
-
- if (pSound->isUsingStreamingBuffer) {
- return draudio_is_streaming_buffer_looping(pSound->pBuffer);
- } else {
- return draudio_is_looping(pSound->pBuffer);
- }
-}
-
-
-
-void draudio_play_inline_sound(draudio_world* pWorld, draudio_sound_desc desc)
-{
- if (pWorld == NULL) {
- return;
- }
-
- // We need to explicitly ensure 3D positioning is disabled.
- desc.flags &= ~DRAUDIO_ENABLE_3D;
-
- draudio_sound* pSound = draudio_create_sound(pWorld, desc);
- if (pSound != NULL)
- {
- // For inline sounds we set a callback for when the sound is stopped. When this callback is fired, the sound is deleted.
- draudio_set_sound_stop_callback(pSound, draudio_inline_sound_stop_callback, pSound);
-
- // Start playing the sound once everything else has been set up.
- draudio_play_sound(pSound, false);
- }
-}
-
-void draudio_play_inline_sound_3f(draudio_world* pWorld, draudio_sound_desc desc, float posX, float posY, float posZ)
-{
- if (pWorld == NULL) {
- return;
- }
-
- draudio_sound* pSound = draudio_create_sound(pWorld, desc);
- if (pSound != NULL)
- {
- // For inline sounds we set a callback for when the sound is stopped. When this callback is fired, the sound is deleted.
- draudio_set_sound_stop_callback(pSound, draudio_inline_sound_stop_callback, pSound);
-
- // Set the position before playing anything.
- draudio_set_sound_position(pSound, posX, posY, posZ);
-
- // Start playing the sound once everything else has been set up.
- draudio_play_sound(pSound, false);
- }
-}
-
-
-void draudio_stop_all_sounds(draudio_world* pWorld)
-{
- if (pWorld == NULL) {
- return;
- }
-
- bool wasPlaying = pWorld->playbackState == draudio_playing;
- if (pWorld->playbackState != draudio_stopped)
- {
- // We need to loop over every sound and stop them. We also need to keep track of their previous playback state
- // so that when resume_all_sounds() is called, it can be restored correctly.
- for (draudio_sound* pSound = pWorld->pFirstSound; pSound != NULL; pSound = pSound->pNextSound)
- {
- if (wasPlaying) {
- pSound->prevPlaybackState = draudio_get_sound_playback_state(pSound);
- }
-
- draudio_stop_sound(pSound);
- }
- }
-}
-
-void draudio_pause_all_sounds(draudio_world* pWorld)
-{
- if (pWorld == NULL) {
- return;
- }
-
- if (pWorld->playbackState == draudio_playing)
- {
- // We need to loop over every sound and stop them. We also need to keep track of their previous playback state
- // so that when resume_all_sounds() is called, it can be restored correctly.
- for (draudio_sound* pSound = pWorld->pFirstSound; pSound != NULL; pSound = pSound->pNextSound)
- {
- pSound->prevPlaybackState = draudio_get_sound_playback_state(pSound);
- draudio_pause_sound(pSound);
- }
- }
-}
-
-void draudio_resume_all_sounds(draudio_world* pWorld)
-{
- if (pWorld == NULL) {
- return;
- }
-
- if (pWorld->playbackState != draudio_playing)
- {
- // When resuming playback, we use the previous playback state to determine how to resume.
- for (draudio_sound* pSound = pWorld->pFirstSound; pSound != NULL; pSound = pSound->pNextSound)
- {
- if (pSound->prevPlaybackState == draudio_playing) {
- draudio_play_sound(pSound, draudio_is_sound_looping(pSound));
- }
- }
- }
-}
-
-
-void draudio_set_sound_stop_callback(draudio_sound* pSound, draudio_event_callback_proc callback, void* pUserData)
-{
- if (pSound != NULL) {
- draudio_register_stop_callback(pSound->pBuffer, callback, pUserData);
- }
-}
-
-void draudio_set_sound_pause_callback(draudio_sound* pSound, draudio_event_callback_proc callback, void* pUserData)
-{
- if (pSound != NULL) {
- draudio_register_pause_callback(pSound->pBuffer, callback, pUserData);
- }
-}
-
-void draudio_set_sound_play_callback(draudio_sound* pSound, draudio_event_callback_proc callback, void* pUserData)
-{
- if (pSound != NULL) {
- draudio_register_play_callback(pSound->pBuffer, callback, pUserData);
- }
-}
-
-
-void draudio_set_sound_position(draudio_sound* pSound, float posX, float posY, float posZ)
-{
- if (pSound != NULL) {
- draudio_set_position(pSound->pBuffer, posX, posY, posZ);
- }
-}
-
-
-void draudio_set_sound_3d_mode(draudio_sound* pSound, draudio_3d_mode mode)
-{
- if (pSound != NULL) {
- draudio_set_3d_mode(pSound->pBuffer, mode);
- }
-}
-
-draudio_3d_mode draudio_get_sound_3d_mode(draudio_sound* pSound)
-{
- if (pSound == NULL) {
- return draudio_3d_mode_disabled;
- }
-
- return draudio_get_3d_mode(pSound->pBuffer);
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-//
-// BACKENDS
-//
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// DirectSound
-//
-// The DirectSound backend is mostly simple, except for event handling. Events
-// are achieved through the use of Win32 event objects and waiting on them to
-// be put into a signaled state by DirectSound. Due to this mechanic we need to
-// create a worker thread that waits on each event.
-//
-// The worker thread waits on three general types of events. The first is an
-// event that is signaled when the thread needs to be terminated. The second
-// is an event that is signaled when a new set of events need to be waited on.
-// The third is a set of events that correspond to an output buffer event (such
-// as stop, pause, play and marker events.)
-//
-///////////////////////////////////////////////////////////////////////////////
-#ifdef DRAUDIO_BUILD_DSOUND
-#include
-#include
-#include
-#include // For testing and debugging with printf(). Delete this later.
-
-// Define a NULL GUID for use later on. If we don't do this and use GUID_NULL we'll end
-// up with a link error.
-GUID DRAUDIO_GUID_NULL = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
-
-
-//// Message Queue ////
-
-#define DRAUDIO_MESSAGE_ID_UNKNOWN 0
-#define DRAUDIO_MESSAGE_ID_EVENT 1
-#define DRAUDIO_MESSAGE_ID_DELETE_BUFFER 2
-#define DRAUDIO_MESSAGE_ID_DELETE_DEVICE 3
-#define DRAUDIO_MESSAGE_ID_TERMINATE_THREAD 4
-
-/// Structure representing an individual message
-typedef struct
-{
- /// The message ID.
- int id;
-
- /// A pointer to the relevant buffer.
- draudio_buffer* pBuffer;
-
-
- // The message-specific data.
- union
- {
- struct
- {
- /// A pointer to the callback function.
- draudio_event_callback_proc callback;
-
- /// The event ID.
- unsigned int eventID;
-
- /// The callback user data.
- void* pUserData;
-
- } callback_event;
-
-
- struct
- {
- /// A pointer to the DirectSound buffer object.
- LPDIRECTSOUNDBUFFER8 pDSBuffer;
-
- /// A pointer to the 3D DirectSound buffer object. This will be NULL if 3D positioning is disabled for the buffer.
- LPDIRECTSOUND3DBUFFER pDSBuffer3D;
-
- /// A pointer to the object for handling notification events.
- LPDIRECTSOUNDNOTIFY pDSNotify;
-
- } delete_buffer;
-
-
- struct
- {
- /// A pointer to the DIRECTSOUND object that was created with DirectSoundCreate8().
- LPDIRECTSOUND8 pDS;
-
- /// A pointer to the DIRECTSOUNDBUFFER object for the primary buffer.
- LPDIRECTSOUNDBUFFER pDSPrimaryBuffer;
-
- /// A pointer to the DIRECTSOUND3DLISTENER8 object associated with the device.
- LPDIRECTSOUND3DLISTENER pDSListener;
-
- /// A pointer to the device object being deleted.
- draudio_device* pDevice;
-
- } delete_device;
-
- } data;
-
-} draudio_message_dsound;
-
-
-/// Structure representing the main message queue.
-///
-/// The message queue is implemented as a fixed-sized cyclic array which means there should be no significant data
-/// movement and fast pushing and popping.
-typedef struct
-{
- /// The buffer containing the list of events.
- draudio_message_dsound messages[DRAUDIO_MAX_MESSAGE_QUEUE_SIZE];
-
- /// The number of active messages in the queue.
- unsigned int messageCount;
-
- /// The index of the first message in the queue.
- unsigned int iFirstMessage;
-
- /// The mutex for synchronizing access to message pushing and popping.
- draudio_mutex queueLock;
-
- /// The semaphore that's used for blocking draudio_next_message_dsound(). The maximum value is set to DRAUDIO_MAX_MESSAGE_QUEUE_SIZE.
- HANDLE hMessageSemaphore;
-
- /// The message handling thread.
- HANDLE hMessageHandlingThread;
-
- /// Keeps track of whether or not the queue is deleted. We use this to ensure a thread does not try to post an event.
- bool isDeleted;
-
-} draudio_message_queue_dsound;
-
-
-/// The function to run on the message handling thread. This is implemented down the bottom.
-DWORD WINAPI MessageHandlingThread_DSound(draudio_message_queue_dsound* pQueue);
-
-/// Posts a new message. This is thread safe.
-void draudio_post_message_dsound(draudio_message_queue_dsound* pQueue, draudio_message_dsound msg);
-
-
-
-/// Initializes the given mesasge queue.
-bool draudio_init_message_queue_dsound(draudio_message_queue_dsound* pQueue)
-{
- if (pQueue == NULL) {
- return false;
- }
-
- pQueue->messageCount = 0;
- pQueue->iFirstMessage = 0;
-
- pQueue->queueLock = draudio_create_mutex();
- if (pQueue->queueLock == NULL) {
- return false;
- }
-
- pQueue->hMessageSemaphore = CreateSemaphoreA(NULL, 0, DRAUDIO_MAX_MESSAGE_QUEUE_SIZE, NULL);
- if (pQueue->hMessageSemaphore == NULL)
- {
- draudio_delete_mutex(pQueue->queueLock);
- return false;
- }
-
- pQueue->hMessageHandlingThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MessageHandlingThread_DSound, pQueue, 0, NULL);
- if (pQueue->hMessageHandlingThread == NULL)
- {
- CloseHandle(pQueue->hMessageSemaphore);
- draudio_delete_mutex(pQueue->queueLock);
- return false;
- }
-
- pQueue->isDeleted = false;
-
- return true;
-}
-
-/// Uninitializes the given message queue.
-void draudio_uninit_message_queue_dsound(draudio_message_queue_dsound* pQueue)
-{
- // We need to make sure the thread is closed properly before returning from here. To do this we just post an DRAUDIO_MESSAGE_ID_TERMINATE_THREAD
- // event to the message queue and wait for the thread to finish.
- draudio_message_dsound msg;
- msg.id = DRAUDIO_MESSAGE_ID_TERMINATE_THREAD;
- draudio_post_message_dsound(pQueue, msg);
-
-
- // Once we posted the event we just wait for the thread to process it and terminate.
- WaitForSingleObject(pQueue->hMessageHandlingThread, INFINITE);
-
- // At this point the thread has been terminated and we can clear everything.
- CloseHandle(pQueue->hMessageHandlingThread);
- pQueue->hMessageHandlingThread = NULL;
-
- CloseHandle(pQueue->hMessageSemaphore);
- pQueue->hMessageSemaphore = NULL;
-
-
- pQueue->isDeleted = true;
- draudio_lock_mutex(pQueue->queueLock);
- {
- pQueue->messageCount = 0;
- pQueue->iFirstMessage = 0;
- }
- draudio_unlock_mutex(pQueue->queueLock);
-
- draudio_delete_mutex(pQueue->queueLock);
- pQueue->queueLock = NULL;
-}
-
-
-void draudio_post_message_dsound(draudio_message_queue_dsound* pQueue, draudio_message_dsound msg)
-{
- assert(pQueue != NULL);
-
- if (pQueue->isDeleted) {
- return;
- }
-
- draudio_lock_mutex(pQueue->queueLock);
- {
- if (pQueue->messageCount < DRAUDIO_MAX_MESSAGE_QUEUE_SIZE)
- {
- pQueue->messages[(pQueue->iFirstMessage + pQueue->messageCount) % DRAUDIO_MAX_MESSAGE_QUEUE_SIZE] = msg;
- pQueue->messageCount += 1;
-
- ReleaseSemaphore(pQueue->hMessageSemaphore, 1, NULL);
- }
- }
- draudio_unlock_mutex(pQueue->queueLock);
-}
-
-
-/// Retrieves the next message in the queue.
-///
-/// @remarks
-/// This blocks until a message is available. This will return false when it receives the DRAUDIO_MESSAGE_ID_TERMINATE_THREAD message.
-bool draudio_next_message_dsound(draudio_message_queue_dsound* pQueue, draudio_message_dsound* pMsgOut)
-{
- if (WaitForSingleObject(pQueue->hMessageSemaphore, INFINITE) == WAIT_OBJECT_0) // Wait for a message to become available.
- {
- draudio_message_dsound msg;
- msg.id = DRAUDIO_MESSAGE_ID_UNKNOWN;
-
- draudio_lock_mutex(pQueue->queueLock);
- {
- assert(pQueue->messageCount > 0);
-
- msg = pQueue->messages[pQueue->iFirstMessage];
-
- pQueue->iFirstMessage = (pQueue->iFirstMessage + 1) % DRAUDIO_MAX_MESSAGE_QUEUE_SIZE;
- pQueue->messageCount -= 1;
- }
- draudio_unlock_mutex(pQueue->queueLock);
-
-
- if (pMsgOut != NULL) {
- pMsgOut[0] = msg;
- }
-
- return msg.id != DRAUDIO_MESSAGE_ID_TERMINATE_THREAD;
- }
-
- return false;
-}
-
-
-DWORD WINAPI MessageHandlingThread_DSound(draudio_message_queue_dsound* pQueue)
-{
- assert(pQueue != NULL);
-
- draudio_message_dsound msg;
- while (draudio_next_message_dsound(pQueue, &msg))
- {
- assert(msg.id != DRAUDIO_MESSAGE_ID_TERMINATE_THREAD); // <-- draudio_next_message_dsound() will return false when it receives DRAUDIO_MESSAGE_ID_TERMINATE_THREAD.
-
- switch (msg.id)
- {
- case DRAUDIO_MESSAGE_ID_EVENT:
- {
- assert(msg.data.callback_event.callback != NULL);
-
- msg.data.callback_event.callback(msg.pBuffer, msg.data.callback_event.eventID, msg.data.callback_event.pUserData);
- break;
- }
-
- case DRAUDIO_MESSAGE_ID_DELETE_BUFFER:
- {
- if (msg.data.delete_buffer.pDSNotify != NULL) {
- IDirectSoundNotify_Release(msg.data.delete_buffer.pDSNotify);
- }
-
- if (msg.data.delete_buffer.pDSBuffer3D != NULL) {
- IDirectSound3DBuffer_Release(msg.data.delete_buffer.pDSBuffer3D);
- }
-
- if (msg.data.delete_buffer.pDSBuffer != NULL) {
- IDirectSoundBuffer8_Release(msg.data.delete_buffer.pDSBuffer);
- }
-
- free(msg.pBuffer);
- break;
- }
-
- case DRAUDIO_MESSAGE_ID_DELETE_DEVICE:
- {
- if (msg.data.delete_device.pDSListener != NULL) {
- IDirectSound3DListener_Release(msg.data.delete_device.pDSListener);
- }
-
- if (msg.data.delete_device.pDSPrimaryBuffer != NULL) {
- IDirectSoundBuffer_Release(msg.data.delete_device.pDSPrimaryBuffer);
- }
-
- if (msg.data.delete_device.pDS != NULL) {
- IDirectSound_Release(msg.data.delete_device.pDS);
- }
-
- free(msg.data.delete_device.pDevice);
- break;
- }
-
- default:
- {
- // Should never hit this.
- assert(false);
- break;
- }
- }
- }
-
- return 0;
-}
-
-
-
-
-/// Deactivates (but does not delete) every event associated with the given buffer.
-void draudio_deactivate_buffer_events_dsound(draudio_buffer* pBuffer);
-
-
-//// Event Management ////
-
-typedef struct draudio_event_manager_dsound draudio_event_manager_dsound;
-typedef struct draudio_event_dsound draudio_event_dsound;
-
-struct draudio_event_dsound
-{
- /// A pointer to the event manager that owns this event.
- draudio_event_manager_dsound* pEventManager;
-
- /// The event.
- HANDLE hEvent;
-
- /// The callback.
- draudio_event_callback_proc callback;
-
- /// A pointer to the applicable buffer.
- draudio_buffer* pBuffer;
-
- /// The event ID. For on_stop events, this will be set to DRAUDIO_EVENT_STOP
- unsigned int eventID;
-
- /// A pointer to the user data.
- void* pUserData;
-
- /// The marker offset. Only used for marker events. Should be set to 0 for non-markers.
- DWORD markerOffset;
-
- /// Events are stored in a linked list. This is a pointer to the next event in the list.
- draudio_event_dsound* pNextEvent;
-
- /// A pointer to the previous event.
- draudio_event_dsound* pPrevEvent;
-};
-
-struct draudio_event_manager_dsound
-{
- /// A pointer to the message queue where messages will be posted for event processing.
- draudio_message_queue_dsound* pMessageQueue;
-
-
- /// A handle to the event worker thread.
- HANDLE hThread;
-
- /// A handle to the terminator event object.
- HANDLE hTerminateEvent;
-
- /// A handle to the refresher event object.
- HANDLE hRefreshEvent;
-
- /// The mutex to use when refreshing the worker thread. This is used to ensure only one refresh is done at a time.
- draudio_mutex refreshMutex;
-
- /// The synchronization lock.
- draudio_mutex mainLock;
-
- /// The event object for notifying dr_audio when an event has finished being handled by the event handling thread.
- HANDLE hEventCompletionLock;
-
-
- /// The first event in a list.
- draudio_event_dsound* pFirstEvent;
-
- /// The last event in the list of events.
- draudio_event_dsound* pLastEvent;
-};
-
-
-/// Locks the event manager.
-void draudio_lock_events_dsound(draudio_event_manager_dsound* pEventManager)
-{
- draudio_lock_mutex(pEventManager->mainLock);
-}
-
-/// Unlocks the event manager.
-void draudio_unlock_events_dsound(draudio_event_manager_dsound* pEventManager)
-{
- draudio_unlock_mutex(pEventManager->mainLock);
-}
-
-
-/// Removes the given event from the event lists.
-///
-/// @remarks
-/// This will be used when moving the event to a new location in the list or when it is being deleted.
-void draudio_remove_event_dsound_nolock(draudio_event_dsound* pEvent)
-{
- assert(pEvent != NULL);
-
- draudio_event_manager_dsound* pEventManager = pEvent->pEventManager;
- assert(pEventManager != NULL);
-
- if (pEventManager->pFirstEvent == pEvent) {
- pEventManager->pFirstEvent = pEvent->pNextEvent;
- }
-
- if (pEventManager->pLastEvent == pEvent) {
- pEventManager->pLastEvent = pEvent->pPrevEvent;
- }
-
-
- if (pEvent->pPrevEvent != NULL) {
- pEvent->pPrevEvent->pNextEvent = pEvent->pNextEvent;
- }
-
- if (pEvent->pNextEvent != NULL) {
- pEvent->pNextEvent->pPrevEvent = pEvent->pPrevEvent;
- }
-
- pEvent->pNextEvent = NULL;
- pEvent->pPrevEvent = NULL;
-}
-
-/// @copydoc draudio_remove_event_dsound_nolock()
-void draudio_remove_event_dsound(draudio_event_dsound* pEvent)
-{
- assert(pEvent != NULL);
-
- draudio_event_manager_dsound* pEventManager = pEvent->pEventManager;
- draudio_lock_events_dsound(pEventManager);
- {
- draudio_remove_event_dsound_nolock(pEvent);
- }
- draudio_unlock_events_dsound(pEventManager);
-}
-
-/// Adds the given event to the end of the internal list.
-void draudio_append_event_dsound(draudio_event_dsound* pEvent)
-{
- assert(pEvent != NULL);
-
- draudio_event_manager_dsound* pEventManager = pEvent->pEventManager;
- draudio_lock_events_dsound(pEventManager);
- {
- draudio_remove_event_dsound_nolock(pEvent);
-
- assert(pEvent->pNextEvent == NULL);
-
- if (pEventManager->pLastEvent != NULL) {
- pEvent->pPrevEvent = pEventManager->pLastEvent;
- pEvent->pPrevEvent->pNextEvent = pEvent;
- }
-
- if (pEventManager->pFirstEvent == NULL) {
- pEventManager->pFirstEvent = pEvent;
- }
-
- pEventManager->pLastEvent = pEvent;
- }
- draudio_unlock_events_dsound(pEventManager);
-}
-
-void draudio_refresh_worker_thread_event_queue(draudio_event_manager_dsound* pEventManager)
-{
- assert(pEventManager != NULL);
-
- // To refresh the worker thread we just need to signal the refresh event. We then just need to wait for
- // processing to finish which we can do by waiting on another event to become signaled.
-
- draudio_lock_mutex(pEventManager->refreshMutex);
- {
- // Signal a refresh.
- SetEvent(pEventManager->hRefreshEvent);
-
- // Wait for refreshing to complete.
- WaitForSingleObject(pEventManager->hEventCompletionLock, INFINITE);
- }
- draudio_unlock_mutex(pEventManager->refreshMutex);
-}
-
-
-/// Closes the Win32 event handle of the given event.
-void draudio_close_win32_event_handle_dsound(draudio_event_dsound* pEvent)
-{
- assert(pEvent != NULL);
- assert(pEvent->pEventManager != NULL);
-
-
- // At the time of calling this function, pEvent should have been removed from the internal list. The issue is that
- // the event notification thread is waiting on it. Thus, we need to refresh the worker thread to ensure the event
- // have been flushed from that queue. To do this we just signal a special event that's used to trigger a refresh.
- draudio_refresh_worker_thread_event_queue(pEvent->pEventManager);
-
- // The worker thread should not be waiting on the event so we can go ahead and close the handle now.
- CloseHandle(pEvent->hEvent);
- pEvent->hEvent = NULL;
-}
-
-
-/// Updates the given event to use the given callback and user data.
-void draudio_update_event_dsound(draudio_event_dsound* pEvent, draudio_event_callback_proc callback, void* pUserData)
-{
- assert(pEvent != NULL);
-
- pEvent->callback = callback;
- pEvent->pUserData = pUserData;
-
- draudio_refresh_worker_thread_event_queue(pEvent->pEventManager);
-}
-
-/// Creates a new event, but does not activate it.
-///
-/// @remarks
-/// When an event is created, it just sits dormant and will never be triggered until it has been
-/// activated with draudio_activate_event_dsound().
-draudio_event_dsound* draudio_create_event_dsound(draudio_event_manager_dsound* pEventManager, draudio_event_callback_proc callback, draudio_buffer* pBuffer, unsigned int eventID, void* pUserData)
-{
- draudio_event_dsound* pEvent = (draudio_event_dsound*)malloc(sizeof(draudio_event_dsound));
- if (pEvent != NULL)
- {
- pEvent->pEventManager = pEventManager;
- pEvent->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
- pEvent->callback = NULL;
- pEvent->pBuffer = pBuffer;
- pEvent->eventID = eventID;
- pEvent->pUserData = NULL;
- pEvent->markerOffset = 0;
- pEvent->pNextEvent = NULL;
- pEvent->pPrevEvent = NULL;
-
- // Append the event to the internal list.
- draudio_append_event_dsound(pEvent);
-
- // This roundabout way of setting the callback and user data is to ensure the worker thread is made aware that it needs
- // to refresh it's local event data.
- draudio_update_event_dsound(pEvent, callback, pUserData);
- }
-
- return pEvent;
-}
-
-/// Deletes an event, and deactivates it.
-///
-/// @remarks
-/// This will not return until the event has been deleted completely.
-void draudio_delete_event_dsound(draudio_event_dsound* pEvent)
-{
- assert(pEvent != NULL);
-
- // Set everything to NULL so the worker thread is aware that the event is about to get deleted.
- pEvent->pBuffer = NULL;
- pEvent->callback = NULL;
- pEvent->eventID = 0;
- pEvent->pUserData = NULL;
- pEvent->markerOffset = 0;
-
- // Remove the event from the list.
- draudio_remove_event_dsound(pEvent);
-
- // Close the Win32 event handle.
- if (pEvent->hEvent != NULL) {
- draudio_close_win32_event_handle_dsound(pEvent);
- }
-
-
- // At this point everything has been closed so we can safely free the memory and return.
- free(pEvent);
-}
-
-
-/// Gathers the event handles and callback data for all of the relevant buffer events.
-unsigned int draudio_gather_events_dsound(draudio_event_manager_dsound *pEventManager, HANDLE* pHandlesOut, draudio_event_dsound** ppEventsOut, unsigned int outputBufferSize)
-{
- assert(pEventManager != NULL);
- assert(pHandlesOut != NULL);
- assert(ppEventsOut != NULL);
- assert(outputBufferSize >= 2);
-
- unsigned int i = 2;
- draudio_lock_events_dsound(pEventManager);
- {
- pHandlesOut[0] = pEventManager->hTerminateEvent;
- ppEventsOut[0] = NULL;
-
- pHandlesOut[1] = pEventManager->hRefreshEvent;
- ppEventsOut[1] = NULL;
-
-
- draudio_event_dsound* pEvent = pEventManager->pFirstEvent;
- while (i < outputBufferSize && pEvent != NULL)
- {
- if (pEvent->hEvent != NULL)
- {
- pHandlesOut[i] = pEvent->hEvent;
- ppEventsOut[i] = pEvent;
-
- i += 1;
- }
-
- pEvent = pEvent->pNextEvent;
- }
- }
- draudio_unlock_events_dsound(pEventManager);
-
- return i;
-}
-
-/// The entry point to the event worker thread.
-DWORD WINAPI DSound_EventWorkerThreadProc(draudio_event_manager_dsound *pEventManager)
-{
- if (pEventManager != NULL)
- {
- HANDLE hTerminateEvent = pEventManager->hTerminateEvent;
- HANDLE hRefreshEvent = pEventManager->hRefreshEvent;
-
- HANDLE eventHandles[1024];
- draudio_event_dsound* events[1024];
- unsigned int eventCount = draudio_gather_events_dsound(pEventManager, eventHandles, events, 1024); // <-- Initial gather.
-
- bool requestedRefresh = false;
- for (;;)
- {
- if (requestedRefresh)
- {
- eventCount = draudio_gather_events_dsound(pEventManager, eventHandles, events, 1024);
-
- // Refreshing is done, so now we need to let other threads know about it.
- SetEvent(pEventManager->hEventCompletionLock);
- requestedRefresh = false;
- }
-
-
-
- DWORD rc = WaitForMultipleObjects(eventCount, eventHandles, FALSE, INFINITE);
- if (rc >= WAIT_OBJECT_0 && rc < eventCount)
- {
- const unsigned int eventIndex = rc - WAIT_OBJECT_0;
- HANDLE hEvent = eventHandles[eventIndex];
-
- if (hEvent == hTerminateEvent)
- {
- // The terminator event was signaled. We just return from the thread immediately.
- return 0;
- }
-
- if (hEvent == hRefreshEvent)
- {
- assert(hRefreshEvent == pEventManager->hRefreshEvent);
-
- // This event will get signaled when a new set of events need to be waited on, such as when a new event has been registered on a buffer.
- requestedRefresh = true;
- continue;
- }
-
-
- // If we get here if means we have hit a callback event.
- draudio_event_dsound* pEvent = events[eventIndex];
- if (pEvent->callback != NULL)
- {
- assert(pEvent->hEvent == hEvent);
-
- // The stop event will be signaled by DirectSound when IDirectSoundBuffer::Stop() is called. The problem is that we need to call that when the
- // sound is paused as well. Thus, we need to check if we got the stop event, and if so DON'T call the callback function if it is in a non-stopped
- // state.
- bool isStopEventButNotStopped = pEvent->eventID == DRAUDIO_EVENT_ID_STOP && draudio_get_playback_state(pEvent->pBuffer) != draudio_stopped;
- if (!isStopEventButNotStopped)
- {
- // We don't call the callback directly. Instead we post a message to the message handling thread for processing later.
- draudio_message_dsound msg;
- msg.id = DRAUDIO_MESSAGE_ID_EVENT;
- msg.pBuffer = pEvent->pBuffer;
- msg.data.callback_event.callback = pEvent->callback;
- msg.data.callback_event.eventID = pEvent->eventID;
- msg.data.callback_event.pUserData = pEvent->pUserData;
- draudio_post_message_dsound(pEventManager->pMessageQueue, msg);
- }
- }
- }
- }
- }
-
- return 0;
-}
-
-
-/// Initializes the event manager by creating the thread and event objects.
-bool draudio_init_event_manager_dsound(draudio_event_manager_dsound* pEventManager, draudio_message_queue_dsound* pMessageQueue)
-{
- assert(pEventManager != NULL);
- assert(pMessageQueue != NULL);
-
- pEventManager->pMessageQueue = pMessageQueue;
-
- HANDLE hTerminateEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
- if (hTerminateEvent == NULL) {
- return false;
- }
-
- HANDLE hRefreshEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
- if (hRefreshEvent == NULL)
- {
- CloseHandle(hTerminateEvent);
- return false;
- }
-
- draudio_mutex refreshMutex = draudio_create_mutex();
- if (refreshMutex == NULL)
- {
- CloseHandle(hTerminateEvent);
- CloseHandle(hRefreshEvent);
- return false;
- }
-
- draudio_mutex mainLock = draudio_create_mutex();
- if (mainLock == NULL)
- {
- CloseHandle(hTerminateEvent);
- CloseHandle(hRefreshEvent);
- draudio_delete_mutex(refreshMutex);
- return false;
- }
-
- HANDLE hEventCompletionLock = CreateEventA(NULL, FALSE, FALSE, NULL);
- if (hEventCompletionLock == NULL)
- {
- CloseHandle(hTerminateEvent);
- CloseHandle(hRefreshEvent);
- draudio_delete_mutex(refreshMutex);
- draudio_delete_mutex(mainLock);
- return false;
- }
-
-
- HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)DSound_EventWorkerThreadProc, pEventManager, 0, NULL);
- if (hThread == NULL)
- {
- CloseHandle(hTerminateEvent);
- CloseHandle(hRefreshEvent);
- draudio_delete_mutex(refreshMutex);
- draudio_delete_mutex(mainLock);
- CloseHandle(hEventCompletionLock);
- return false;
- }
-
-
- pEventManager->hTerminateEvent = hTerminateEvent;
- pEventManager->hRefreshEvent = hRefreshEvent;
- pEventManager->refreshMutex = refreshMutex;
- pEventManager->mainLock = mainLock;
- pEventManager->hEventCompletionLock = hEventCompletionLock;
- pEventManager->hThread = hThread;
-
- pEventManager->pFirstEvent = NULL;
- pEventManager->pLastEvent = NULL;
-
- return true;
-}
-
-/// Shuts down the event manager by closing the thread and event objects.
-///
-/// @remarks
-/// This does not return until the worker thread has been terminated completely.
-/// @par
-/// This will delete every event, so any pointers to events will be made invalid upon calling this function.
-void draudio_uninit_event_manager_dsound(draudio_event_manager_dsound* pEventManager)
-{
- assert(pEventManager != NULL);
-
-
- // Cleanly delete every event first.
- while (pEventManager->pFirstEvent != NULL) {
- draudio_delete_event_dsound(pEventManager->pFirstEvent);
- }
-
-
-
- // Terminate the thread and wait for the thread to finish executing before freeing the context for real.
- SignalObjectAndWait(pEventManager->hTerminateEvent, pEventManager->hThread, INFINITE, FALSE);
-
- // Only delete the thread after it has returned naturally.
- CloseHandle(pEventManager->hThread);
- pEventManager->hThread = NULL;
-
-
- // Once the thread has been terminated we can delete the terminator and refresher events.
- CloseHandle(pEventManager->hTerminateEvent);
- pEventManager->hTerminateEvent = NULL;
-
- CloseHandle(pEventManager->hRefreshEvent);
- pEventManager->hRefreshEvent = NULL;
-
- draudio_delete_mutex(pEventManager->refreshMutex);
- pEventManager->refreshMutex = NULL;
-
- draudio_delete_mutex(pEventManager->mainLock);
- pEventManager->mainLock = NULL;
-
-
- CloseHandle(pEventManager->hEventCompletionLock);
- pEventManager->hEventCompletionLock = NULL;
-}
-
-
-//// End Event Management ////
-
-static GUID _g_DSListenerGUID = {0x279AFA84, 0x4981, 0x11CE, {0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60}};
-static GUID _g_DirectSoundBuffer8GUID = {0x6825a449, 0x7524, 0x4d82, {0x92, 0x0f, 0x50, 0xe3, 0x6a, 0xb3, 0xab, 0x1e}};
-static GUID _g_DirectSound3DBuffer8GUID = {0x279AFA86, 0x4981, 0x11CE, {0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60}};
-static GUID _g_DirectSoundNotifyGUID = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};
-static GUID _g_KSDATAFORMAT_SUBTYPE_PCM_GUID = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
-static GUID _g_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT_GUID = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
-
-#ifdef __cplusplus
-static GUID g_DSListenerGUID = _g_DSListenerGUID;
-static GUID g_DirectSoundBuffer8GUID = _g_DirectSoundBuffer8GUID;
-static GUID g_DirectSound3DBuffer8GUID = _g_DirectSound3DBuffer8GUID;
-static GUID g_DirectSoundNotifyGUID = _g_DirectSoundNotifyGUID;
-static GUID g_KSDATAFORMAT_SUBTYPE_PCM_GUID = _g_KSDATAFORMAT_SUBTYPE_PCM_GUID;
-static GUID g_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT_GUID = _g_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT_GUID;
-#else
-static GUID* g_DSListenerGUID = &_g_DSListenerGUID;
-static GUID* g_DirectSoundBuffer8GUID = &_g_DirectSoundBuffer8GUID;
-static GUID* g_DirectSound3DBuffer8GUID = &_g_DirectSound3DBuffer8GUID;
-static GUID* g_DirectSoundNotifyGUID = &_g_DirectSoundNotifyGUID;
-static GUID* g_KSDATAFORMAT_SUBTYPE_PCM_GUID = &_g_KSDATAFORMAT_SUBTYPE_PCM_GUID;
-static GUID* g_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT_GUID = &_g_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT_GUID;
-#endif
-
-
-typedef HRESULT (WINAPI * pDirectSoundCreate8Proc)(LPCGUID pcGuidDevice, LPDIRECTSOUND8 *ppDS8, LPUNKNOWN pUnkOuter);
-typedef HRESULT (WINAPI * pDirectSoundEnumerateAProc)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext);
-typedef HRESULT (WINAPI * pDirectSoundCaptureCreate8Proc)(LPCGUID pcGuidDevice, LPDIRECTSOUNDCAPTURE8 *ppDSC8, LPUNKNOWN pUnkOuter);
-typedef HRESULT (WINAPI * pDirectSoundCaptureEnumerateAProc)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext);
-
-typedef struct
-{
- /// A pointer to the GUID of the device. This will be set to all zeros for the default device.
- GUID guid;
-
- /// The description of the device.
- char description[256];
-
- /// The module name of the DirectSound driver corresponding to this device.
- char moduleName[256];
-
-} draudio_device_info_dsound;
-
-typedef struct
-{
- /// The base context data. This must always be the first item in the struct.
- draudio_context base;
-
- /// A handle to the dsound.dll file that was loaded by LoadLibrary().
- HMODULE hDSoundDLL;
-
- // DirectSound APIs.
- pDirectSoundCreate8Proc pDirectSoundCreate8;
- pDirectSoundEnumerateAProc pDirectSoundEnumerateA;
- pDirectSoundCaptureCreate8Proc pDirectSoundCaptureCreate8;
- pDirectSoundCaptureEnumerateAProc pDirectSoundCaptureEnumerateA;
-
-
- /// The number of output devices that were iterated when the context was created. This is static, so if the user was to unplug
- /// a device one would need to re-create the context.
- unsigned int outputDeviceCount;
-
- /// The buffer containing the list of enumerated output devices.
- draudio_device_info_dsound outputDeviceInfo[DRAUDIO_MAX_DEVICE_COUNT];
-
-
- /// The number of capture devices that were iterated when the context was created. This is static, so if the user was to unplug
- /// a device one would need to re-create the context.
- unsigned int inputDeviceCount;
-
- /// The buffer containing the list of enumerated input devices.
- draudio_device_info_dsound inputDeviceInfo[DRAUDIO_MAX_DEVICE_COUNT];
-
-
- /// The event manager.
- draudio_event_manager_dsound eventManager;
-
-
- /// The message queue.
- draudio_message_queue_dsound messageQueue;
-
-} draudio_context_dsound;
-
-typedef struct
-{
- /// The base device data. This must always be the first item in the struct.
- draudio_device base;
-
- /// A pointer to the DIRECTSOUND object that was created with DirectSoundCreate8().
- LPDIRECTSOUND8 pDS;
-
- /// A pointer to the DIRECTSOUNDBUFFER object for the primary buffer.
- LPDIRECTSOUNDBUFFER pDSPrimaryBuffer;
-
- /// A pointer to the DIRECTSOUND3DLISTENER8 object associated with the device.
- LPDIRECTSOUND3DLISTENER pDSListener;
-
-} draudio_device_dsound;
-
-typedef struct
-{
- /// The base buffer data. This must always be the first item in the struct.
- draudio_buffer base;
-
- /// A pointer to the DirectSound buffer object.
- LPDIRECTSOUNDBUFFER8 pDSBuffer;
-
- /// A pointer to the 3D DirectSound buffer object. This will be NULL if 3D positioning is disabled for the buffer.
- LPDIRECTSOUND3DBUFFER pDSBuffer3D;
-
- /// A pointer to the object for handling notification events.
- LPDIRECTSOUNDNOTIFY pDSNotify;
-
- /// The current playback state.
- draudio_playback_state playbackState;
-
-
- /// The number of marker events that have been registered. This will never be more than DRAUDIO_MAX_MARKER_COUNT.
- unsigned int markerEventCount;
-
- /// The marker events.
- draudio_event_dsound* pMarkerEvents[DRAUDIO_MAX_MARKER_COUNT];
-
- /// The event to trigger when the sound is stopped.
- draudio_event_dsound* pStopEvent;
-
- /// The event to trigger when the sound is paused.
- draudio_event_dsound* pPauseEvent;
-
- /// The event to trigger when the sound is played or resumed.
- draudio_event_dsound* pPlayEvent;
-
-
- /// The size in bytes of the buffer's extra data.
- unsigned int extraDataSize;
-
- /// The buffer's extra data.
- unsigned char pExtraData[1];
-
-} draudio_buffer_dsound;
-
-
-void draudio_activate_buffer_events_dsound(draudio_buffer* pBuffer)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- unsigned int dwPositionNotifies = 0;
- DSBPOSITIONNOTIFY n[DRAUDIO_MAX_MARKER_COUNT + 1]; // +1 because we use this array for the markers + stop event.
-
- // Stop
- if (pBufferDS->pStopEvent != NULL)
- {
- LPDSBPOSITIONNOTIFY pN = n + dwPositionNotifies;
- pN->dwOffset = DSBPN_OFFSETSTOP;
- pN->hEventNotify = pBufferDS->pStopEvent->hEvent;
-
- dwPositionNotifies += 1;
- }
-
- // Markers
- for (unsigned int iMarker = 0; iMarker < pBufferDS->markerEventCount; ++iMarker)
- {
- LPDSBPOSITIONNOTIFY pN = n + dwPositionNotifies;
- pN->dwOffset = pBufferDS->pMarkerEvents[iMarker]->markerOffset;
- pN->hEventNotify = pBufferDS->pMarkerEvents[iMarker]->hEvent;
-
- dwPositionNotifies += 1;
- }
-
-
- HRESULT hr = IDirectSoundNotify_SetNotificationPositions(pBufferDS->pDSNotify, dwPositionNotifies, n);
-#if 0
- if (FAILED(hr)) {
- printf("WARNING: FAILED TO CREATE DIRECTSOUND NOTIFIERS\n");
- }
-#else
- (void)hr;
-#endif
-}
-
-void draudio_deactivate_buffer_events_dsound(draudio_buffer* pBuffer)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
-
- HRESULT hr = IDirectSoundNotify_SetNotificationPositions(pBufferDS->pDSNotify, 0, NULL);
-#if 0
- if (FAILED(hr)) {
- printf("WARNING: FAILED TO CLEAR DIRECTSOUND NOTIFIERS\n");
- }
-#else
- (void)hr;
-#endif
-}
-
-
-void draudio_delete_context_dsound(draudio_context* pContext)
-{
- draudio_context_dsound* pContextDS = (draudio_context_dsound*)pContext;
- assert(pContextDS != NULL);
-
- draudio_uninit_event_manager_dsound(&pContextDS->eventManager);
-
- // The message queue needs to uninitialized after the DirectSound marker notification thread.
- draudio_uninit_message_queue_dsound(&pContextDS->messageQueue);
-
- FreeLibrary(pContextDS->hDSoundDLL);
- free(pContextDS);
-}
-
-
-unsigned int draudio_get_output_device_count_dsound(draudio_context* pContext)
-{
- draudio_context_dsound* pContextDS = (draudio_context_dsound*)pContext;
- assert(pContextDS != NULL);
-
- return pContextDS->outputDeviceCount;
-}
-
-bool draudio_get_output_device_info_dsound(draudio_context* pContext, unsigned int deviceIndex, draudio_device_info* pInfoOut)
-{
- draudio_context_dsound* pContextDS = (draudio_context_dsound*)pContext;
- assert(pContextDS != NULL);
- assert(pInfoOut != NULL);
-
- if (deviceIndex >= pContextDS->outputDeviceCount) {
- return false;
- }
-
-
- draudio_strcpy(pInfoOut->description, sizeof(pInfoOut->description), pContextDS->outputDeviceInfo[deviceIndex].description);
-
- return true;
-}
-
-
-draudio_device* draudio_create_output_device_dsound(draudio_context* pContext, unsigned int deviceIndex)
-{
- draudio_context_dsound* pContextDS = (draudio_context_dsound*)pContext;
- assert(pContextDS != NULL);
-
- if (deviceIndex >= pContextDS->outputDeviceCount) {
- return NULL;
- }
-
-
- LPDIRECTSOUND8 pDS;
-
- // Create the device.
- HRESULT hr;
- if (deviceIndex == 0) {
- hr = pContextDS->pDirectSoundCreate8(NULL, &pDS, NULL);
- } else {
- hr = pContextDS->pDirectSoundCreate8(&pContextDS->outputDeviceInfo[deviceIndex].guid, &pDS, NULL);
- }
-
- if (FAILED(hr)) {
- return NULL;
- }
-
-
- // Set the cooperative level. Must be done before anything else.
- hr = IDirectSound_SetCooperativeLevel(pDS, GetForegroundWindow(), DSSCL_EXCLUSIVE);
- if (FAILED(hr)) {
- IDirectSound_Release(pDS);
- return NULL;
- }
-
-
- // Primary buffer.
- DSBUFFERDESC descDSPrimary;
- memset(&descDSPrimary, 0, sizeof(DSBUFFERDESC));
- descDSPrimary.dwSize = sizeof(DSBUFFERDESC);
- descDSPrimary.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRL3D;
- descDSPrimary.guid3DAlgorithm = DRAUDIO_GUID_NULL;
-
- LPDIRECTSOUNDBUFFER pDSPrimaryBuffer;
- hr = IDirectSound_CreateSoundBuffer(pDS, &descDSPrimary, &pDSPrimaryBuffer, NULL);
- if (FAILED(hr)) {
- IDirectSound_Release(pDS);
- return NULL;
- }
-
-
- WAVEFORMATIEEEFLOATEX wf = {0};
- wf.Format.cbSize = sizeof(wf);
- wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- wf.Format.nChannels = 2;
- wf.Format.nSamplesPerSec = 48000;
- wf.Format.wBitsPerSample = 32;
- wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
- wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
- wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample;
- wf.dwChannelMask = 0;
- wf.SubFormat = _g_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT_GUID;
- hr = IDirectSoundBuffer_SetFormat(pDSPrimaryBuffer, (WAVEFORMATEX*)&wf);
- if (FAILED(hr)) {
- IDirectSoundBuffer_Release(pDSPrimaryBuffer);
- IDirectSound_Release(pDS);
- return NULL;
- }
-
-
- // Listener.
- LPDIRECTSOUND3DLISTENER pDSListener = NULL;
- hr = IDirectSound3DListener_QueryInterface(pDSPrimaryBuffer, g_DSListenerGUID, (LPVOID*)&pDSListener);
- if (FAILED(hr)) {
- IDirectSoundBuffer_Release(pDSPrimaryBuffer);
- IDirectSound_Release(pDS);
- return NULL;
- }
-
-
- draudio_device_dsound* pDeviceDS = (draudio_device_dsound*)malloc(sizeof(draudio_device_dsound));
- if (pDeviceDS != NULL)
- {
- pDeviceDS->base.pContext = pContext;
- pDeviceDS->pDS = pDS;
- pDeviceDS->pDSPrimaryBuffer = pDSPrimaryBuffer;
- pDeviceDS->pDSListener = pDSListener;
-
- return (draudio_device*)pDeviceDS;
- }
- else
- {
- IDirectSound3DListener_Release(pDSListener);
- IDirectSoundBuffer_Release(pDeviceDS->pDSPrimaryBuffer);
- IDirectSound_Release(pDS);
- return NULL;
- }
-}
-
-void draudio_delete_output_device_dsound(draudio_device* pDevice)
-{
- draudio_device_dsound* pDeviceDS = (draudio_device_dsound*)pDevice;
- assert(pDeviceDS != NULL);
-
- draudio_context_dsound* pContextDS = (draudio_context_dsound*)pDevice->pContext;
- assert(pContextDS != NULL);
-
- // The device id not deleted straight away. Instead we post a message to the message for delayed processing. The reason for this is that buffer
- // deletion is also delayed which means we want to ensure any delayed processing of buffers is handled before deleting the device.
- draudio_message_dsound msg;
- msg.id = DRAUDIO_MESSAGE_ID_DELETE_DEVICE;
- msg.pBuffer = NULL;
- msg.data.delete_device.pDSListener = pDeviceDS->pDSListener;
- msg.data.delete_device.pDSPrimaryBuffer = pDeviceDS->pDSPrimaryBuffer;
- msg.data.delete_device.pDS = pDeviceDS->pDS;
- msg.data.delete_device.pDevice = pDevice;
- draudio_post_message_dsound(&pContextDS->messageQueue, msg);
-
-#if 0
- IDirectSound3DListener_Release(pDeviceDS->pDSListener);
- IDirectSoundBuffer_Release(pDeviceDS->pDSPrimaryBuffer);
- IDirectSound_Release(pDeviceDS->pDS);
- free(pDeviceDS);
-#endif
-}
-
-
-draudio_buffer* draudio_create_buffer_dsound(draudio_device* pDevice, draudio_buffer_desc* pBufferDesc, size_t extraDataSize)
-{
- draudio_device_dsound* pDeviceDS = (draudio_device_dsound*)pDevice;
- assert(pDeviceDS != NULL);
- assert(pBufferDesc != NULL);
-
- // 3D is only valid for mono sounds.
- if (pBufferDesc->channels > 1 && (pBufferDesc->flags & DRAUDIO_ENABLE_3D) != 0) {
- return NULL;
- }
-
- WAVEFORMATIEEEFLOATEX wf = {0};
- wf.Format.cbSize = sizeof(wf);
- wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- wf.Format.nChannels = (WORD)pBufferDesc->channels;
- wf.Format.nSamplesPerSec = pBufferDesc->sampleRate;
- wf.Format.wBitsPerSample = (WORD)pBufferDesc->bitsPerSample;
- wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
- wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
- wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample;
- wf.dwChannelMask = 0;
-
- if (pBufferDesc->format == draudio_format_pcm) {
- wf.SubFormat = _g_KSDATAFORMAT_SUBTYPE_PCM_GUID;
- } else if (pBufferDesc->format == draudio_format_float) {
- wf.SubFormat = _g_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT_GUID;
- } else {
- return NULL;
- }
-
-
-
- // We want to try and create a 3D enabled buffer, however this will fail whenever the number of channels is > 1. In this case
- // we do not want to attempt to create a 3D enabled buffer because it will just fail anyway. Instead we'll just create a normal
- // buffer with panning enabled.
- DSBUFFERDESC descDS;
- memset(&descDS, 0, sizeof(DSBUFFERDESC));
- descDS.dwSize = sizeof(DSBUFFERDESC);
- descDS.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
- descDS.dwBufferBytes = (DWORD)pBufferDesc->sizeInBytes;
- descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
-
- LPDIRECTSOUNDBUFFER8 pDSBuffer = NULL;
- LPDIRECTSOUND3DBUFFER pDSBuffer3D = NULL;
- if ((pBufferDesc->flags & DRAUDIO_ENABLE_3D) == 0)
- {
- // 3D Disabled.
- descDS.dwFlags |= DSBCAPS_CTRLPAN;
-
- LPDIRECTSOUNDBUFFER pDSBufferTemp;
- HRESULT hr = IDirectSound_CreateSoundBuffer(pDeviceDS->pDS, &descDS, &pDSBufferTemp, NULL);
- if (FAILED(hr)) {
- return NULL;
- }
-
- hr = IDirectSoundBuffer_QueryInterface(pDSBufferTemp, g_DirectSoundBuffer8GUID, (void**)&pDSBuffer);
- if (FAILED(hr)) {
- IDirectSoundBuffer_Release(pDSBufferTemp);
- return NULL;
- }
- IDirectSoundBuffer_Release(pDSBufferTemp);
- }
- else
- {
- // 3D Enabled.
- descDS.dwFlags |= DSBCAPS_CTRL3D;
- descDS.guid3DAlgorithm = DS3DALG_DEFAULT;
-
- LPDIRECTSOUNDBUFFER pDSBufferTemp;
- HRESULT hr = IDirectSound_CreateSoundBuffer(pDeviceDS->pDS, &descDS, &pDSBufferTemp, NULL);
- if (FAILED(hr)) {
- return NULL;
- }
-
- hr = IDirectSoundBuffer_QueryInterface(pDSBufferTemp, g_DirectSoundBuffer8GUID, (void**)&pDSBuffer);
- if (FAILED(hr)) {
- IDirectSoundBuffer_Release(pDSBufferTemp);
- return NULL;
- }
- IDirectSoundBuffer_Release(pDSBufferTemp);
-
-
- hr = IDirectSoundBuffer_QueryInterface(pDSBuffer, g_DirectSound3DBuffer8GUID, (void**)&pDSBuffer3D);
- if (FAILED(hr)) {
- return NULL;
- }
-
- IDirectSound3DBuffer_SetPosition(pDSBuffer3D, 0, 0, 0, DS3D_IMMEDIATE);
-
- if ((pBufferDesc->flags & DRAUDIO_RELATIVE_3D) != 0) {
- IDirectSound3DBuffer_SetMode(pDSBuffer3D, DS3DMODE_HEADRELATIVE, DS3D_IMMEDIATE);
- }
- }
-
-
-
- // We need to create a notification object so we can notify the host application when the playback buffer hits a certain point.
- LPDIRECTSOUNDNOTIFY pDSNotify;
- HRESULT hr = IDirectSoundBuffer8_QueryInterface(pDSBuffer, g_DirectSoundNotifyGUID, (void**)&pDSNotify);
- if (FAILED(hr)) {
- IDirectSound3DBuffer_Release(pDSBuffer3D);
- IDirectSoundBuffer8_Release(pDSBuffer);
- return NULL;
- }
-
-
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)malloc(sizeof(draudio_buffer_dsound) - sizeof(pBufferDS->pExtraData) + extraDataSize);
- if (pBufferDS == NULL) {
- IDirectSound3DBuffer_Release(pDSBuffer3D);
- IDirectSoundBuffer8_Release(pDSBuffer);
- return NULL;
- }
-
- pBufferDS->base.pDevice = pDevice;
- pBufferDS->pDSBuffer = pDSBuffer;
- pBufferDS->pDSBuffer3D = pDSBuffer3D;
- pBufferDS->pDSNotify = pDSNotify;
- pBufferDS->playbackState = draudio_stopped;
-
- pBufferDS->markerEventCount = 0;
- memset(pBufferDS->pMarkerEvents, 0, sizeof(pBufferDS->pMarkerEvents));
- pBufferDS->pStopEvent = NULL;
- pBufferDS->pPauseEvent = NULL;
- pBufferDS->pPlayEvent = NULL;
-
-
-
- // Fill with initial data, if applicable.
- if (pBufferDesc->pData != NULL) {
- draudio_set_buffer_data((draudio_buffer*)pBufferDS, 0, pBufferDesc->pData, pBufferDesc->sizeInBytes);
- }
-
- return (draudio_buffer*)pBufferDS;
-}
-
-void draudio_delete_buffer_dsound(draudio_buffer* pBuffer)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
- assert(pBuffer->pDevice != NULL);
-
- draudio_context_dsound* pContextDS = (draudio_context_dsound*)pBuffer->pDevice->pContext;
- assert(pContextDS != NULL);
-
-
- // Deactivate the DirectSound notify events for sanity.
- draudio_deactivate_buffer_events_dsound(pBuffer);
-
-
- draudio_message_dsound msg;
- msg.id = DRAUDIO_MESSAGE_ID_DELETE_BUFFER;
- msg.pBuffer = pBuffer;
- msg.data.delete_buffer.pDSNotify = pBufferDS->pDSNotify;
- msg.data.delete_buffer.pDSBuffer3D = pBufferDS->pDSBuffer3D;
- msg.data.delete_buffer.pDSBuffer = pBufferDS->pDSBuffer;
- draudio_post_message_dsound(&pContextDS->messageQueue, msg);
-
-#if 0
- if (pBufferDS->pDSNotify != NULL) {
- IDirectSoundNotify_Release(pBufferDS->pDSNotify);
- }
-
- if (pBufferDS->pDSBuffer3D != NULL) {
- IDirectSound3DBuffer_Release(pBufferDS->pDSBuffer3D);
- }
-
- if (pBufferDS->pDSBuffer != NULL) {
- IDirectSoundBuffer8_Release(pBufferDS->pDSBuffer);
- }
-
- free(pBufferDS);
-#endif
-}
-
-
-unsigned int draudio_get_buffer_extra_data_size_dsound(draudio_buffer* pBuffer)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- return pBufferDS->extraDataSize;
-}
-
-void* draudio_get_buffer_extra_data_dsound(draudio_buffer* pBuffer)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- return pBufferDS->pExtraData;
-}
-
-
-void draudio_set_buffer_data_dsound(draudio_buffer* pBuffer, size_t offset, const void* pData, size_t dataSizeInBytes)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
- assert(pData != NULL);
-
- LPVOID lpvWrite;
- DWORD dwLength;
- HRESULT hr = IDirectSoundBuffer8_Lock(pBufferDS->pDSBuffer, (DWORD)offset, (DWORD)dataSizeInBytes, &lpvWrite, &dwLength, NULL, NULL, 0);
- if (FAILED(hr)) {
- return;
- }
-
- assert(dataSizeInBytes <= dwLength);
- memcpy(lpvWrite, pData, dataSizeInBytes);
-
- hr = IDirectSoundBuffer8_Unlock(pBufferDS->pDSBuffer, lpvWrite, dwLength, NULL, 0);
- if (FAILED(hr)) {
- return;
- }
-}
-
-
-void draudio_play_dsound(draudio_buffer* pBuffer, bool loop)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- bool postEvent = true;
- if (pBufferDS->playbackState == draudio_playing) {
- postEvent = false;
- }
-
-
- // Events need to be activated.
- if (pBufferDS->playbackState == draudio_stopped) {
- draudio_activate_buffer_events_dsound(pBuffer);
- }
-
-
- DWORD dwFlags = 0;
- if (loop) {
- dwFlags |= DSBPLAY_LOOPING;
- }
-
- pBufferDS->playbackState = draudio_playing;
- IDirectSoundBuffer8_Play(pBufferDS->pDSBuffer, 0, 0, dwFlags);
-
- // If we have a play event we need to signal the event which will cause the worker thread to call the callback function.
- if (pBufferDS->pPlayEvent != NULL && postEvent) {
- SetEvent(pBufferDS->pPlayEvent->hEvent);
- }
-}
-
-void draudio_pause_dsound(draudio_buffer* pBuffer)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- if (pBufferDS->playbackState == draudio_playing)
- {
- pBufferDS->playbackState = draudio_paused;
- IDirectSoundBuffer8_Stop(pBufferDS->pDSBuffer);
-
- // If we have a pause event we need to signal the event which will cause the worker thread to call the callback function.
- if (pBufferDS->pPlayEvent != NULL) {
- SetEvent(pBufferDS->pPauseEvent->hEvent);
- }
- }
-}
-
-void draudio_stop_dsound(draudio_buffer* pBuffer)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- if (pBufferDS->playbackState == draudio_playing)
- {
- pBufferDS->playbackState = draudio_stopped;
- IDirectSoundBuffer8_Stop(pBufferDS->pDSBuffer);
- IDirectSoundBuffer8_SetCurrentPosition(pBufferDS->pDSBuffer, 0);
- }
- else if (pBufferDS->playbackState == draudio_paused)
- {
- pBufferDS->playbackState = draudio_stopped;
- IDirectSoundBuffer8_SetCurrentPosition(pBufferDS->pDSBuffer, 0);
-
- if (pBufferDS->pStopEvent != NULL) {
- SetEvent(pBufferDS->pStopEvent->hEvent);
- }
- }
-}
-
-draudio_playback_state draudio_get_playback_state_dsound(draudio_buffer* pBuffer)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- return pBufferDS->playbackState;
-}
-
-
-void draudio_set_playback_position_dsound(draudio_buffer* pBuffer, unsigned int position)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- IDirectSoundBuffer8_SetCurrentPosition(pBufferDS->pDSBuffer, position);
-}
-
-unsigned int draudio_get_playback_position_dsound(draudio_buffer* pBuffer)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- DWORD position;
- HRESULT hr = IDirectSoundBuffer8_GetCurrentPosition(pBufferDS->pDSBuffer, &position, NULL);
- if (FAILED(hr)) {
- return 0;
- }
-
- return position;
-}
-
-
-void draudio_set_pan_dsound(draudio_buffer* pBuffer, float pan)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- LONG panDB;
- if (pan == 0) {
- panDB = DSBPAN_CENTER;
- } else {
- if (pan > 1) {
- panDB = DSBPAN_RIGHT;
- } else if (pan < -1) {
- panDB = DSBPAN_LEFT;
- } else {
- if (pan < 0) {
- panDB = (LONG)((20*log10f(1 + pan)) * 100);
- } else {
- panDB = -(LONG)((20*log10f(1 - pan)) * 100);
- }
- }
- }
-
- IDirectSoundBuffer_SetPan(pBufferDS->pDSBuffer, panDB);
-}
-
-float draudio_get_pan_dsound(draudio_buffer* pBuffer)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- LONG panDB;
- HRESULT hr = IDirectSoundBuffer_GetPan(pBufferDS->pDSBuffer, &panDB);
- if (FAILED(hr)) {
- return 0;
- }
-
-
- if (panDB < 0) {
- return -(1 - (float)(1.0f / powf(10.0f, -panDB / (20.0f*100.0f))));
- }
-
- if (panDB > 0) {
- return (1 - (float)(1.0f / powf(10.0f, panDB / (20.0f*100.0f))));
- }
-
- return 0;
-}
-
-
-void draudio_set_volume_dsound(draudio_buffer* pBuffer, float volume)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- LONG volumeDB;
- if (volume > 0) {
- if (volume < 1) {
- volumeDB = (LONG)((20*log10f(volume)) * 100);
- } else {
- volumeDB = DSBVOLUME_MAX;
- }
- } else {
- volumeDB = DSBVOLUME_MIN;
- }
-
- IDirectSoundBuffer_SetVolume(pBufferDS->pDSBuffer, volumeDB);
-}
-
-float draudio_get_volume_dsound(draudio_buffer* pBuffer)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- LONG volumeDB;
- HRESULT hr = IDirectSoundBuffer_GetVolume(pBufferDS->pDSBuffer, &volumeDB);
- if (FAILED(hr)) {
- return 1;
- }
-
- return (float)(1.0f / powf(10.0f, -volumeDB / (20.0f*100.0f)));
-}
-
-
-void draudio_remove_markers_dsound(draudio_buffer* pBuffer)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- for (unsigned int iMarker = 0; iMarker < pBufferDS->markerEventCount; ++iMarker)
- {
- if (pBufferDS->pMarkerEvents[iMarker] != NULL) {
- draudio_delete_event_dsound(pBufferDS->pMarkerEvents[iMarker]);
- pBufferDS->pMarkerEvents[iMarker] = NULL;
- }
- }
-
- pBufferDS->markerEventCount = 0;
-}
-
-bool draudio_register_marker_callback_dsound(draudio_buffer* pBuffer, size_t offsetInBytes, draudio_event_callback_proc callback, unsigned int eventID, void* pUserData)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
- assert(pBufferDS->markerEventCount <= DRAUDIO_MAX_MARKER_COUNT);
-
- if (pBufferDS->markerEventCount == DRAUDIO_MAX_MARKER_COUNT) {
- // Too many markers.
- return false;
- }
-
- draudio_context_dsound* pContextDS = (draudio_context_dsound*)pBuffer->pDevice->pContext;
- assert(pContextDS != NULL);
-
- draudio_event_dsound* pEvent = draudio_create_event_dsound(&pContextDS->eventManager, callback, pBuffer, eventID, pUserData);
- if (pEvent == NULL) {
- return false;
- }
-
- // draudio_create_event_dsound() will initialize the marker offset to 0, so we'll need to set it manually here.
- pEvent->markerOffset = (DWORD)offsetInBytes;
-
- pBufferDS->pMarkerEvents[pBufferDS->markerEventCount] = pEvent;
- pBufferDS->markerEventCount += 1;
-
- return true;
-}
-
-bool draudio_register_stop_callback_dsound(draudio_buffer* pBuffer, draudio_event_callback_proc callback, void* pUserData)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- if (callback == NULL)
- {
- if (pBufferDS->pStopEvent != NULL) {
- draudio_delete_event_dsound(pBufferDS->pStopEvent);
- pBufferDS->pStopEvent = NULL;
- }
-
- return true;
- }
- else
- {
- draudio_context_dsound* pContextDS = (draudio_context_dsound*)pBuffer->pDevice->pContext;
-
- // If we already have a stop event, just replace the existing one.
- if (pBufferDS->pStopEvent != NULL) {
- draudio_update_event_dsound(pBufferDS->pStopEvent, callback, pUserData);
- } else {
- pBufferDS->pStopEvent = draudio_create_event_dsound(&pContextDS->eventManager, callback, pBuffer, DRAUDIO_EVENT_ID_STOP, pUserData);
- }
-
- return pBufferDS->pStopEvent != NULL;
- }
-}
-
-bool draudio_register_pause_callback_dsound(draudio_buffer* pBuffer, draudio_event_callback_proc callback, void* pUserData)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- if (callback == NULL)
- {
- if (pBufferDS->pPauseEvent != NULL) {
- draudio_delete_event_dsound(pBufferDS->pPauseEvent);
- pBufferDS->pPauseEvent = NULL;
- }
-
- return true;
- }
- else
- {
- draudio_context_dsound* pContextDS = (draudio_context_dsound*)pBuffer->pDevice->pContext;
-
- // If we already have a stop event, just replace the existing one.
- if (pBufferDS->pPauseEvent != NULL) {
- draudio_update_event_dsound(pBufferDS->pPauseEvent, callback, pUserData);
- } else {
- pBufferDS->pPauseEvent = draudio_create_event_dsound(&pContextDS->eventManager, callback, pBuffer, DRAUDIO_EVENT_ID_PAUSE, pUserData);
- }
-
- return pBufferDS->pPauseEvent != NULL;
- }
-}
-
-bool draudio_register_play_callback_dsound(draudio_buffer* pBuffer, draudio_event_callback_proc callback, void* pUserData)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- if (callback == NULL)
- {
- if (pBufferDS->pPlayEvent != NULL) {
- draudio_delete_event_dsound(pBufferDS->pPlayEvent);
- pBufferDS->pPlayEvent = NULL;
- }
-
- return true;
- }
- else
- {
- draudio_context_dsound* pContextDS = (draudio_context_dsound*)pBuffer->pDevice->pContext;
-
- // If we already have a stop event, just replace the existing one.
- if (pBufferDS->pPlayEvent != NULL) {
- draudio_update_event_dsound(pBufferDS->pPlayEvent, callback, pUserData);
- } else {
- pBufferDS->pPlayEvent = draudio_create_event_dsound(&pContextDS->eventManager, callback, pBuffer, DRAUDIO_EVENT_ID_PLAY, pUserData);
- }
-
- return pBufferDS->pPlayEvent != NULL;
- }
-}
-
-
-
-void draudio_set_position_dsound(draudio_buffer* pBuffer, float x, float y, float z)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- if (pBufferDS->pDSBuffer3D != NULL) {
- IDirectSound3DBuffer_SetPosition(pBufferDS->pDSBuffer3D, x, y, z, DS3D_IMMEDIATE);
- }
-}
-
-void draudio_get_position_dsound(draudio_buffer* pBuffer, float* pPosOut)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
- assert(pPosOut != NULL);
-
- if (pBufferDS->pDSBuffer3D != NULL)
- {
- D3DVECTOR pos;
- IDirectSound3DBuffer_GetPosition(pBufferDS->pDSBuffer3D, &pos);
-
- pPosOut[0] = pos.x;
- pPosOut[1] = pos.y;
- pPosOut[2] = pos.z;
- }
- else
- {
- pPosOut[0] = 0;
- pPosOut[1] = 1;
- pPosOut[2] = 2;
- }
-}
-
-
-void draudio_set_listener_position_dsound(draudio_device* pDevice, float x, float y, float z)
-{
- draudio_device_dsound* pDeviceDS = (draudio_device_dsound*)pDevice;
- assert(pDeviceDS != NULL);
-
- IDirectSound3DListener_SetPosition(pDeviceDS->pDSListener, x, y, z, DS3D_IMMEDIATE);
-}
-
-void draudio_get_listener_position_dsound(draudio_device* pDevice, float* pPosOut)
-{
- draudio_device_dsound* pDeviceDS = (draudio_device_dsound*)pDevice;
- assert(pDeviceDS != NULL);
- assert(pPosOut != NULL);
-
- D3DVECTOR pos;
- IDirectSound3DListener_GetPosition(pDeviceDS->pDSListener, &pos);
-
- pPosOut[0] = pos.x;
- pPosOut[1] = pos.y;
- pPosOut[2] = pos.z;
-}
-
-
-void draudio_set_listener_orientation_dsound(draudio_device* pDevice, float forwardX, float forwardY, float forwardZ, float upX, float upY, float upZ)
-{
- draudio_device_dsound* pDeviceDS = (draudio_device_dsound*)pDevice;
- assert(pDeviceDS != NULL);
-
- IDirectSound3DListener_SetOrientation(pDeviceDS->pDSListener, forwardX, forwardY, forwardZ, upX, upY, upZ, DS3D_IMMEDIATE);
-}
-
-void draudio_get_listener_orientation_dsound(draudio_device* pDevice, float* pForwardOut, float* pUpOut)
-{
- draudio_device_dsound* pDeviceDS = (draudio_device_dsound*)pDevice;
- assert(pDeviceDS != NULL);
- assert(pForwardOut != NULL);
- assert(pUpOut != NULL);
-
- D3DVECTOR forward;
- D3DVECTOR up;
- IDirectSound3DListener_GetOrientation(pDeviceDS->pDSListener, &forward, &up);
-
- pForwardOut[0] = forward.x;
- pForwardOut[1] = forward.y;
- pForwardOut[2] = forward.z;
-
- pUpOut[0] = up.x;
- pUpOut[1] = up.y;
- pUpOut[2] = up.z;
-}
-
-void draudio_set_3d_mode_dsound(draudio_buffer* pBuffer, draudio_3d_mode mode)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- if (pBufferDS->pDSBuffer3D == NULL) {
- return;
- }
-
-
- DWORD dwMode = DS3DMODE_NORMAL;
- if (mode == draudio_3d_mode_relative) {
- dwMode = DS3DMODE_HEADRELATIVE;
- } else if (mode == draudio_3d_mode_disabled) {
- dwMode = DS3DMODE_DISABLE;
- }
-
- IDirectSound3DBuffer_SetMode(pBufferDS->pDSBuffer3D, dwMode, DS3D_IMMEDIATE);
-}
-
-draudio_3d_mode draudio_get_3d_mode_dsound(draudio_buffer* pBuffer)
-{
- draudio_buffer_dsound* pBufferDS = (draudio_buffer_dsound*)pBuffer;
- assert(pBufferDS != NULL);
-
- if (pBufferDS->pDSBuffer3D == NULL) {
- return draudio_3d_mode_disabled;
- }
-
-
- DWORD dwMode;
- if (FAILED(IDirectSound3DBuffer_GetMode(pBufferDS->pDSBuffer3D, &dwMode))) {
- return draudio_3d_mode_disabled;
- }
-
-
- if (dwMode == DS3DMODE_NORMAL) {
- return draudio_3d_mode_absolute;
- }
-
- if (dwMode == DS3DMODE_HEADRELATIVE) {
- return draudio_3d_mode_relative;
- }
-
- return draudio_3d_mode_disabled;
-}
-
-
-static BOOL CALLBACK DSEnumCallback_OutputDevices(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
-{
- // From MSDN:
- //
- // The first device enumerated is always called the Primary Sound Driver, and the lpGUID parameter of the callback is
- // NULL. This device represents the preferred output device set by the user in Control Panel.
-
- draudio_context_dsound* pContextDS = (draudio_context_dsound*)lpContext;
- assert(pContextDS != NULL);
-
- if (pContextDS->outputDeviceCount < DRAUDIO_MAX_DEVICE_COUNT)
- {
- if (lpGuid != NULL) {
- memcpy(&pContextDS->outputDeviceInfo[pContextDS->outputDeviceCount].guid, lpGuid, sizeof(GUID));
- } else {
- memset(&pContextDS->outputDeviceInfo[pContextDS->outputDeviceCount].guid, 0, sizeof(GUID));
- }
-
- draudio_strcpy(pContextDS->outputDeviceInfo[pContextDS->outputDeviceCount].description, 256, lpcstrDescription);
- draudio_strcpy(pContextDS->outputDeviceInfo[pContextDS->outputDeviceCount].moduleName, 256, lpcstrModule);
-
- pContextDS->outputDeviceCount += 1;
- return TRUE;
- }
- else
- {
- // Ran out of device slots.
- return FALSE;
- }
-}
-
-static BOOL CALLBACK DSEnumCallback_InputDevices(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
-{
- // From MSDN:
- //
- // The first device enumerated is always called the Primary Sound Driver, and the lpGUID parameter of the callback is
- // NULL. This device represents the preferred output device set by the user in Control Panel.
-
- draudio_context_dsound* pContextDS = (draudio_context_dsound*)lpContext;
- assert(pContextDS != NULL);
-
- if (pContextDS->inputDeviceCount < DRAUDIO_MAX_DEVICE_COUNT)
- {
- if (lpGuid != NULL) {
- memcpy(&pContextDS->inputDeviceInfo[pContextDS->inputDeviceCount].guid, lpGuid, sizeof(GUID));
- } else {
- memset(&pContextDS->inputDeviceInfo[pContextDS->inputDeviceCount].guid, 0, sizeof(GUID));
- }
-
- draudio_strcpy(pContextDS->inputDeviceInfo[pContextDS->inputDeviceCount].description, 256, lpcstrDescription);
- draudio_strcpy(pContextDS->inputDeviceInfo[pContextDS->inputDeviceCount].moduleName, 256, lpcstrModule);
-
- pContextDS->inputDeviceCount += 1;
- return TRUE;
- }
- else
- {
- // Ran out of device slots.
- return FALSE;
- }
-}
-
-draudio_context* draudio_create_context_dsound()
-{
- // Load the DLL.
- HMODULE hDSoundDLL = LoadLibraryW(L"dsound.dll");
- if (hDSoundDLL == NULL) {
- return NULL;
- }
-
-
- // Retrieve the APIs.
- pDirectSoundCreate8Proc pDirectSoundCreate8 = (pDirectSoundCreate8Proc)GetProcAddress(hDSoundDLL, "DirectSoundCreate8");
- if (pDirectSoundCreate8 == NULL){
- FreeLibrary(hDSoundDLL);
- return NULL;
- }
-
- pDirectSoundEnumerateAProc pDirectSoundEnumerateA = (pDirectSoundEnumerateAProc)GetProcAddress(hDSoundDLL, "DirectSoundEnumerateA");
- if (pDirectSoundEnumerateA == NULL){
- FreeLibrary(hDSoundDLL);
- return NULL;
- }
-
- pDirectSoundCaptureCreate8Proc pDirectSoundCaptureCreate8 = (pDirectSoundCaptureCreate8Proc)GetProcAddress(hDSoundDLL, "DirectSoundCaptureCreate8");
- if (pDirectSoundCaptureCreate8 == NULL) {
- FreeLibrary(hDSoundDLL);
- return NULL;
- }
-
- pDirectSoundCaptureEnumerateAProc pDirectSoundCaptureEnumerateA = (pDirectSoundCaptureEnumerateAProc)GetProcAddress(hDSoundDLL, "DirectSoundCaptureEnumerateA");
- if (pDirectSoundCaptureEnumerateA == NULL ){
- FreeLibrary(hDSoundDLL);
- return NULL;
- }
-
-
-
- // At this point we can almost certainly assume DirectSound is usable so we'll now go ahead and create the context.
- draudio_context_dsound* pContext = (draudio_context_dsound*)malloc(sizeof(draudio_context_dsound));
- if (pContext != NULL)
- {
- pContext->base.delete_context = draudio_delete_context_dsound;
- pContext->base.create_output_device = draudio_create_output_device_dsound;
- pContext->base.delete_output_device = draudio_delete_output_device_dsound;
- pContext->base.get_output_device_count = draudio_get_output_device_count_dsound;
- pContext->base.get_output_device_info = draudio_get_output_device_info_dsound;
- pContext->base.create_buffer = draudio_create_buffer_dsound;
- pContext->base.delete_buffer = draudio_delete_buffer_dsound;
- pContext->base.get_buffer_extra_data_size = draudio_get_buffer_extra_data_size_dsound;
- pContext->base.get_buffer_extra_data = draudio_get_buffer_extra_data_dsound;
- pContext->base.set_buffer_data = draudio_set_buffer_data_dsound;
- pContext->base.play = draudio_play_dsound;
- pContext->base.pause = draudio_pause_dsound;
- pContext->base.stop = draudio_stop_dsound;
- pContext->base.get_playback_state = draudio_get_playback_state_dsound;
- pContext->base.set_playback_position = draudio_set_playback_position_dsound;
- pContext->base.get_playback_position = draudio_get_playback_position_dsound;
- pContext->base.set_pan = draudio_set_pan_dsound;
- pContext->base.get_pan = draudio_get_pan_dsound;
- pContext->base.set_volume = draudio_set_volume_dsound;
- pContext->base.get_volume = draudio_get_volume_dsound;
- pContext->base.remove_markers = draudio_remove_markers_dsound;
- pContext->base.register_marker_callback = draudio_register_marker_callback_dsound;
- pContext->base.register_stop_callback = draudio_register_stop_callback_dsound;
- pContext->base.register_pause_callback = draudio_register_pause_callback_dsound;
- pContext->base.register_play_callback = draudio_register_play_callback_dsound;
- pContext->base.set_position = draudio_set_position_dsound;
- pContext->base.get_position = draudio_get_position_dsound;
- pContext->base.set_listener_position = draudio_set_listener_position_dsound;
- pContext->base.get_listener_position = draudio_get_listener_position_dsound;
- pContext->base.set_listener_orientation = draudio_set_listener_orientation_dsound;
- pContext->base.get_listener_orientation = draudio_get_listener_orientation_dsound;
- pContext->base.set_3d_mode = draudio_set_3d_mode_dsound;
- pContext->base.get_3d_mode = draudio_get_3d_mode_dsound;
-
- pContext->hDSoundDLL = hDSoundDLL;
- pContext->pDirectSoundCreate8 = pDirectSoundCreate8;
- pContext->pDirectSoundEnumerateA = pDirectSoundEnumerateA;
- pContext->pDirectSoundCaptureCreate8 = pDirectSoundCaptureCreate8;
- pContext->pDirectSoundCaptureEnumerateA = pDirectSoundCaptureEnumerateA;
-
- // Enumerate output devices.
- pContext->outputDeviceCount = 0;
- pContext->pDirectSoundEnumerateA(DSEnumCallback_OutputDevices, pContext);
-
- // Enumerate input devices.
- pContext->inputDeviceCount = 0;
- pContext->pDirectSoundCaptureEnumerateA(DSEnumCallback_InputDevices, pContext);
-
- // The message queue and marker notification thread.
- if (!draudio_init_message_queue_dsound(&pContext->messageQueue) || !draudio_init_event_manager_dsound(&pContext->eventManager, &pContext->messageQueue))
- {
- // Failed to initialize the event manager.
- FreeLibrary(hDSoundDLL);
- free(pContext);
-
- return NULL;
- }
- }
-
- return (draudio_context*)pContext;
-}
-#endif // !DRAUDIO_BUILD_DSOUND
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// XAudio2
-//
-///////////////////////////////////////////////////////////////////////////////
-
-#if 0
-#define uuid(x)
-#define DX_BUILD
-#define INITGUID 1
-#include
-#endif
-#endif
-
-/*
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-For more information, please refer to
-*/
diff --git a/source/engine/thirdparty/dr_libs/old/dr_fs.h b/source/engine/thirdparty/dr_libs/old/dr_fs.h
deleted file mode 100644
index 6589c14..0000000
--- a/source/engine/thirdparty/dr_libs/old/dr_fs.h
+++ /dev/null
@@ -1,8051 +0,0 @@
-// Public Domain. See "unlicense" statement at the end of this file.
-//
-// Includes code from miniz.c which can be found here: https://github.com/richgel999/miniz
-
-// NOTE: dr_fs is very early in development and should be considered unstable. Expect many APIs to change.
-
-// ABOUT
-//
-// dr_fs is a simple library which abstracts file IO to allow one to open files from both the native file
-// system and archive/package files such as Zip files using a common API.
-//
-// This file includes code from miniz.c which has been stripped down to include only what's needed to support
-// Zip files at basic level. Every public API has been namespaced with "drfs_" to avoid naming conflicts.
-//
-// Some noteworthy features:
-// - Supports verbose absolute paths to avoid ambiguity. For example you can specify a path
-// such as "my/package.zip/file.txt"
-// - Supports shortened, transparent paths by automatically scanning for supported archives. The
-// path "my/package.zip/file.txt" can be shortened to "my/file.txt", for example. This does not
-// work for absolute paths, however. See notes below.
-// - Fully recursive. A path such as "pack1.zip/pack2.zip/file.txt" should work just fine.
-// - Easily supports custom package formats without the need to modify the original source code.
-// Look at drfs_register_archive_backend() and the implementation of Zip archives for an
-// example.
-// - No dependencies except for the C standard library.
-//
-// Limitations:
-// - When a file contained within a Zip file is opened, the entire uncompressed data is loaded
-// onto the heap. Keep this in mind when working with large files.
-// - Zip, PAK and Wavefront MTL archives are read-only at the moment.
-// - dr_fs is not fully thread-safe. See notes below.
-// - Asynchronous IO is not supported.
-//
-//
-//
-// USAGE
-//
-// This is a single-file library. To use it, do something like the following in one .c file.
-// #define DR_FS_IMPLEMENTATION
-// #include "dr_fs.h"
-//
-// You can then #include dr_fs.h in other parts of the program as you would with any other header file.
-//
-// Example:
-// // Create a context.
-// drfs_context* pVFS = drfs_create_context();
-// if (pVFS == NULL) {
-// // There was an error creating the context.
-// }
-//
-// // Add your base directories for loading from relative paths. If you do not specify at
-// // least one base directory you will need to load from absolute paths.
-// drfs_add_base_directory(pVFS, "C:/Users/Admin");
-// drfs_add_base_directory(pVFS, "C:/My/Folder");
-//
-// ...
-//
-// // Open a file. A relative path was specified which means it will first check it against
-// // "C:/Users/Admin". If it can't be found it will then check against "C:/My/Folder".
-// drfs_file* pFile;
-// drfs_result result = drfs_open(pVFS, "my/file.txt", DRFS_READ, &pFile);
-// if (result != drfs_success) {
-// // There was an error loading the file. It probably doesn't exist.
-// }
-//
-// result = drfs_read(pFile, buffer, bufferSize, NULL);
-// if (result != drfs_success) {
-// // There was an error reading the file.
-// }
-//
-// drfs_close(pFile);
-//
-// ...
-//
-// // Shutdown.
-// drfs_delete_context(pVFS);
-//
-//
-//
-// OPTIONS
-//
-// To use these options, just place the appropriate #define's in the .c file before including this file.
-//
-// #define DR_FS_NO_ZIP
-// Disable built-in support for Zip files.
-//
-// #define DR_FS_NO_PAK
-// Disable support for Quake 2 PAK files.
-//
-// #define DR_FS_NO_MTL
-// Disable support for Wavefront MTL files.
-//
-//
-//
-// THREAD SAFETY
-//
-// dr_fs is not fully thread safe. Known unsafe functionality includes:
-// - Opening a file while adding or removing base directories and backends
-// - Closing a file while doing anything on that file object
-// - drfs_open() will malloc() the drfs_file object, and drfs_close() will free() it with no garbage collection
-// nor reference counting.
-//
-// The issues mentioned above should not be an issue for the vast majority of cases. Base directories and backends
-// will typically be registered once during initialization when the context is created, and it's unlikely an
-// application will want to close a file while simultaneously trying to use it on another thread without doing it's
-// own synchronization anyway.
-//
-// Thread-safety has not been completely ignored either. It is possible to read, write and seek on multiple threads.
-// In this case it is a simple matter of first-in first-served. Also, APIs are in place to allow an application to
-// do it's own synchronization. An application can use drfs_lock() and drfs_unlock() to lock and unlock a file using
-// simple mutal exclusion. Inside the lock/unlock pair the application can then use the "_nolock" variation of the
-// relevant APIs:
-// - drfs_read_nolock()
-// - drfs_write_nolock()
-// - drfs_seek_nolock()
-// - drfs_tell_nolock()
-// - drfs_size_nolock()
-//
-// Opening two files that share the same archive should work fine across multiple threads. For example, if you have
-// an archive called MyArchive.zip and then open two files within that archive, you can do work with each of those
-// files independently on separate threads. This functionality depends on the implementation of the relevant backend,
-// however.
-//
-// When implementing a backend, it is important to keep synchronization in mind when reading data from the host
-// archive file. To help with this, use the drfs_lock() and drfs_unlock() combined with the "_nolock" variations
-// of the APIs listed above.
-//
-//
-//
-// QUICK NOTES
-//
-// - The library works by using the notion of an "archive" to create an abstraction around the file system.
-// - Conceptually, and archive is just a grouping of files and folders. An archive can be a directory on the native
-// file system or an actual archive file such as a .zip file.
-// - When iterating over files and folder, the order is undefined. Do not assume alphabetical.
-// - When a path includes the name of the package file, such as "my/package.zip/file.txt" (note how the .zip file is
-// included in the path), it is referred to as a verbose path.
-// - When specifying an absolute path, it is assumed to be verbose. When specifying a relative path, it does not need
-// to be verbose, in which case the library will try to search for it. A path such as "my/package.zip/file.txt" is
-// equivalent to "my/file.txt".
-// - Archive backends are selected based on their extension.
-// - Archive backends cannot currently share the same extension. For example, many package file formats use the .pak
-// extension, however only one backend can use that .pak extension.
-// - For safety, if you want to overwrite a file you must explicitly call drfs_open() with the DRFS_TRUNCATE flag.
-// - Platforms other than Windows do not use buffering. That is, they use read() and write() instead of fread() and
-// fwrite().
-// - On Linux platforms, if you are having issues with opening files larger than 2GB, make sure this file is the first
-// file included in the .c file. This ensures the _LARGEFILE64_SOURCE macro is defined before any other header file
-// as required for the use of 64-bit variants of the POSIX APIs.
-// - Base paths must be absolute and verbose.
-//
-//
-//
-// TODO:
-//
-// - Test result code consistency.
-// - Document performance issues.
-// - Consider making it so persistent constant strings (such as base paths) use dynamically allocated strings rather
-// than fixed sized arrays of DRFS_MAX_PATH.
-// - Replace the miniz reading functionality with a custom one:
-// - There is a sort-of-bug where miniz does not correctly enumerate directories in a zip file that was created with
-// the "Send to -> Compressed (zipped) folder" functionality in Windows Explorer. This is more of a thing with
-// Windows Explorer more than anything, but it'd be nice if it would Just Work.
-// - miniz does not support streamed reading yet. Instead, one must decompress the entire file onto the heap which
-// is a bit untidy and doesn't work well with very large files.
-// - ZIP64 is not supported.
-
-#ifndef dr_fs_h
-#define dr_fs_h
-
-// These need to be defined before including any headers, but we don't want to expose it to the public header.
-#if defined(DR_FS_IMPLEMENTATION) && !defined(_WIN32)
-#ifndef _LARGEFILE64_SOURCE
-#define _LARGEFILE64_SOURCE
-#endif
-#ifndef _FILE_OFFSET_BITS
-#define _FILE_OFFSET_BITS 64
-#endif
-#endif
-
-#include
-
-#ifndef DR_SIZED_TYPES_DEFINED
-#define DR_SIZED_TYPES_DEFINED
-#if defined(_MSC_VER) && _MSC_VER < 1600
-typedef signed char dr_int8;
-typedef unsigned char dr_uint8;
-typedef signed short dr_int16;
-typedef unsigned short dr_uint16;
-typedef signed int dr_int32;
-typedef unsigned int dr_uint32;
-typedef signed __int64 dr_int64;
-typedef unsigned __int64 dr_uint64;
-#else
-#include
-typedef int8_t dr_int8;
-typedef uint8_t dr_uint8;
-typedef int16_t dr_int16;
-typedef uint16_t dr_uint16;
-typedef int32_t dr_int32;
-typedef uint32_t dr_uint32;
-typedef int64_t dr_int64;
-typedef uint64_t dr_uint64;
-#endif
-typedef dr_uint8 dr_bool8;
-typedef dr_uint32 dr_bool32;
-#define DR_TRUE 1
-#define DR_FALSE 0
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-// The maximum length of a path in bytes, including the null terminator. If a path exceeds this amount, it will be set to an empty
-// string. When this is changed the source file will need to be recompiled. Most of the time leaving this at 256 is fine, but it's
-// not a problem to increase the size if you are encountering issues. Note that increasing this value will increase memory usage
-// on both the heap and the stack.
-#ifndef DRFS_MAX_PATH
-//#define DRFS_MAX_PATH 256
-#define DRFS_MAX_PATH 1024
-//#define DRFS_MAX_PATH 4096
-#endif
-
-#define DRFS_READ (1 << 0)
-#define DRFS_WRITE (1 << 1)
-#define DRFS_EXISTING (1 << 2)
-#define DRFS_TRUNCATE (1 << 3)
-#define DRFS_CREATE_DIRS (1 << 4) // Creates the directory structure if required.
-
-#define DRFS_FILE_ATTRIBUTE_DIRECTORY 0x00000001
-#define DRFS_FILE_ATTRIBUTE_READONLY 0x00000002
-
-// Result codes.
-typedef enum
-{
- drfs_success = 0,
- drfs_unknown_error = -1,
- drfs_invalid_args = -2, // Bad input arguments like a file path string is equal to NULL or whatnot.
- drfs_does_not_exist = -3,
- drfs_already_exists = -4,
- drfs_permission_denied = -5,
- drfs_too_many_open_files = -6,
- drfs_no_backend = -7,
- drfs_out_of_memory = -8,
- drfs_not_in_write_directory = -9, // A write operation is required, but the given path is not within the write directory or it's sub-directories.
- drfs_path_too_long = -10,
- drfs_no_space = -11,
- drfs_not_directory = -12,
- drfs_too_large = -13,
- drfs_at_end_of_file = -14,
- drfs_invalid_archive = -15,
- drfs_negative_seek = -16
-} drfs_result;
-
-// The allowable seeking origins.
-typedef enum
-{
- drfs_origin_current,
- drfs_origin_start,
- drfs_origin_end
-}drfs_seek_origin;
-
-
-typedef void* drfs_handle;
-
-typedef struct drfs_context drfs_context;
-typedef struct drfs_archive drfs_archive;
-typedef struct drfs_file drfs_file;
-typedef struct drfs_file_info drfs_file_info;
-typedef struct drfs_iterator drfs_iterator;
-
-typedef dr_bool32 (* drfs_is_valid_extension_proc)(const char* extension);
-typedef drfs_result (* drfs_open_archive_proc) (drfs_file* pArchiveFile, unsigned int accessMode, drfs_handle* pHandleOut);
-typedef void (* drfs_close_archive_proc) (drfs_handle archive);
-typedef drfs_result (* drfs_get_file_info_proc) (drfs_handle archive, const char* relativePath, drfs_file_info* fi);
-typedef drfs_handle (* drfs_begin_iteration_proc) (drfs_handle archive, const char* relativePath);
-typedef void (* drfs_end_iteration_proc) (drfs_handle archive, drfs_handle iterator);
-typedef dr_bool32 (* drfs_next_iteration_proc) (drfs_handle archive, drfs_handle iterator, drfs_file_info* fi);
-typedef drfs_result (* drfs_delete_file_proc) (drfs_handle archive, const char* relativePath);
-typedef drfs_result (* drfs_create_directory_proc) (drfs_handle archive, const char* relativePath);
-typedef drfs_result (* drfs_move_file_proc) (drfs_handle archive, const char* relativePathOld, const char* relativePathNew);
-typedef drfs_result (* drfs_copy_file_proc) (drfs_handle archive, const char* relativePathSrc, const char* relativePathDst, dr_bool32 failIfExists);
-typedef drfs_result (* drfs_open_file_proc) (drfs_handle archive, const char* relativePath, unsigned int accessMode, drfs_handle* pHandleOut);
-typedef void (* drfs_close_file_proc) (drfs_handle archive, drfs_handle file);
-typedef drfs_result (* drfs_read_file_proc) (drfs_handle archive, drfs_handle file, void* pDataOut, size_t bytesToRead, size_t* pBytesReadOut);
-typedef drfs_result (* drfs_write_file_proc) (drfs_handle archive, drfs_handle file, const void* pData, size_t bytesToWrite, size_t* pBytesWrittenOut);
-typedef drfs_result (* drfs_seek_file_proc) (drfs_handle archive, drfs_handle file, dr_int64 bytesToSeek, drfs_seek_origin origin);
-typedef dr_uint64 (* drfs_tell_file_proc) (drfs_handle archive, drfs_handle file);
-typedef dr_uint64 (* drfs_file_size_proc) (drfs_handle archive, drfs_handle file);
-typedef void (* drfs_flush_file_proc) (drfs_handle archive, drfs_handle file);
-
-typedef struct
-{
- drfs_is_valid_extension_proc is_valid_extension;
- drfs_open_archive_proc open_archive;
- drfs_close_archive_proc close_archive;
- drfs_get_file_info_proc get_file_info;
- drfs_begin_iteration_proc begin_iteration;
- drfs_end_iteration_proc end_iteration;
- drfs_next_iteration_proc next_iteration;
- drfs_delete_file_proc delete_file;
- drfs_create_directory_proc create_directory;
- drfs_move_file_proc move_file;
- drfs_copy_file_proc copy_file;
- drfs_open_file_proc open_file;
- drfs_close_file_proc close_file;
- drfs_read_file_proc read_file;
- drfs_write_file_proc write_file;
- drfs_seek_file_proc seek_file;
- drfs_tell_file_proc tell_file;
- drfs_file_size_proc file_size;
- drfs_flush_file_proc flush_file;
-} drfs_archive_callbacks;
-
-struct drfs_file_info
-{
- // The absolute path of the file.
- char absolutePath[DRFS_MAX_PATH];
-
- // The size of the file, in bytes.
- dr_uint64 sizeInBytes;
-
- // The time the file was last modified.
- dr_uint64 lastModifiedTime;
-
- // File attributes.
- unsigned int attributes;
-};
-
-struct drfs_iterator
-{
- // A pointer to the archive that contains the folder being iterated.
- drfs_archive* pArchive;
-
- // A pointer to the iterator's internal handle that was returned with begin_iteration().
- drfs_handle internalIteratorHandle;
-
- // The file info.
- drfs_file_info info;
-};
-
-
-typedef struct
-{
- drfs_archive_callbacks* pBuffer;
- unsigned int count;
-} drfs_callbacklist;
-
-typedef struct
-{
- char absolutePath[DRFS_MAX_PATH];
-} drfs_basepath;
-
-typedef struct
-{
- drfs_basepath* pBuffer;
- unsigned int capacity;
- unsigned int count;
-} drfs_basedirs;
-
-struct drfs_context
-{
- // The list of archive callbacks which are used for loading non-native archives. This does not include the native callbacks.
- drfs_callbacklist archiveCallbacks;
-
- // The list of base directories.
- drfs_basedirs baseDirectories;
-
- // The write base directory.
- char writeBaseDirectory[DRFS_MAX_PATH];
-
- // Keeps track of whether or not write directory guard is enabled.
- dr_bool32 isWriteGuardEnabled;
-};
-
-
-// Initializes a pre-allocated context.
-drfs_result drfs_init(drfs_context* pContext);
-
-// Uninitializes a context.
-drfs_result drfs_uninit(drfs_context* pContext);
-
-
-// Creates an empty context.
-drfs_context* drfs_create_context();
-
-// Deletes the given context.
-//
-// This does not close any files or archives - it is up to the application to ensure those are tidied up.
-void drfs_delete_context(drfs_context* pContext);
-
-
-// Registers an archive back-end.
-void drfs_register_archive_backend(drfs_context* pContext, drfs_archive_callbacks callbacks);
-
-
-// Inserts a base directory at a specific priority position.
-//
-// A lower value index means a higher priority. This must be in the range of [0, drfs_get_base_directory_count()].
-void drfs_insert_base_directory(drfs_context* pContext, const char* absolutePath, unsigned int index);
-
-// Adds a base directory to the end of the list.
-//
-// The further down the list the base directory, the lower priority is will receive. This adds it to the end which
-// means it it given a lower priority to those that are already in the list. Use drfs_insert_base_directory() to
-// insert the base directory at a specific position.
-//
-// Base directories must be an absolute path to a real directory.
-void drfs_add_base_directory(drfs_context* pContext, const char* absolutePath);
-
-// Removes the given base directory.
-void drfs_remove_base_directory(drfs_context* pContext, const char* absolutePath);
-
-// Removes the directory at the given index.
-//
-// If you need to remove every base directory, use drfs_remove_all_base_directories() since that is more efficient.
-void drfs_remove_base_directory_by_index(drfs_context* pContext, unsigned int index);
-
-// Removes every base directory from the given context.
-void drfs_remove_all_base_directories(drfs_context* pContext);
-
-// Retrieves the number of base directories attached to the given context.
-unsigned int drfs_get_base_directory_count(drfs_context* pContext);
-
-// Retrieves the base directory at the given index.
-const char* drfs_get_base_directory_by_index(drfs_context* pContext, unsigned int index);
-
-
-// Sets the base directory for write operations (including delete).
-//
-// When doing a write operation using a relative path, the full path will be resolved using this directory as the base.
-//
-// If the base write directory is not set, and absolute path must be used for all write operations.
-//
-// If the write directory guard is enabled, all write operations that are attempted at a higher level than this directory
-// will fail.
-void drfs_set_base_write_directory(drfs_context* pContext, const char* absolutePath);
-
-// Retrieves the base write directory.
-dr_bool32 drfs_get_base_write_directory(drfs_context* pContext, char* absolutePathOut, unsigned int absolutePathOutSize);
-
-// Enables the write directory guard.
-void drfs_enable_write_directory_guard(drfs_context* pContext);
-
-// Disables the write directory guard.
-void drfs_disable_write_directory_guard(drfs_context* pContext);
-
-// Determines whether or not the base directory guard is enabled.
-dr_bool32 drfs_is_write_directory_guard_enabled(drfs_context* pContext);
-
-
-// Opens an archive at the given path.
-//
-// If the given path points to a directory on the native file system an archive will be created at that
-// directory. If the path points to an archive file such as a .zip file, dr_fs will hold a handle to
-// that file until the archive is closed with drfs_close_archive(). Keep this in mind if you are keeping
-// many archives open at a time on platforms that limit the number of files one can have open at any given
-// time.
-//
-// The given path must be either absolute, or relative to one of the base directories.
-//
-// The path can be nested, such as "C:/my_zip_file.zip/my_inner_zip_file.zip".
-drfs_result drfs_open_archive(drfs_context* pContext, const char* absoluteOrRelativePath, unsigned int accessMode, drfs_archive** ppArchiveOut);
-
-// Opens the archive that owns the given file.
-//
-// This is different to drfs_open_archive() in that it can accept non-archive files. It will open the
-// archive that directly owns the file. In addition, it will output the path of the file, relative to the
-// archive.
-//
-// If the given file is an archive itself, the archive that owns that archive will be opened. If the file
-// is a file on the native file system, the returned archive will represent the folder it's directly
-// contained in.
-drfs_result drfs_open_owner_archive(drfs_context* pContext, const char* absoluteOrRelativePath, unsigned int accessMode, char* relativePathOut, size_t relativePathOutSize, drfs_archive** ppArchiveOut);
-
-// Closes the given archive.
-void drfs_close_archive(drfs_archive* pArchive);
-
-// Opens a file relative to the given archive.
-drfs_result drfs_open_file_from_archive(drfs_archive* pArchive, const char* relativePath, unsigned int accessMode, drfs_file** ppFileOut);
-
-
-
-// Opens a file.
-//
-// When opening the file in write mode, the write pointer will always be sitting at the start of the file.
-drfs_result drfs_open(drfs_context* pContext, const char* absoluteOrRelativePath, unsigned int accessMode, drfs_file** ppFileOut);
-
-// Closes the given file.
-void drfs_close(drfs_file* pFile);
-
-// Reads data from the given file.
-//
-// Returns DR_TRUE if successful; DR_FALSE otherwise. If the value output to is less than it means the file is at the end.
-//
-// Do not use the return value to check if the end of the file has been reached. Instead, compare to the value returned to .
-drfs_result drfs_read(drfs_file* pFile, void* pDataOut, size_t bytesToRead, size_t* pBytesReadOut);
-
-// Writes data to the given file.
-drfs_result drfs_write(drfs_file* pFile, const void* pData, size_t bytesToWrite, size_t* pBytesWrittenOut);
-
-// Seeks the file pointer by the given number of bytes, relative to the specified origin.
-drfs_result drfs_seek(drfs_file* pFile, dr_int64 bytesToSeek, drfs_seek_origin origin);
-
-// Retrieves the current position of the file pointer.
-dr_uint64 drfs_tell(drfs_file* pFile);
-
-// Retrieves the size of the given file.
-dr_uint64 drfs_size(drfs_file* pFile);
-
-// Flushes the given file.
-void drfs_flush(drfs_file* pFile);
-
-
-// Locks the given file for simple mutal exclusion.
-//
-// If DR_FALSE is returned it means there was an error and the operation should be aborted.
-dr_bool32 drfs_lock(drfs_file* pFile);
-
-// Unlocks the given file for simple mutal exclusion.
-void drfs_unlock(drfs_file* pFile);
-
-// Unlocked drfs_read() - should only be called inside a drfs_lock()/drfs_unlock() pair.
-drfs_result drfs_read_nolock(drfs_file* pFile, void* pDataOut, size_t bytesToRead, size_t* pBytesReadOut);
-
-// Unlocked drfs_write() - should only be called inside a drfs_lock()/drfs_unlock() pair.
-drfs_result drfs_write_nolock(drfs_file* pFile, const void* pData, size_t bytesToWrite, size_t* pBytesWrittenOut);
-
-// Unlocked drfs_seek() - should only be called inside a drfs_lock()/drfs_unlock() pair.
-drfs_result drfs_seek_nolock(drfs_file* pFile, dr_int64 bytesToSeek, drfs_seek_origin origin);
-
-// Unlocked drfs_tell() - should only be called inside a drfs_lock()/drfs_unlock() pair.
-dr_uint64 drfs_tell_nolock(drfs_file* pFile);
-
-// Unlocked drfs_size() - should only be called inside a drfs_lock()/drfs_unlock() pair.
-dr_uint64 drfs_size_nolock(drfs_file* pFile);
-
-
-// Retrieves information about the file at the given path.
-//
-// is allowed to be null, in which case the call is equivalent to simply checking if the file exists.
-drfs_result drfs_get_file_info(drfs_context* pContext, const char* absoluteOrRelativePath, drfs_file_info* fi);
-
-
-// Creates an iterator for iterating over the files and folders in the given directory.
-dr_bool32 drfs_begin(drfs_context* pContext, const char* absoluteOrRelativePath, drfs_iterator* pIteratorOut);
-
-// Goes to the next file or folder based on the given iterator.
-dr_bool32 drfs_next(drfs_context* pContext, drfs_iterator* pIterator);
-
-// Closes the given iterator.
-//
-// This is not needed if drfs_next_iteration() returns DR_FALSE naturally. If iteration is terminated early, however, this
-// needs to be called on the iterator to ensure internal resources are freed.
-void drfs_end(drfs_context* pContext, drfs_iterator* pIterator);
-
-
-// Deletes the file at the given path.
-//
-// The path must be a absolute, or relative to the write directory.
-drfs_result drfs_delete_file(drfs_context* pContext, const char* path);
-
-// Creates a directory.
-//
-// The path must be a absolute, or relative to the write directory.
-drfs_result drfs_create_directory(drfs_context* pContext, const char* path);
-
-// Moves or renames the given file.
-//
-// The path must be a absolute, or relative to the write directory. This will fail if:
-// - the file already exists
-// - the old and new paths are across different archives
-// - the archive containing both the old and new paths is read-only
-// - the destinations directory structure does not exist
-//
-// Consider using drfs_copy_file() for more flexibility with moving files to a different location.
-drfs_result drfs_move_file(drfs_context* pContext, const char* pathOld, const char* pathNew);
-
-// Copies a file.
-//
-// The destination path must be a absolute, or relative to the write directory.
-drfs_result drfs_copy_file(drfs_context* pContext, const char* srcPath, const char* dstPath, dr_bool32 failIfExists);
-
-
-// Determines whether or not the given path refers to an archive file.
-//
-// This does not validate that the archive file exists or is valid. This will also return DR_FALSE if the path refers
-// to a folder on the normal file system.
-//
-// Use drfs_open_archive() to check that the archive file actually exists.
-dr_bool32 drfs_is_archive_path(drfs_context* pContext, const char* path);
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// High Level API
-//
-///////////////////////////////////////////////////////////////////////////////
-
-// Free's memory that was allocated internally by dr_fs. This is used when dr_fs allocates memory via a high-level helper API
-// and the application is done with that memory.
-void drfs_free(void* p);
-
-// Finds the absolute, verbose path of the given path.
-drfs_result drfs_find_absolute_path(drfs_context* pContext, const char* relativePath, char* absolutePathOut, size_t absolutePathOutSize);
-
-// Finds the absolute, verbose path of the given path, using the given path as the higest priority base path.
-drfs_result drfs_find_absolute_path_explicit_base(drfs_context* pContext, const char* relativePath, const char* highestPriorityBasePath, char* absolutePathOut, size_t absolutePathOutSize);
-
-// Helper function for determining whether or not the given path refers to a base directory.
-dr_bool32 drfs_is_base_directory(drfs_context* pContext, const char* baseDir);
-
-// Helper function for writing a string.
-drfs_result drfs_write_string(drfs_file* pFile, const char* str);
-
-// Helper function for writing a string, and then inserting a new line right after it.
-//
-// The new line character is "\n" and NOT "\r\n".
-drfs_result drfs_write_line(drfs_file* pFile, const char* str);
-
-
-// Helper function for opening a binary file and retrieving it's data in one go.
-//
-// Free the returned pointer with drfs_free()
-void* drfs_open_and_read_binary_file(drfs_context* pContext, const char* absoluteOrRelativePath, size_t* pSizeInBytesOut);
-
-// Helper function for opening a text file and retrieving it's data in one go.
-//
-// Free the returned pointer with drfs_free().
-//
-// The returned string is null terminated. The size returned by pSizeInBytesOut does not include the null terminator.
-char* drfs_open_and_read_text_file(drfs_context* pContext, const char* absoluteOrRelativePath, size_t* pSizeInBytesOut);
-
-// Helper function for opening a file, writing the given data, and then closing it. This deletes the contents of the existing file, if any.
-drfs_result drfs_open_and_write_binary_file(drfs_context* pContext, const char* absoluteOrRelativePath, const void* pData, size_t dataSize);
-
-// Helper function for opening a file, writing the given textual data, and then closing it. This deletes the contents of the existing file, if any.
-drfs_result drfs_open_and_write_text_file(drfs_context* pContext, const char* absoluteOrRelativePath, const char* pTextData);
-
-
-// Helper function for determining whether or not the given path refers to an existing file or directory.
-dr_bool32 drfs_exists(drfs_context* pContext, const char* absoluteOrRelativePath);
-
-// Determines if the given path refers to an existing file (not a directory).
-//
-// This will return DR_FALSE for directories. Use drfs_exists() to check for either a file or directory.
-dr_bool32 drfs_is_existing_file(drfs_context* pContext, const char* absoluteOrRelativePath);
-
-// Determines if the given path refers to an existing directory.
-dr_bool32 drfs_is_existing_directory(drfs_context* pContext, const char* absoluteOrRelativePath);
-
-// Same as drfs_create_directory(), except creates the entire directory structure recursively.
-drfs_result drfs_create_directory_recursive(drfs_context* pContext, const char* path);
-
-// Determines whether or not the given file is at the end.
-//
-// This is just a high-level helper function equivalent to drfs_tell(pFile) == drfs_size(pFile).
-dr_bool32 drfs_eof(drfs_file* pFile);
-
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif //dr_fs_h
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// IMPLEMENTATION
-//
-///////////////////////////////////////////////////////////////////////////////
-
-#ifdef DR_FS_IMPLEMENTATION
-#include
-#include
-#include
-
-#ifdef _WIN32
-#include
-#else
-#include
-#include
-#include
-#endif
-
-// Whether or not the file owns the archive object it's part of.
-#define DR_FS_OWNS_PARENT_ARCHIVE 0x00000001
-
-
-static int drfs__strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
-{
-#ifdef _MSC_VER
- return strcpy_s(dst, dstSizeInBytes, src);
-#else
- if (dst == 0) {
- return EINVAL;
- }
- if (dstSizeInBytes == 0) {
- return ERANGE;
- }
- if (src == 0) {
- dst[0] = '\0';
- return EINVAL;
- }
-
- size_t i;
- for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
- dst[i] = src[i];
- }
-
- if (i < dstSizeInBytes) {
- dst[i] = '\0';
- return 0;
- }
-
- dst[0] = '\0';
- return ERANGE;
-#endif
-}
-
-static int drfs__strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
-{
-#ifdef _MSC_VER
- return strncpy_s(dst, dstSizeInBytes, src, count);
-#else
- if (dst == 0) {
- return EINVAL;
- }
- if (dstSizeInBytes == 0) {
- return EINVAL;
- }
- if (src == 0) {
- dst[0] = '\0';
- return EINVAL;
- }
-
- size_t maxcount = count;
- if (count == ((size_t)-1) || count >= dstSizeInBytes) { // -1 = _TRUNCATE
- maxcount = dstSizeInBytes - 1;
- }
-
- size_t i;
- for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
- dst[i] = src[i];
- }
-
- if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
- dst[i] = '\0';
- return 0;
- }
-
- dst[0] = '\0';
- return ERANGE;
-#endif
-}
-
-static int drfs__strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
-{
-#ifdef _MSC_VER
- return strcat_s(dst, dstSizeInBytes, src);
-#else
- if (dst == 0) {
- return EINVAL;
- }
- if (dstSizeInBytes == 0) {
- return ERANGE;
- }
- if (src == 0) {
- dst[0] = '\0';
- return EINVAL;
- }
-
- char* dstorig = dst;
-
- while (dstSizeInBytes > 0 && dst[0] != '\0') {
- dst += 1;
- dstSizeInBytes -= 1;
- }
-
- if (dstSizeInBytes == 0) {
- return EINVAL; // Unterminated.
- }
-
-
- while (dstSizeInBytes > 0 && src[0] != '\0') {
- *dst++ = *src++;
- dstSizeInBytes -= 1;
- }
-
- if (dstSizeInBytes > 0) {
- dst[0] = '\0';
- } else {
- dstorig[0] = '\0';
- return ERANGE;
- }
-
- return 0;
-#endif
-}
-
-static int drfs__stricmp(const char* string1, const char* string2)
-{
-#ifdef _MSC_VER
- return _stricmp(string1, string2);
-#else
- return strcasecmp(string1, string2);
-#endif
-}
-
-
-static dr_bool32 drfs_basedirs_init(drfs_basedirs* pBasePaths)
-{
- if (pBasePaths == NULL) {
- return DR_FALSE;
- }
-
- pBasePaths->pBuffer = 0;
- pBasePaths->capacity = 0;
- pBasePaths->count = 0;
-
- return DR_TRUE;
-}
-
-static void drfs_basedirs_uninit(drfs_basedirs* pBasePaths)
-{
- if (pBasePaths == NULL) {
- return;
- }
-
- free(pBasePaths->pBuffer);
-}
-
-static dr_bool32 drfs_basedirs_inflateandinsert(drfs_basedirs* pBasePaths, const char* absolutePath, unsigned int index)
-{
- if (pBasePaths == NULL) {
- return DR_FALSE;
- }
-
- unsigned int newBufferSize = (pBasePaths->capacity == 0) ? 2 : pBasePaths->capacity*2;
-
- drfs_basepath* pOldBuffer = pBasePaths->pBuffer;
- drfs_basepath* pNewBuffer = (drfs_basepath*)malloc(newBufferSize * sizeof(drfs_basepath));
- if (pNewBuffer == NULL) {
- return DR_FALSE;
- }
-
- for (unsigned int iDst = 0; iDst < index; ++iDst) {
- memcpy(pNewBuffer + iDst, pOldBuffer + iDst, sizeof(drfs_basepath));
- }
-
- drfs__strcpy_s((pNewBuffer + index)->absolutePath, DRFS_MAX_PATH, absolutePath);
-
- for (unsigned int iDst = index; iDst < pBasePaths->count; ++iDst) {
- memcpy(pNewBuffer + iDst + 1, pOldBuffer + iDst, sizeof(drfs_basepath));
- }
-
-
- pBasePaths->pBuffer = pNewBuffer;
- pBasePaths->capacity = newBufferSize;
- pBasePaths->count += 1;
-
- free(pOldBuffer);
- return DR_TRUE;
-}
-
-static dr_bool32 drfs_basedirs_movedown1slot(drfs_basedirs* pBasePaths, unsigned int index)
-{
- if (pBasePaths == NULL || pBasePaths->count >= pBasePaths->capacity) {
- return DR_FALSE;
- }
-
- for (unsigned int iDst = pBasePaths->count; iDst > index; --iDst) {
- memcpy(pBasePaths->pBuffer + iDst, pBasePaths->pBuffer + iDst - 1, sizeof(drfs_basepath));
- }
-
- return DR_TRUE;
-}
-
-static dr_bool32 drfs_basedirs_insert(drfs_basedirs* pBasePaths, const char* absolutePath, unsigned int index)
-{
- if (pBasePaths == NULL || index > pBasePaths->count) {
- return DR_FALSE;
- }
-
- if (pBasePaths->count == pBasePaths->capacity) {
- return drfs_basedirs_inflateandinsert(pBasePaths, absolutePath, index);
- } else {
- if (!drfs_basedirs_movedown1slot(pBasePaths, index)) {
- return DR_FALSE;
- }
-
- drfs__strcpy_s((pBasePaths->pBuffer + index)->absolutePath, DRFS_MAX_PATH, absolutePath);
- pBasePaths->count += 1;
-
- return DR_TRUE;
- }
-}
-
-static dr_bool32 drfs_basedirs_remove(drfs_basedirs* pBasePaths, unsigned int index)
-{
- if (pBasePaths == NULL || index >= pBasePaths->count) {
- return DR_FALSE;
- }
-
- assert(pBasePaths->count > 0);
-
- for (unsigned int iDst = index; iDst < pBasePaths->count - 1; ++iDst) {
- memcpy(pBasePaths->pBuffer + iDst, pBasePaths->pBuffer + iDst + 1, sizeof(drfs_basepath));
- }
-
- pBasePaths->count -= 1;
-
- return DR_TRUE;
-}
-
-static void drfs_basedirs_clear(drfs_basedirs* pBasePaths)
-{
- if (pBasePaths == NULL) {
- return;
- }
-
- drfs_basedirs_uninit(pBasePaths);
- drfs_basedirs_init(pBasePaths);
-}
-
-
-
-static dr_bool32 drfs_callbacklist_init(drfs_callbacklist* pList)
-{
- if (pList == NULL) {
- return DR_FALSE;
- }
-
- pList->pBuffer = 0;
- pList->count = 0;
-
- return DR_TRUE;
-}
-
-static void drfs_callbacklist_uninit(drfs_callbacklist* pList)
-{
- if (pList == NULL) {
- return;
- }
-
- free(pList->pBuffer);
-}
-
-static dr_bool32 drfs_callbacklist_inflate(drfs_callbacklist* pList)
-{
- if (pList == NULL) {
- return DR_FALSE;
- }
-
- drfs_archive_callbacks* pOldBuffer = pList->pBuffer;
- drfs_archive_callbacks* pNewBuffer = (drfs_archive_callbacks*)malloc((pList->count + 1) * sizeof(drfs_archive_callbacks));
- if (pNewBuffer == NULL) {
- return DR_FALSE;
- }
-
- for (unsigned int iDst = 0; iDst < pList->count; ++iDst) {
- memcpy(pNewBuffer + iDst, pOldBuffer + iDst, sizeof(drfs_archive_callbacks));
- }
-
- pList->pBuffer = pNewBuffer;
-
- free(pOldBuffer);
- return DR_TRUE;
-}
-
-static dr_bool32 drfs_callbacklist_pushback(drfs_callbacklist* pList, drfs_archive_callbacks callbacks)
-{
- if (pList == NULL) {
- return DR_FALSE;
- }
-
- if (!drfs_callbacklist_inflate(pList)) {
- return DR_FALSE;
- }
-
- pList->pBuffer[pList->count] = callbacks;
- pList->count += 1;
-
- return DR_TRUE;
-}
-
-
-struct drfs_archive
-{
- // A pointer to the context that owns this archive.
- drfs_context* pContext;
-
- // A pointer to the archive that contains this archive. This can be null in which case it is the top level archive (which is always native).
- drfs_archive* pParentArchive;
-
- // A pointer to the file containing the data of the archive file.
- drfs_file* pFile;
-
- // The internal handle that was returned when the archive was opened by the archive definition.
- drfs_handle internalArchiveHandle;
-
- // Flags. Can be a combination of the following:
- // DR_FS_OWNS_PARENT_ARCHIVE
- int flags;
-
- // The callbacks to use when working with on the archive. This contains all of the functions for opening files, reading
- // files, etc.
- drfs_archive_callbacks callbacks;
-
- // The absolute, verbose path of the archive. For native archives, this will be the name of the folder on the native file
- // system. For non-native archives (zip, etc.) this is the the path of the archive file.
- char absolutePath[DRFS_MAX_PATH]; // Change this to char absolutePath[1] and have it sized exactly as needed.
-};
-
-struct drfs_file
-{
- // A pointer to the archive that contains the file. This should never be null. Retrieve a pointer to the contex from this
- // by doing pArchive->pContext. The file containing the archive's raw data can be retrieved with pArchive->pFile.
- drfs_archive* pArchive;
-
- // The internal file handle for use by the archive that owns it.
- drfs_handle internalFileHandle;
-
-
- // Flags. Can be a combination of the following:
- // DR_FS_OWNS_PARENT_ARCHIVE
- int flags;
-
-
- // The critical section for locking and unlocking files.
-#ifdef _WIN32
-#ifdef DR_FS_WIN32_USE_EVENT_MUTEX
- HANDLE lock;
-#else
- CRITICAL_SECTION lock;
-#endif
-#else
- pthread_mutex_t lock;
-#endif
-};
-
-
-//// Path Manipulation ////
-//
-// Functionality below is taken straight from dr_path, but namespaced as "drfs" to avoid naming conflicts.
-
-// Structure representing a section of a path.
-typedef struct
-{
- size_t offset;
- size_t length;
-} drfs_drpath_segment;
-
-// Structure used for iterating over a path while at the same time providing useful and easy-to-use information about the iteration.
-typedef struct drfs_drpath_iterator
-{
- const char* path;
- drfs_drpath_segment segment;
-} drfs_drpath_iterator;
-
-static dr_bool32 drfs_drpath_next(drfs_drpath_iterator* i)
-{
- if (i == NULL || i->path == NULL) {
- return DR_FALSE;
- }
-
- i->segment.offset = i->segment.offset + i->segment.length;
- i->segment.length = 0;
-
- while (i->path[i->segment.offset] != '\0' && (i->path[i->segment.offset] == '/' || i->path[i->segment.offset] == '\\')) {
- i->segment.offset += 1;
- }
-
- if (i->path[i->segment.offset] == '\0') {
- return DR_FALSE;
- }
-
-
- while (i->path[i->segment.offset + i->segment.length] != '\0' && (i->path[i->segment.offset + i->segment.length] != '/' && i->path[i->segment.offset + i->segment.length] != '\\')) {
- i->segment.length += 1;
- }
-
- return DR_TRUE;
-}
-
-static dr_bool32 drfs_drpath_prev(drfs_drpath_iterator* i)
-{
- if (i == NULL || i->path == NULL || i->segment.offset == 0) {
- return DR_FALSE;
- }
-
- i->segment.length = 0;
-
- do
- {
- i->segment.offset -= 1;
- } while (i->segment.offset > 0 && (i->path[i->segment.offset] == '/' || i->path[i->segment.offset] == '\\'));
-
- if (i->segment.offset == 0) {
- if (i->path[i->segment.offset] == '/' || i->path[i->segment.offset] == '\\') {
- i->segment.length = 0;
- return DR_TRUE;
- }
-
- return DR_FALSE;
- }
-
-
- size_t offsetEnd = i->segment.offset + 1;
- while (i->segment.offset > 0 && (i->path[i->segment.offset] != '/' && i->path[i->segment.offset] != '\\')) {
- i->segment.offset -= 1;
- }
-
- if (i->path[i->segment.offset] == '/' || i->path[i->segment.offset] == '\\') {
- i->segment.offset += 1;
- }
-
-
- i->segment.length = offsetEnd - i->segment.offset;
-
- return DR_TRUE;
-}
-
-static dr_bool32 drfs_drpath_first(const char* path, drfs_drpath_iterator* i)
-{
- if (i == 0) return DR_FALSE;
- i->path = path;
- i->segment.offset = 0;
- i->segment.length = 0;
-
- if (path == 0 || path[0] == '\0') {
- return DR_FALSE;
- }
-
- while (i->path[i->segment.length] != '\0' && (i->path[i->segment.length] != '/' && i->path[i->segment.length] != '\\')) {
- i->segment.length += 1;
- }
-
- return DR_TRUE;
-}
-
-static dr_bool32 drfs_drpath_last(const char* path, drfs_drpath_iterator* i)
-{
- if (i == 0) return DR_FALSE;
- i->path = path;
- i->segment.offset = 0;
- i->segment.length = 0;
-
- if (path == 0 || path[0] == '\0') {
- return DR_FALSE;
- }
-
- i->path = path;
- i->segment.offset = strlen(path);
- i->segment.length = 0;
-
- return drfs_drpath_prev(i);
-}
-
-static dr_bool32 drfs_drpath_segments_equal(const char* s0Path, const drfs_drpath_segment s0, const char* s1Path, const drfs_drpath_segment s1)
-{
- if (s0Path == NULL || s1Path == NULL) {
- return DR_FALSE;
- }
-
- if (s0.length != s1.length) {
- return DR_FALSE;
- }
-
- return strncmp(s0Path + s0.offset, s1Path + s1.offset, s0.length) == 0;
-}
-
-static dr_bool32 drfs_drpath_iterators_equal(const drfs_drpath_iterator i0, const drfs_drpath_iterator i1)
-{
- return drfs_drpath_segments_equal(i0.path, i0.segment, i1.path, i1.segment);
-}
-
-static dr_bool32 drfs_drpath_is_linux_style_root_segment(const drfs_drpath_iterator i)
-{
- if (i.path == NULL) {
- return DR_FALSE;
- }
-
- if (i.segment.offset == 0 && i.segment.length == 0) {
- return DR_TRUE; // "/" style root.
- }
-
- return DR_FALSE;
-}
-
-static dr_bool32 drfs_drpath_is_win32_style_root_segment(const drfs_drpath_iterator i)
-{
- if (i.path == NULL) {
- return DR_FALSE;
- }
-
- if (i.segment.offset == 0 && i.segment.length == 2) {
- if (((i.path[0] >= 'a' && i.path[0] <= 'z') || (i.path[0] >= 'A' && i.path[0] <= 'Z')) && i.path[1] == ':') {
- return DR_TRUE;
- }
- }
-
- return DR_FALSE;
-}
-
-static dr_bool32 drfs_drpath_is_root_segment(const drfs_drpath_iterator i)
-{
- return drfs_drpath_is_linux_style_root_segment(i) || drfs_drpath_is_win32_style_root_segment(i);
-}
-
-static dr_bool32 drfs_drpath_append_iterator(char* base, size_t baseBufferSizeInBytes, drfs_drpath_iterator i)
-{
- if (base == NULL) {
- return DR_FALSE;
- }
-
- size_t path1Length = strlen(base);
- size_t path2Length = i.segment.length;
-
- if (path1Length >= baseBufferSizeInBytes) {
- return DR_FALSE;
- }
-
- if (drfs_drpath_is_linux_style_root_segment(i)) {
- if (baseBufferSizeInBytes > 1) {
- base[0] = '/';
- base[1] = '\0';
- return DR_TRUE;
- }
- }
-
-
- // Slash.
- if (path1Length > 0 && base[path1Length - 1] != '/' && base[path1Length - 1] != '\\') {
- base[path1Length] = '/';
- path1Length += 1;
- }
-
- // Other part.
- if (path1Length + path2Length >= baseBufferSizeInBytes) {
- path2Length = baseBufferSizeInBytes - path1Length - 1; // -1 for the null terminator.
- }
-
- drfs__strncpy_s(base + path1Length, baseBufferSizeInBytes - path1Length, i.path + i.segment.offset, path2Length);
-
- return DR_TRUE;
-}
-
-static dr_bool32 drfs_drpath_is_descendant(const char* descendantAbsolutePath, const char* parentAbsolutePath)
-{
- drfs_drpath_iterator iChild;
- if (!drfs_drpath_first(descendantAbsolutePath, &iChild)) {
- return DR_FALSE; // The descendant is an empty string which makes it impossible for it to be a descendant.
- }
-
- drfs_drpath_iterator iParent;
- if (drfs_drpath_first(parentAbsolutePath, &iParent))
- {
- do
- {
- // If the segment is different, the paths are different and thus it is not a descendant.
- if (!drfs_drpath_iterators_equal(iParent, iChild)) {
- return DR_FALSE;
- }
-
- if (!drfs_drpath_next(&iChild)) {
- return DR_FALSE; // The descendant is shorter which means it's impossible for it to be a descendant.
- }
-
- } while (drfs_drpath_next(&iParent));
- }
-
- return DR_TRUE;
-}
-
-static dr_bool32 drfs_drpath_is_child(const char* childAbsolutePath, const char* parentAbsolutePath)
-{
- drfs_drpath_iterator iChild;
- if (!drfs_drpath_first(childAbsolutePath, &iChild)) {
- return DR_FALSE; // The descendant is an empty string which makes it impossible for it to be a descendant.
- }
-
- drfs_drpath_iterator iParent;
- if (drfs_drpath_first(parentAbsolutePath, &iParent))
- {
- do
- {
- // If the segment is different, the paths are different and thus it is not a descendant.
- if (!drfs_drpath_iterators_equal(iParent, iChild)) {
- return DR_FALSE;
- }
-
- if (!drfs_drpath_next(&iChild)) {
- return DR_FALSE; // The descendant is shorter which means it's impossible for it to be a descendant.
- }
-
- } while (drfs_drpath_next(&iParent));
- }
-
- // At this point we have finished iteration of the parent, which should be shorter one. We now do one more iterations of
- // the child to ensure it is indeed a direct child and not a deeper descendant.
- return !drfs_drpath_next(&iChild);
-}
-
-static void drfs_drpath_base_path(char* path)
-{
- if (path == NULL) {
- return;
- }
-
- char* baseend = path;
-
- // We just loop through the path until we find the last slash.
- while (path[0] != '\0') {
- if (path[0] == '/' || path[0] == '\\') {
- baseend = path;
- }
-
- path += 1;
- }
-
-
- // Now we just loop backwards until we hit the first non-slash.
- while (baseend > path) {
- if (baseend[0] != '/' && baseend[0] != '\\') {
- break;
- }
-
- baseend -= 1;
- }
-
-
- // We just put a null terminator on the end.
- baseend[0] = '\0';
-}
-
-static void drfs_drpath_copy_base_path(const char* path, char* baseOut, size_t baseSizeInBytes)
-{
- if (path == NULL || baseOut == NULL || baseSizeInBytes == 0) {
- return;
- }
-
- drfs__strcpy_s(baseOut, baseSizeInBytes, path);
- drfs_drpath_base_path(baseOut);
-}
-
-static const char* drfs_drpath_file_name(const char* path)
-{
- if (path == NULL) {
- return NULL;
- }
-
- const char* fileName = path;
-
- // We just loop through the path until we find the last slash.
- while (path[0] != '\0') {
- if (path[0] == '/' || path[0] == '\\') {
- fileName = path;
- }
-
- path += 1;
- }
-
- // At this point the file name is sitting on a slash, so just move forward.
- while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
- fileName += 1;
- }
-
- return fileName;
-}
-
-static const char* drfs_drpath_extension(const char* path)
-{
- if (path == NULL) {
- return NULL;
- }
-
- const char* extension = drfs_drpath_file_name(path);
- const char* lastoccurance = 0;
-
- // Just find the last '.' and return.
- while (extension[0] != '\0')
- {
- extension += 1;
-
- if (extension[0] == '.') {
- extension += 1;
- lastoccurance = extension;
- }
- }
-
- return (lastoccurance != 0) ? lastoccurance : extension;
-}
-
-static dr_bool32 drfs_drpath_equal(const char* path1, const char* path2)
-{
- if (path1 == NULL || path2 == NULL) {
- return DR_FALSE;
- }
-
- if (path1 == path2 || (path1[0] == '\0' && path2[0] == '\0')) {
- return DR_TRUE; // Two empty paths are treated as the same.
- }
-
- drfs_drpath_iterator iPath1;
- drfs_drpath_iterator iPath2;
- if (drfs_drpath_first(path1, &iPath1) && drfs_drpath_first(path2, &iPath2))
- {
- dr_bool32 isPath1Valid;
- dr_bool32 isPath2Valid;
-
- do
- {
- if (!drfs_drpath_iterators_equal(iPath1, iPath2)) {
- return DR_FALSE;
- }
-
- isPath1Valid = drfs_drpath_next(&iPath1);
- isPath2Valid = drfs_drpath_next(&iPath2);
-
- } while (isPath1Valid && isPath2Valid);
-
- // At this point either iPath1 and/or iPath2 have finished iterating. If both of them are at the end, the two paths are equal.
- return isPath1Valid == isPath2Valid && iPath1.path[iPath1.segment.offset] == '\0' && iPath2.path[iPath2.segment.offset] == '\0';
- }
-
- return DR_FALSE;
-}
-
-static dr_bool32 drfs_drpath_is_relative(const char* path)
-{
- if (path == NULL) {
- return DR_FALSE;
- }
-
- drfs_drpath_iterator seg;
- if (drfs_drpath_first(path, &seg)) {
- return !drfs_drpath_is_root_segment(seg);
- }
-
- // We'll get here if the path is empty. We consider this to be a relative path.
- return DR_TRUE;
-}
-
-static dr_bool32 drfs_drpath_is_absolute(const char* path)
-{
- return !drfs_drpath_is_relative(path);
-}
-
-static dr_bool32 drfs_drpath_append(char* base, size_t baseBufferSizeInBytes, const char* other)
-{
- if (base == NULL || other == NULL) {
- return DR_FALSE;
- }
-
- size_t path1Length = strlen(base);
- size_t path2Length = strlen(other);
-
- if (path1Length >= baseBufferSizeInBytes) {
- return DR_FALSE;
- }
-
-
- // Slash.
- if (path1Length > 0 && base[path1Length - 1] != '/' && base[path1Length - 1] != '\\') {
- base[path1Length] = '/';
- path1Length += 1;
- }
-
- // Other part.
- if (path1Length + path2Length >= baseBufferSizeInBytes) {
- path2Length = baseBufferSizeInBytes - path1Length - 1; // -1 for the null terminator.
- }
-
- drfs__strncpy_s(base + path1Length, baseBufferSizeInBytes - path1Length, other, path2Length);
-
- return DR_TRUE;
-}
-
-static dr_bool32 drfs_drpath_copy_and_append(char* dst, size_t dstSizeInBytes, const char* base, const char* other)
-{
- if (dst == NULL || dstSizeInBytes == 0) {
- return DR_FALSE;
- }
-
- drfs__strcpy_s(dst, dstSizeInBytes, base);
- return drfs_drpath_append(dst, dstSizeInBytes, other);
-}
-
-// This function recursively cleans a path which is defined as a chain of iterators. This function works backwards, which means at the time of calling this
-// function, each iterator in the chain should be placed at the end of the path.
-//
-// This does not write the null terminator, nor a leading slash for absolute paths.
-static size_t _drfs_drpath_clean_trywrite(drfs_drpath_iterator* iterators, unsigned int iteratorCount, char* pathOut, size_t pathOutSizeInBytes, unsigned int ignoreCounter)
-{
- if (iteratorCount == 0) {
- return 0;
- }
-
- drfs_drpath_iterator isegment = iterators[iteratorCount - 1];
-
-
- // If this segment is a ".", we ignore it. If it is a ".." we ignore it and increment "ignoreCount".
- int ignoreThisSegment = ignoreCounter > 0 && isegment.segment.length > 0;
-
- if (isegment.segment.length == 1 && isegment.path[isegment.segment.offset] == '.')
- {
- // "."
- ignoreThisSegment = 1;
- }
- else
- {
- if (isegment.segment.length == 2 && isegment.path[isegment.segment.offset] == '.' && isegment.path[isegment.segment.offset + 1] == '.')
- {
- // ".."
- ignoreThisSegment = 1;
- ignoreCounter += 1;
- }
- else
- {
- // It's a regular segment, so decrement the ignore counter.
- if (ignoreCounter > 0) {
- ignoreCounter -= 1;
- }
- }
- }
-
-
- // The previous segment needs to be written before we can write this one.
- size_t bytesWritten = 0;
-
- drfs_drpath_iterator prev = isegment;
- if (!drfs_drpath_prev(&prev))
- {
- if (iteratorCount > 1)
- {
- iteratorCount -= 1;
- prev = iterators[iteratorCount - 1];
- }
- else
- {
- prev.path = NULL;
- prev.segment.offset = 0;
- prev.segment.length = 0;
- }
- }
-
- if (prev.segment.length > 0)
- {
- iterators[iteratorCount - 1] = prev;
- bytesWritten = _drfs_drpath_clean_trywrite(iterators, iteratorCount, pathOut, pathOutSizeInBytes, ignoreCounter);
- }
-
-
- if (!ignoreThisSegment)
- {
- if (pathOutSizeInBytes > 0)
- {
- pathOut += bytesWritten;
- pathOutSizeInBytes -= bytesWritten;
-
- if (bytesWritten > 0)
- {
- pathOut[0] = '/';
- bytesWritten += 1;
-
- pathOut += 1;
- pathOutSizeInBytes -= 1;
- }
-
- if (pathOutSizeInBytes >= isegment.segment.length)
- {
- drfs__strncpy_s(pathOut, pathOutSizeInBytes, isegment.path + isegment.segment.offset, isegment.segment.length);
- bytesWritten += isegment.segment.length;
- }
- else
- {
- drfs__strncpy_s(pathOut, pathOutSizeInBytes, isegment.path + isegment.segment.offset, pathOutSizeInBytes);
- bytesWritten += pathOutSizeInBytes;
- }
- }
- }
-
- return bytesWritten;
-}
-
-static size_t drfs_drpath_append_and_clean(char* dst, size_t dstSizeInBytes, const char* base, const char* other)
-{
- if (base == NULL || other == NULL) {
- return 0;
- }
-
- drfs_drpath_iterator last[2];
- dr_bool32 isPathEmpty0 = !drfs_drpath_last(base, last + 0);
- dr_bool32 isPathEmpty1 = !drfs_drpath_last(other, last + 1);
-
- if (isPathEmpty0 && isPathEmpty1) {
- return 0; // Both input strings are empty.
- }
-
- size_t bytesWritten = 0;
- if (base[0] == '/') {
- if (dst != NULL && dstSizeInBytes > 1) {
- dst[0] = '/';
- bytesWritten = 1;
- }
- }
-
- bytesWritten += _drfs_drpath_clean_trywrite(last, 2, dst + bytesWritten, dstSizeInBytes - bytesWritten - 1, 0); // -1 to ensure there is enough room for a null terminator later on.
- if (dstSizeInBytes > bytesWritten) {
- dst[bytesWritten] = '\0';
- }
-
- return bytesWritten + 1;
-}
-
-
-//// Private Utiltities ////
-
-// Recursively creates the given directory structure on the native file system.
-static dr_bool32 drfs_mkdir_recursive_native(const char* absolutePath);
-
-// Determines whether or not the given path is valid for writing based on the base write path and whether or not
-// the write guard is enabled.
-static dr_bool32 drfs_validate_write_path(drfs_context* pContext, const char* absoluteOrRelativePath, char* absolutePathOut, unsigned int absolutePathOutSize)
-{
- // If the path is relative, we need to convert to absolute. Then, if the write directory guard is enabled, we need to check that it's a descendant of the base path.
- if (drfs_drpath_is_relative(absoluteOrRelativePath)) {
- if (drfs_drpath_append_and_clean(absolutePathOut, absolutePathOutSize, pContext->writeBaseDirectory, absoluteOrRelativePath)) {
- absoluteOrRelativePath = absolutePathOut;
- } else {
- return DR_FALSE;
- }
- } else {
- if (drfs__strcpy_s(absolutePathOut, absolutePathOutSize, absoluteOrRelativePath) != 0) {
- return DR_FALSE;
- }
- }
-
- // If you trigger this assert it means you're trying to open a file for writing with a relative path but haven't yet
- // set the write directory. Either set the write directory with drfs_set_base_write_directory(), or use an absolute
- // path to open the file.
- assert(drfs_drpath_is_absolute(absoluteOrRelativePath));
- if (!drfs_drpath_is_absolute(absoluteOrRelativePath)) {
- return DR_FALSE;
- }
-
-
- if (drfs_is_write_directory_guard_enabled(pContext)) {
- if (drfs_drpath_is_descendant(absoluteOrRelativePath, pContext->writeBaseDirectory)) {
- return DR_TRUE;
- } else {
- return DR_FALSE;
- }
- } else {
- return DR_TRUE;
- }
-}
-
-// A simple helper function for determining the access mode to use for an archive file based on the access mode
-// of a file within that archive.
-static unsigned int drfs_archive_access_mode(unsigned int fileAccessMode)
-{
- return (fileAccessMode == DRFS_READ) ? DRFS_READ : DRFS_READ | DRFS_WRITE | DRFS_EXISTING;
-}
-
-
-
-//// Platform-Specific Section ////
-
-// The functions in this section implement a common abstraction for working with files on the native file system. When adding
-// support for a new platform you'll probably want to either implement a platform-specific back-end or force stdio.
-
-#if defined(_WIN32)
-#define DR_FS_USE_WIN32
-#else
-#define DR_FS_USE_STDIO
-#endif
-
-// Low-level function for opening a file on the native file system.
-//
-// This will fail if attempting to open a file that's inside an archive such as a zip file. It can only open
-// files that are sitting on the native file system.
-//
-// The given file path must be absolute.
-static drfs_result drfs_open_native_file(const char* absolutePath, unsigned int accessMode, drfs_handle* pHandleOut);
-
-// Closes the given native file.
-static void drfs_close_native_file(drfs_handle file);
-
-// Determines whether or not the given path refers to a directory on the native file system.
-static dr_bool32 drfs_is_native_directory(const char* absolutePath);
-
-// Determines whether or not the given path refers to a file on the native file system. This will return DR_FALSE for directories.
-static dr_bool32 drfs_is_native_file(const char* absolutePath);
-
-// Deletes a native file.
-static drfs_result drfs_delete_native_file(const char* absolutePath);
-
-// Creates a directory on the native file system.
-static drfs_result drfs_mkdir_native(const char* absolutePath);
-
-// Moves or renames a native file. Fails if the target already exists.
-static drfs_result drfs_move_native_file(const char* absolutePathOld, const char* absolutePathNew);
-
-// Creates a copy of a native file.
-static drfs_result drfs_copy_native_file(const char* absolutePathSrc, const char* absolutePathDst, dr_bool32 failIfExists);
-
-// Reads data from the given native file.
-static drfs_result drfs_read_native_file(drfs_handle file, void* pDataOut, size_t bytesToRead, size_t* pBytesReadOut);
-
-// Writes data to the given native file.
-static drfs_result drfs_write_native_file(drfs_handle file, const void* pData, size_t bytesToWrite, size_t* pBytesWrittenOut);
-
-// Seeks the given native file.
-static drfs_result drfs_seek_native_file(drfs_handle file, dr_int64 bytesToSeek, drfs_seek_origin origin);
-
-// Retrieves the read/write pointer of the given native file.
-static dr_uint64 drfs_tell_native_file(drfs_handle file);
-
-// Retrieves the size of the given native file.
-static dr_uint64 drfs_get_native_file_size(drfs_handle file);
-
-// Flushes the given native file.
-static void drfs_flush_native_file(drfs_handle file);
-
-// Retrieves information about the file OR DIRECTORY at the given path on the native file system.
-//
-// is allowed to be null, in which case the call is equivalent to simply checking if the file or directory exists.
-static drfs_result drfs_get_native_file_info(const char* absolutePath, drfs_file_info* fi);
-
-// Creates an iterator for iterating over the native files in the given directory.
-//
-// The iterator will be deleted with drfs_end_native_iteration().
-static drfs_handle drfs_begin_native_iteration(const char* absolutePath);
-
-// Deletes the iterator that was returned with drfs_end_native_iteration().
-static void drfs_end_native_iteration(drfs_handle iterator);
-
-// Retrieves information about the next native file based on the given iterator.
-static dr_bool32 drfs_next_native_iteration(drfs_handle iterator, drfs_file_info* fi);
-
-
-
-#ifdef DR_FS_USE_WIN32
-static drfs_result drfs__GetLastError_to_result()
-{
- switch (GetLastError())
- {
- case ERROR_SUCCESS: return drfs_success;
- case ERROR_FILE_NOT_FOUND: return drfs_does_not_exist;
- case ERROR_PATH_NOT_FOUND: return drfs_does_not_exist;
- case ERROR_TOO_MANY_OPEN_FILES: return drfs_too_many_open_files;
- case ERROR_ACCESS_DENIED: return drfs_permission_denied;
- case ERROR_NOT_ENOUGH_MEMORY: return drfs_out_of_memory;
- case ERROR_DISK_FULL: return drfs_no_space;
- case ERROR_HANDLE_EOF: return drfs_at_end_of_file;
- case ERROR_NEGATIVE_SEEK: return drfs_negative_seek;
- default: return drfs_unknown_error;
- }
-}
-
-static drfs_result drfs_open_native_file(const char* absolutePath, unsigned int accessMode, drfs_handle* pHandleOut)
-{
- assert(absolutePath != NULL);
- assert(pHandleOut != NULL);
-
- DWORD dwDesiredAccess = 0;
- DWORD dwShareMode = 0;
- DWORD dwCreationDisposition = OPEN_EXISTING;
-
- if ((accessMode & DRFS_READ) != 0) {
- dwDesiredAccess |= FILE_GENERIC_READ;
- dwShareMode |= FILE_SHARE_READ;
- }
-
- if ((accessMode & DRFS_WRITE) != 0) {
- dwDesiredAccess |= FILE_GENERIC_WRITE;
-
- if ((accessMode & DRFS_EXISTING) != 0) {
- if ((accessMode & DRFS_TRUNCATE) != 0) {
- dwCreationDisposition = TRUNCATE_EXISTING;
- } else {
- dwCreationDisposition = OPEN_EXISTING;
- }
- } else {
- if ((accessMode & DRFS_TRUNCATE) != 0) {
- dwCreationDisposition = CREATE_ALWAYS;
- } else {
- dwCreationDisposition = OPEN_ALWAYS;
- }
- }
- }
-
-
- HANDLE hFile = CreateFileA(absolutePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
- if (hFile == INVALID_HANDLE_VALUE)
- {
- // We failed to create the file, however it may be because we are trying to create the file and the directory structure does not exist yet. In this
- // case we want to try creating the directory structure and try again.
- if ((accessMode & DRFS_WRITE) != 0 && (accessMode & DRFS_CREATE_DIRS) != 0)
- {
- char dirAbsolutePath[DRFS_MAX_PATH];
- drfs_drpath_copy_base_path(absolutePath, dirAbsolutePath, sizeof(dirAbsolutePath));
-
- if (!drfs_is_native_directory(dirAbsolutePath) && drfs_mkdir_recursive_native(dirAbsolutePath)) {
- hFile = CreateFileA(absolutePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
- }
- }
- }
-
- if (hFile != INVALID_HANDLE_VALUE) {
- *pHandleOut = (drfs_handle)hFile;
- return drfs_success;
- }
-
- return drfs__GetLastError_to_result();
-}
-
-static void drfs_close_native_file(drfs_handle file)
-{
- CloseHandle((HANDLE)file);
-}
-
-static dr_bool32 drfs_is_native_directory(const char* absolutePath)
-{
- DWORD attributes = GetFileAttributesA(absolutePath);
- return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
-}
-
-static dr_bool32 drfs_is_native_file(const char* absolutePath)
-{
- DWORD attributes = GetFileAttributesA(absolutePath);
- return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
-}
-
-static drfs_result drfs_delete_native_file(const char* absolutePath)
-{
- BOOL wasSuccessful;
-
- DWORD attributes = GetFileAttributesA(absolutePath);
- if (attributes == INVALID_FILE_ATTRIBUTES || (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
- wasSuccessful = DeleteFileA(absolutePath);
- } else {
- wasSuccessful = RemoveDirectoryA(absolutePath);
- }
-
- if (wasSuccessful) {
- return drfs_success;
- }
-
- return drfs__GetLastError_to_result();
-}
-
-static drfs_result drfs_mkdir_native(const char* absolutePath)
-{
- BOOL wasSuccessful = CreateDirectoryA(absolutePath, NULL);
- if (wasSuccessful) {
- return drfs_success;
- }
-
- return drfs__GetLastError_to_result();
-}
-
-static drfs_result drfs_move_native_file(const char* absolutePathOld, const char* absolutePathNew)
-{
- BOOL wasSuccessful = MoveFileExA(absolutePathOld, absolutePathNew, 0);
- if (wasSuccessful) {
- return drfs_success;
- }
-
- return drfs__GetLastError_to_result();
-}
-
-static drfs_result drfs_copy_native_file(const char* absolutePathSrc, const char* absolutePathDst, dr_bool32 failIfExists)
-{
- BOOL wasSuccessful = CopyFileA(absolutePathSrc, absolutePathDst, failIfExists);
- if (wasSuccessful) {
- return drfs_success;
- }
-
- return drfs__GetLastError_to_result();
-}
-
-static drfs_result drfs_read_native_file(drfs_handle file, void* pDataOut, size_t bytesToRead, size_t* pBytesReadOut)
-{
- // Unfortunately Win32 expects a DWORD for the number of bytes to read, however we accept size_t. We need to loop to ensure
- // we handle data > 4GB correctly.
-
- size_t totalBytesRead = 0;
-
- char* pDst = (char*)pDataOut;
- dr_uint64 bytesRemaining = bytesToRead;
- while (bytesRemaining > 0)
- {
- DWORD bytesToProcess;
- if (bytesRemaining > UINT_MAX) {
- bytesToProcess = UINT_MAX;
- } else {
- bytesToProcess = (DWORD)bytesRemaining;
- }
-
-
- DWORD bytesRead;
- BOOL wasSuccessful = ReadFile((HANDLE)file, pDst, bytesToProcess, &bytesRead, NULL);
- if (!wasSuccessful || bytesRead != bytesToProcess)
- {
- // We failed to read the chunk.
- totalBytesRead += bytesRead;
-
- if (pBytesReadOut) {
- *pBytesReadOut = totalBytesRead;
- }
-
- return drfs__GetLastError_to_result();
- }
-
- pDst += bytesRead;
- bytesRemaining -= bytesRead;
- totalBytesRead += bytesRead;
- }
-
-
- if (pBytesReadOut != NULL) {
- *pBytesReadOut = totalBytesRead;
- }
-
- return drfs_success;
-}
-
-static drfs_result drfs_write_native_file(drfs_handle file, const void* pData, size_t bytesToWrite, size_t* pBytesWrittenOut)
-{
- // Unfortunately Win32 expects a DWORD for the number of bytes to write, however we accept size_t. We need to loop to ensure
- // we handle data > 4GB correctly.
-
- size_t totalBytesWritten = 0;
- const char* pSrc = (const char*)pData;
-
- size_t bytesRemaining = bytesToWrite;
- while (bytesRemaining > 0)
- {
- DWORD bytesToProcess;
- if (bytesRemaining > UINT_MAX) {
- bytesToProcess = UINT_MAX;
- } else {
- bytesToProcess = (DWORD)bytesRemaining;
- }
-
- DWORD bytesWritten;
- BOOL wasSuccessful = WriteFile((HANDLE)file, pSrc, bytesToProcess, &bytesWritten, NULL);
- if (!wasSuccessful || bytesWritten != bytesToProcess)
- {
- // We failed to write the chunk.
- totalBytesWritten += bytesWritten;
-
- if (pBytesWrittenOut) {
- *pBytesWrittenOut = totalBytesWritten;
- }
-
- return drfs__GetLastError_to_result();
- }
-
- pSrc += bytesWritten;
- bytesRemaining -= bytesWritten;
- totalBytesWritten += bytesWritten;
- }
-
- if (pBytesWrittenOut) {
- *pBytesWrittenOut = totalBytesWritten;
- }
-
- return drfs_success;;
-}
-
-static drfs_result drfs_seek_native_file(drfs_handle file, dr_int64 bytesToSeek, drfs_seek_origin origin)
-{
- LARGE_INTEGER lNewFilePointer;
- LARGE_INTEGER lDistanceToMove;
- lDistanceToMove.QuadPart = bytesToSeek;
-
- DWORD dwMoveMethod = FILE_CURRENT;
- if (origin == drfs_origin_start) {
- dwMoveMethod = FILE_BEGIN;
- } else if (origin == drfs_origin_end) {
- dwMoveMethod = FILE_END;
- }
-
- BOOL wasSuccessful = SetFilePointerEx((HANDLE)file, lDistanceToMove, &lNewFilePointer, dwMoveMethod);
- if (!wasSuccessful) {
- return drfs__GetLastError_to_result();
- }
-
- return drfs_success;
-}
-
-static dr_uint64 drfs_tell_native_file(drfs_handle file)
-{
- LARGE_INTEGER lNewFilePointer;
- LARGE_INTEGER lDistanceToMove;
- lDistanceToMove.QuadPart = 0;
-
- if (SetFilePointerEx((HANDLE)file, lDistanceToMove, &lNewFilePointer, FILE_CURRENT)) {
- return (dr_uint64)lNewFilePointer.QuadPart;
- }
-
- return 0;
-}
-
-static dr_uint64 drfs_get_native_file_size(drfs_handle file)
-{
- LARGE_INTEGER fileSize;
- if (GetFileSizeEx((HANDLE)file, &fileSize)) {
- return (dr_uint64)fileSize.QuadPart;
- }
-
- return 0;
-}
-
-static void drfs_flush_native_file(drfs_handle file)
-{
- FlushFileBuffers((HANDLE)file);
-}
-
-static drfs_result drfs_get_native_file_info(const char* absolutePath, drfs_file_info* fi)
-{
- assert(absolutePath != NULL);
-
- // is allowed to be null, in which case the call is equivalent to simply checking if the file exists.
- if (fi == NULL) {
- if (GetFileAttributesA(absolutePath) != INVALID_FILE_ATTRIBUTES) {
- return drfs_success;
- }
-
- return drfs_does_not_exist;
- }
-
- WIN32_FILE_ATTRIBUTE_DATA fad;
- if (GetFileAttributesExA(absolutePath, GetFileExInfoStandard, &fad))
- {
- ULARGE_INTEGER liSize;
- liSize.LowPart = fad.nFileSizeLow;
- liSize.HighPart = fad.nFileSizeHigh;
-
- ULARGE_INTEGER liTime;
- liTime.LowPart = fad.ftLastWriteTime.dwLowDateTime;
- liTime.HighPart = fad.ftLastWriteTime.dwHighDateTime;
-
- strcpy_s(fi->absolutePath, sizeof(fi->absolutePath), absolutePath);
- fi->sizeInBytes = liSize.QuadPart;
- fi->lastModifiedTime = liTime.QuadPart;
-
- fi->attributes = 0;
- if ((fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
- fi->attributes |= DRFS_FILE_ATTRIBUTE_DIRECTORY;
- }
- if ((fad.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0) {
- fi->attributes |= DRFS_FILE_ATTRIBUTE_READONLY;
- }
-
- return drfs_success;
- }
-
- return drfs__GetLastError_to_result();
-}
-
-typedef struct
-{
- HANDLE hFind;
- WIN32_FIND_DATAA ffd;
- char directoryPath[1];
-} drfs_iterator_win32;
-
-static drfs_handle drfs_begin_native_iteration(const char* absolutePath)
-{
- assert(drfs_drpath_is_absolute(absolutePath));
-
- char searchQuery[DRFS_MAX_PATH];
- if (strcpy_s(searchQuery, sizeof(searchQuery), absolutePath) != 0) {
- return NULL;
- }
-
- unsigned int searchQueryLength = (unsigned int)strlen(searchQuery);
- if (searchQueryLength >= DRFS_MAX_PATH - 3) {
- return NULL; // Path is too long.
- }
-
- searchQuery[searchQueryLength + 0] = '\\';
- searchQuery[searchQueryLength + 1] = '*';
- searchQuery[searchQueryLength + 2] = '\0';
-
- drfs_iterator_win32* pIterator = (drfs_iterator_win32*)malloc(sizeof(*pIterator) + searchQueryLength);
- if (pIterator == NULL) {
- return NULL;
- }
-
- ZeroMemory(pIterator, sizeof(*pIterator));
-
- pIterator->hFind = FindFirstFileA(searchQuery, &pIterator->ffd);
- if (pIterator->hFind == INVALID_HANDLE_VALUE) {
- free(pIterator);
- return NULL; // Failed to begin search.
- }
-
- strcpy_s(pIterator->directoryPath, searchQueryLength + 1, absolutePath); // This won't fail in practice.
- return (drfs_handle)pIterator;
-}
-
-static void drfs_end_native_iteration(drfs_handle iterator)
-{
- drfs_iterator_win32* pIterator = (drfs_iterator_win32*)iterator;
- assert(pIterator != NULL);
-
- if (pIterator->hFind) {
- FindClose(pIterator->hFind);
- }
-
- free(pIterator);
-}
-
-static dr_bool32 drfs_next_native_iteration(drfs_handle iterator, drfs_file_info* fi)
-{
- drfs_iterator_win32* pIterator = (drfs_iterator_win32*)iterator;
- assert(pIterator != NULL);
-
- if (pIterator->hFind != INVALID_HANDLE_VALUE && pIterator->hFind != NULL)
- {
- // Skip past "." and ".." directories.
- while (strcmp(pIterator->ffd.cFileName, ".") == 0 || strcmp(pIterator->ffd.cFileName, "..") == 0) {
- if (FindNextFileA(pIterator->hFind, &pIterator->ffd) == 0) {
- return DR_FALSE;
- }
- }
-
- if (fi != NULL)
- {
- // The absolute path actually needs to be set to the relative path. The higher level APIs are the once responsible for translating
- // it back to an absolute path.
- drfs__strcpy_s(fi->absolutePath, sizeof(fi->absolutePath), pIterator->ffd.cFileName);
-
- ULARGE_INTEGER liSize;
- liSize.LowPart = pIterator->ffd.nFileSizeLow;
- liSize.HighPart = pIterator->ffd.nFileSizeHigh;
- fi->sizeInBytes = liSize.QuadPart;
-
- ULARGE_INTEGER liTime;
- liTime.LowPart = pIterator->ffd.ftLastWriteTime.dwLowDateTime;
- liTime.HighPart = pIterator->ffd.ftLastWriteTime.dwHighDateTime;
- fi->lastModifiedTime = liTime.QuadPart;
-
- fi->attributes = 0;
- if ((pIterator->ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
- fi->attributes |= DRFS_FILE_ATTRIBUTE_DIRECTORY;
- }
- if ((pIterator->ffd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0) {
- fi->attributes |= DRFS_FILE_ATTRIBUTE_READONLY;
- }
- }
-
- if (!FindNextFileA(pIterator->hFind, &pIterator->ffd)) {
- FindClose(pIterator->hFind);
- pIterator->hFind = NULL;
- }
-
- return DR_TRUE;
- }
-
- return DR_FALSE;
-}
-#endif //DR_FS_USE_WIN32
-
-
-#ifdef DR_FS_USE_STDIO
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifdef __linux__
-#include
-#endif
-
-#define DRFS_HANDLE_TO_FD(file) ((int)((size_t)file) - 1)
-#define DRFS_FD_TO_HANDLE(fd) ((drfs_handle)(size_t)(fd + 1))
-
-static drfs_result drfs__errno_to_result()
-{
- switch (errno)
- {
- case EACCES: return drfs_permission_denied;
- case EEXIST: return drfs_already_exists;
- case EISDIR: return drfs_permission_denied;
- case EMFILE: return drfs_too_many_open_files;
- case ENFILE: return drfs_too_many_open_files;
- case ENAMETOOLONG: return drfs_path_too_long;
- case ENOENT: return drfs_does_not_exist;
- case ENOMEM: return drfs_out_of_memory;
- case ENOSPC: return drfs_no_space;
- case ENOTDIR: return drfs_not_directory;
- case EOVERFLOW: return drfs_too_large;
- case EROFS: return drfs_permission_denied;
- case ETXTBSY: return drfs_permission_denied;
- case EBADF: return drfs_invalid_args;
- case EINVAL: return drfs_invalid_args;
- default: return drfs_unknown_error;
- }
-}
-
-static int drfs__open_fd(const char* absolutePath, int flags)
-{
- return open64(absolutePath, flags, 0666);
-}
-
-static int drfs__stat64(const char* filename, struct stat64* st)
-{
- return stat64(filename, st);
-}
-
-static int drfs__fstat64(int fd, struct stat64* st)
-{
- return fstat64(fd, st);
-}
-
-static int drfs__mode_is_dir(int mode)
-{
- return S_ISDIR(mode);
-}
-
-static int drfs__mode_has_write_permission(int mode)
-{
- return (mode & S_IWUSR) != 0;
-}
-
-
-static drfs_result drfs_open_native_file(const char* absolutePath, unsigned int accessMode, drfs_handle* pHandleOut)
-{
- assert(absolutePath != NULL);
- assert(pHandleOut != NULL);
-
- *pHandleOut = NULL;
-
- int flags = 0;
- if ((accessMode & DRFS_READ) != 0) {
- if ((accessMode & DRFS_WRITE) != 0) {
- flags = O_RDWR;
- } else {
- flags = O_RDONLY;
- }
- } else {
- if ((accessMode & DRFS_WRITE) != 0) {
- flags = O_WRONLY;
- } else {
- return drfs_permission_denied; // Neither read nor write mode was specified.
- }
- }
-
- if ((accessMode & DRFS_TRUNCATE) != 0) {
- flags |= O_TRUNC;
- }
-
- if ((accessMode & DRFS_EXISTING) == 0) {
- flags |= O_CREAT;
- }
-
- int fd = drfs__open_fd(absolutePath, flags);
- if (fd == -1)
- {
- // We failed to open the file, however it could be because the directory structure is not in place. We need to check
- // the access mode flags for DRFS_CREATE_DIRS and try creating the directory structure.
- if ((accessMode & DRFS_WRITE) != 0 && (accessMode & DRFS_CREATE_DIRS) != 0)
- {
- char dirAbsolutePath[DRFS_MAX_PATH];
- drfs_drpath_copy_base_path(absolutePath, dirAbsolutePath, sizeof(dirAbsolutePath));
-
- if (!drfs_is_native_directory(dirAbsolutePath) && drfs_mkdir_recursive_native(dirAbsolutePath)) {
- fd = drfs__open_fd(absolutePath, flags);
- }
- }
- }
-
- if (fd == -1) {
- return drfs__errno_to_result();
- }
-
- *pHandleOut = DRFS_FD_TO_HANDLE(fd);
- return drfs_success;
-}
-
-static void drfs_close_native_file(drfs_handle file)
-{
- close(DRFS_HANDLE_TO_FD(file));
-}
-
-static dr_bool32 drfs_is_native_directory(const char* absolutePath)
-{
- struct stat64 info;
- if (drfs__stat64(absolutePath, &info) != 0) {
- return DR_FALSE; // Likely the folder doesn't exist.
- }
-
- return drfs__mode_is_dir(info.st_mode); // Only return DR_TRUE if it's a directory. Return DR_FALSE if it's a file.
-}
-
-static dr_bool32 drfs_is_native_file(const char* absolutePath)
-{
- struct stat64 info;
- if (drfs__stat64(absolutePath, &info) != 0) {
- return DR_FALSE; // Likely the folder doesn't exist.
- }
-
- return !drfs__mode_is_dir(info.st_mode); // Only return DR_TRUE if it's a file. Return DR_FALSE if it's a directory.
-}
-
-static drfs_result drfs_delete_native_file(const char* absolutePath)
-{
- if (remove(absolutePath) == 0) {
- return drfs_success;
- }
-
- return drfs__errno_to_result();
-}
-
-static drfs_result drfs_move_native_file(const char* absolutePathOld, const char* absolutePathNew)
-{
- if (rename(absolutePathOld, absolutePathNew) == 0) {
- return drfs_success;
- }
-
- return drfs__errno_to_result();
-}
-
-static drfs_result drfs_mkdir_native(const char* absolutePath)
-{
- if (mkdir(absolutePath, 0777) == 0) {
- return drfs_success;
- }
-
- return drfs__errno_to_result();
-}
-
-static drfs_result drfs_copy_native_file(const char* absolutePathSrc, const char* absolutePathDst, dr_bool32 failIfExists)
-{
- if (drfs_drpath_equal(absolutePathSrc, absolutePathDst)) {
- if (!failIfExists) {
- return drfs_success;
- } else {
- return drfs_already_exists;
- }
- }
-
- int fdSrc = drfs__open_fd(absolutePathSrc, O_RDONLY);
- if (fdSrc == -1) {
- return drfs__errno_to_result();
- }
-
- int fdDst = drfs__open_fd(absolutePathDst, O_WRONLY | O_TRUNC | O_CREAT | ((failIfExists) ? O_EXCL : 0));
- if (fdDst == -1) {
- close(fdSrc);
- return drfs__errno_to_result();
- }
-
- drfs_result result = drfs_success;
-
- struct stat64 info;
- if (drfs__fstat64(fdSrc, &info) == 0)
- {
-#ifdef __linux__
- ssize_t bytesRemaining = info.st_size;
- while (bytesRemaining > 0) {
- ssize_t bytesCopied = sendfile(fdDst, fdSrc, NULL, bytesRemaining);
- if (bytesCopied == -1) {
- result = drfs__errno_to_result();
- break;
- }
-
- bytesRemaining -= bytesCopied;
- }
-#else
- char buffer[BUFSIZ];
- int bytesRead;
- while ((bytesRead = read(fdSrc, buffer, sizeof(buffer))) > 0) {
- if (write(fdDst, buffer, bytesRead) != bytesRead) {
- result = drfs__errno_to_result();
- break;
- }
- }
-#endif
- }
- else
- {
- result = drfs__errno_to_result();
- }
-
- close(fdDst);
- close(fdSrc);
-
- // Permissions.
- chmod(absolutePathDst, info.st_mode & 07777);
-
- return result;
-}
-
-static drfs_result drfs_read_native_file(drfs_handle file, void* pDataOut, size_t bytesToRead, size_t* pBytesReadOut)
-{
- // The documentation for read() states that if the number of bytes being read (bytesToRead) is larger than SSIZE_MAX,
- // the result is unspecified. We'll make things a bit more robust by explicitly checking for this and handling it.
- char* pDataOut8 = (char*)pDataOut;
- drfs_result result = drfs_success;
-
- size_t totalBytesRead = 0;
- while (bytesToRead > 0)
- {
- ssize_t bytesRead = read(DRFS_HANDLE_TO_FD(file), pDataOut8 + totalBytesRead, (bytesToRead > SSIZE_MAX) ? SSIZE_MAX : bytesToRead);
- if (bytesRead == -1) {
- result = drfs__errno_to_result();
- break;
- }
-
- if (bytesRead == 0) {
- break; // Reached the end.
- }
-
- totalBytesRead += bytesRead;
- bytesToRead -= bytesRead;
- }
-
- if (pBytesReadOut) {
- *pBytesReadOut = totalBytesRead;
- }
-
- return result;
-}
-
-static drfs_result drfs_write_native_file(drfs_handle file, const void* pData, size_t bytesToWrite, size_t* pBytesWrittenOut)
-{
- // We want to handle writes in the same way as we do reads due to the return valid being signed.
- const char* pDataIn8 = (const char*)pData;
- drfs_result result = drfs_success;
-
- size_t totalBytesWritten = 0;
- while (bytesToWrite > 0)
- {
- ssize_t bytesWritten = write(DRFS_HANDLE_TO_FD(file), pDataIn8 + totalBytesWritten, (bytesToWrite > SSIZE_MAX) ? SSIZE_MAX : bytesToWrite);
- if (bytesWritten == -1) {
- result = drfs__errno_to_result();
- break;
- }
-
- if (bytesWritten == 0) {
- result = drfs__errno_to_result();
- break;
- }
-
- totalBytesWritten += bytesWritten;
- bytesToWrite -= bytesWritten;
- }
-
- if (pBytesWrittenOut != NULL) {
- *pBytesWrittenOut = totalBytesWritten;
- }
-
- return result;
-}
-
-static drfs_result drfs_seek_native_file(drfs_handle file, dr_int64 bytesToSeek, drfs_seek_origin origin)
-{
- int stdioOrigin = SEEK_CUR;
- if (origin == drfs_origin_start) {
- stdioOrigin = SEEK_SET;
- } else if (origin == drfs_origin_end) {
- stdioOrigin = SEEK_END;
- }
-
- if (lseek64(DRFS_HANDLE_TO_FD(file), bytesToSeek, stdioOrigin) == -1) {
- return drfs__errno_to_result();
- }
-
- return drfs_success;
-}
-
-static dr_uint64 drfs_tell_native_file(drfs_handle file)
-{
- return lseek64(DRFS_HANDLE_TO_FD(file), 0, SEEK_CUR);
-}
-
-static dr_uint64 drfs_get_native_file_size(drfs_handle file)
-{
- struct stat64 info;
- if (drfs__fstat64(DRFS_HANDLE_TO_FD(file), &info) != 0) {
- return 0;
- }
-
- return info.st_size;
-}
-
-static void drfs_flush_native_file(drfs_handle file)
-{
- // The posix implementation does not require flushing.
- (void)file;
-}
-
-static drfs_result drfs_get_native_file_info(const char* absolutePath, drfs_file_info* fi)
-{
- assert(absolutePath != NULL);
-
- struct stat64 info;
- if (stat64(absolutePath, &info) != 0) {
- return drfs__errno_to_result();
- }
-
- // is allowed to be null, in which case the call is equivalent to simply checking if the file exists.
- if (fi != NULL)
- {
- drfs__strcpy_s(fi->absolutePath, sizeof(fi->absolutePath), absolutePath);
- fi->sizeInBytes = info.st_size;
- fi->lastModifiedTime = info.st_mtime;
-
- fi->attributes = 0;
- if (drfs__mode_is_dir(info.st_mode)) {
- fi->attributes |= DRFS_FILE_ATTRIBUTE_DIRECTORY;
- }
- if (drfs__mode_has_write_permission(info.st_mode) == DR_FALSE) {
- fi->attributes |= DRFS_FILE_ATTRIBUTE_READONLY;
- }
- }
-
- return drfs_success;
-}
-
-typedef struct
-{
- DIR* dir;
- char directoryPath[1];
-} drfs_iterator_posix;
-
-static drfs_handle drfs_begin_native_iteration(const char* absolutePath)
-{
- DIR* dir = opendir(absolutePath);
- if (dir == NULL) {
- return NULL;
- }
-
- drfs_iterator_posix* pIterator = (drfs_iterator_posix*)malloc(sizeof(drfs_iterator_posix) + strlen(absolutePath));
- if (pIterator == NULL) {
- return NULL;
- }
-
- pIterator->dir = dir;
- drfs__strcpy_s(pIterator->directoryPath, strlen(absolutePath) + 1, absolutePath);
-
- return pIterator;
-}
-
-static void drfs_end_native_iteration(drfs_handle iterator)
-{
- drfs_iterator_posix* pIterator = (drfs_iterator_posix*)iterator;
- if (pIterator == NULL) {
- return;
- }
-
- closedir(pIterator->dir);
- free(pIterator);
-}
-
-static dr_bool32 drfs_next_native_iteration(drfs_handle iterator, drfs_file_info* fi)
-{
- drfs_iterator_posix* pIterator = (drfs_iterator_posix*)iterator;
- if (pIterator == NULL || pIterator->dir == NULL) {
- return DR_FALSE;
- }
-
- struct dirent* info = readdir(pIterator->dir);
- if (info == NULL) {
- return DR_FALSE;
- }
-
- // Skip over "." and ".." folders.
- while (strcmp(info->d_name, ".") == 0 || strcmp(info->d_name, "..") == 0) {
- info = readdir(pIterator->dir);
- if (info == NULL) {
- return DR_FALSE;
- }
- }
-
- char fileAbsolutePath[DRFS_MAX_PATH];
- drfs_drpath_copy_and_append(fileAbsolutePath, sizeof(fileAbsolutePath), pIterator->directoryPath, info->d_name);
-
- if (drfs_get_native_file_info(fileAbsolutePath, fi)) {
- drfs__strcpy_s(fi->absolutePath, sizeof(fi->absolutePath), info->d_name);
- }
-
- return DR_TRUE;
-}
-#endif //DR_FS_USE_STDIO
-
-
-static dr_bool32 drfs_mkdir_recursive_native(const char* absolutePath)
-{
- char runningPath[DRFS_MAX_PATH];
- runningPath[0] = '\0';
-
- drfs_drpath_iterator iPathSeg;
- if (!drfs_drpath_first(absolutePath, &iPathSeg)) {
- return DR_FALSE;
- }
-
- // Never check the first segment because we can assume it always exists - it will always be the drive root.
- if (drfs_drpath_append_iterator(runningPath, sizeof(runningPath), iPathSeg))
- {
- // Loop over every directory until we find one that does not exist.
- while (drfs_drpath_next(&iPathSeg))
- {
- if (!drfs_drpath_append_iterator(runningPath, sizeof(runningPath), iPathSeg)) {
- return DR_FALSE;
- }
-
- if (!drfs_is_native_directory(runningPath)) {
- if (drfs_mkdir_native(runningPath) != drfs_success) {
- return DR_FALSE;
- }
-
- break;
- }
- }
-
-
- // At this point all we need to do is create the remaining directories - we can assert that the directory does not exist
- // rather than actually checking it which should be a bit more efficient.
- while (drfs_drpath_next(&iPathSeg))
- {
- if (!drfs_drpath_append_iterator(runningPath, sizeof(runningPath), iPathSeg)) {
- return DR_FALSE;
- }
-
- assert(!drfs_is_native_directory(runningPath));
-
- if (drfs_mkdir_native(runningPath) != drfs_success) {
- return DR_FALSE;
- }
- }
-
- return DR_TRUE;
- }
-
- return DR_FALSE;
-}
-
-
-
-//// Native Archive Implementation ////
-
-typedef struct
-{
- // The access mode.
- unsigned int accessMode;
-
- // The absolute path of the directory.
- char absolutePath[1];
-
-} drfs_archive_native;
-
-static drfs_result drfs_open_archive__native(drfs_file* pArchiveFile, unsigned int accessMode, drfs_handle* pHandleOut)
-{
- (void)pArchiveFile;
- (void)accessMode;
-
- // This function should never be called for native archives. Native archives are a special case because they are just directories
- // on the file system rather than actual files, and as such they are handled just slightly differently. This function is only
- // included here for this assert so we can ensure we don't incorrectly call this function.
- assert(DR_FALSE);
-
- *pHandleOut = NULL;
- return drfs_unknown_error;
-}
-
-static drfs_result drfs_open_archive__native__special(const char* absolutePath, unsigned int accessMode, drfs_handle* pHandleOut)
-{
- assert(absolutePath != NULL); // There is no notion of a file for native archives (which are just directories on the file system).
- assert(pHandleOut != NULL);
-
- *pHandleOut = NULL;
-
- size_t absolutePathLen = strlen(absolutePath);
-
- drfs_archive_native* pNativeArchive = (drfs_archive_native*)malloc(sizeof(*pNativeArchive) + absolutePathLen + 1);
- if (pNativeArchive == NULL) {
- return drfs_out_of_memory;
- }
-
- drfs__strcpy_s(pNativeArchive->absolutePath, absolutePathLen + 1, absolutePath);
- pNativeArchive->accessMode = accessMode;
-
- *pHandleOut = (drfs_handle)pNativeArchive;
- return drfs_success;
-}
-
-static void drfs_close_archive__native(drfs_handle archive)
-{
- // Nothing to do except free the object.
- free(archive);
-}
-
-static drfs_result drfs_get_file_info__native(drfs_handle archive, const char* relativePath, drfs_file_info* fi)
-{
- drfs_archive_native* pNativeArchive = (drfs_archive_native*)archive;
- assert(pNativeArchive != NULL);
-
- char absolutePath[DRFS_MAX_PATH];
- if (!drfs_drpath_copy_and_append(absolutePath, sizeof(absolutePath), pNativeArchive->absolutePath, relativePath)) {
- return drfs_path_too_long;
- }
-
- if (fi != NULL) {
- memset(fi, 0, sizeof(*fi));
- }
-
- return drfs_get_native_file_info(absolutePath, fi);
-}
-
-static drfs_handle drfs_begin_iteration__native(drfs_handle archive, const char* relativePath)
-{
- drfs_archive_native* pNativeArchive = (drfs_archive_native*)archive;
- assert(pNativeArchive != NULL);
-
- char absolutePath[DRFS_MAX_PATH];
- if (!drfs_drpath_copy_and_append(absolutePath, sizeof(absolutePath), pNativeArchive->absolutePath, relativePath)) {
- return NULL;
- }
-
- return drfs_begin_native_iteration(absolutePath);
-}
-
-static void drfs_end_iteration__native(drfs_handle archive, drfs_handle iterator)
-{
- (void)archive;
- assert(archive != NULL);
- assert(iterator != NULL);
-
- drfs_end_native_iteration(iterator);
-}
-
-static dr_bool32 drfs_next_iteration__native(drfs_handle archive, drfs_handle iterator, drfs_file_info* fi)
-{
- (void)archive;
- assert(archive != NULL);
- assert(iterator != NULL);
-
- return drfs_next_native_iteration(iterator, fi);
-}
-
-static drfs_result drfs_delete_file__native(drfs_handle archive, const char* relativePath)
-{
- assert(relativePath != NULL);
-
- drfs_archive_native* pNativeArchive = (drfs_archive_native*)archive;
- assert(pNativeArchive != NULL);
-
- char absolutePath[DRFS_MAX_PATH];
- if (!drfs_drpath_copy_and_append(absolutePath, sizeof(absolutePath), pNativeArchive->absolutePath, relativePath)) {
- return drfs_path_too_long;
- }
-
- return drfs_delete_native_file(absolutePath);
-}
-
-static drfs_result drfs_move_file__native(drfs_handle archive, const char* relativePathOld, const char* relativePathNew)
-{
- assert(relativePathOld != NULL);
- assert(relativePathNew != NULL);
-
- drfs_archive_native* pNativeArchive = (drfs_archive_native*)archive;
- assert(pNativeArchive != NULL);
-
- char absolutePathOld[DRFS_MAX_PATH];
- if (!drfs_drpath_copy_and_append(absolutePathOld, sizeof(absolutePathOld), pNativeArchive->absolutePath, relativePathOld)) {
- return drfs_path_too_long;
- }
-
- char absolutePathNew[DRFS_MAX_PATH];
- if (!drfs_drpath_copy_and_append(absolutePathNew, sizeof(absolutePathNew), pNativeArchive->absolutePath, relativePathNew)) {
- return drfs_path_too_long;
- }
-
- return drfs_move_native_file(absolutePathOld, absolutePathNew);
-}
-
-static drfs_result drfs_create_directory__native(drfs_handle archive, const char* relativePath)
-{
- assert(relativePath != NULL);
-
- drfs_archive_native* pNativeArchive = (drfs_archive_native*)archive;
- assert(pNativeArchive != NULL);
-
- char absolutePath[DRFS_MAX_PATH];
- if (!drfs_drpath_copy_and_append(absolutePath, sizeof(absolutePath), pNativeArchive->absolutePath, relativePath)) {
- return drfs_path_too_long;
- }
-
- return drfs_mkdir_native(absolutePath);
-}
-
-static drfs_result drfs_copy_file__native(drfs_handle archive, const char* relativePathSrc, const char* relativePathDst, dr_bool32 failIfExists)
-{
- assert(relativePathSrc != NULL);
- assert(relativePathDst != NULL);
-
- drfs_archive_native* pNativeArchive = (drfs_archive_native*)archive;
- assert(pNativeArchive != NULL);
-
- char absolutePathSrc[DRFS_MAX_PATH];
- if (!drfs_drpath_copy_and_append(absolutePathSrc, sizeof(absolutePathSrc), pNativeArchive->absolutePath, relativePathSrc)) {
- return drfs_path_too_long;
- }
-
- char absolutePathDst[DRFS_MAX_PATH];
- if (!drfs_drpath_copy_and_append(absolutePathDst, sizeof(absolutePathDst), pNativeArchive->absolutePath, relativePathDst)) {
- return drfs_path_too_long;
- }
-
- return drfs_copy_native_file(absolutePathSrc, absolutePathDst, failIfExists);
-}
-
-static drfs_result drfs_open_file__native(drfs_handle archive, const char* relativePath, unsigned int accessMode, drfs_handle* pHandleOut)
-{
- assert(archive != NULL);
- assert(relativePath != NULL);
-
- drfs_archive_native* pNativeArchive = (drfs_archive_native*)archive;
- assert(pNativeArchive != NULL);
-
- char absolutePath[DRFS_MAX_PATH];
- if (drfs_drpath_copy_and_append(absolutePath, sizeof(absolutePath), pNativeArchive->absolutePath, relativePath)) {
- return drfs_open_native_file(absolutePath, accessMode, pHandleOut);
- }
-
- return drfs_path_too_long;
-}
-
-static void drfs_close_file__native(drfs_handle archive, drfs_handle file)
-{
- (void)archive;
- assert(archive != NULL);
- assert(file != NULL);
-
- drfs_close_native_file(file);
-}
-
-static drfs_result drfs_read_file__native(drfs_handle archive, drfs_handle file, void* pDataOut, size_t bytesToRead, size_t* pBytesReadOut)
-{
- (void)archive;
- assert(archive != NULL);
- assert(file != NULL);
-
- return drfs_read_native_file(file, pDataOut, bytesToRead, pBytesReadOut);
-}
-
-static drfs_result drfs_write_file__native(drfs_handle archive, drfs_handle file, const void* pData, size_t bytesToWrite, size_t* pBytesWrittenOut)
-{
- (void)archive;
- assert(archive != NULL);
- assert(file != NULL);
-
- return drfs_write_native_file(file, pData, bytesToWrite, pBytesWrittenOut);
-}
-
-static drfs_result drfs_seek_file__native(drfs_handle archive, drfs_handle file, dr_int64 bytesToSeek, drfs_seek_origin origin)
-{
- (void)archive;
- assert(archive != NULL);
- assert(file != NULL);
-
- return drfs_seek_native_file(file, bytesToSeek, origin);
-}
-
-static dr_uint64 drfs_tell_file__native(drfs_handle archive, drfs_handle file)
-{
- (void)archive;
- assert(archive != NULL);
- assert(file != NULL);
-
- return drfs_tell_native_file(file);
-}
-
-static dr_uint64 drfs_file_size__native(drfs_handle archive, drfs_handle file)
-{
- (void)archive;
- assert(archive != NULL);
- assert(file != NULL);
-
- return drfs_get_native_file_size(file);
-}
-
-static void drfs_flush__native(drfs_handle archive, drfs_handle file)
-{
- (void)archive;
- assert(archive != NULL);
- assert(file != NULL);
-
- drfs_flush_native_file(file);
-}
-
-
-// Finds the back-end callbacks by the given extension.
-static dr_bool32 drfs_find_backend_by_extension(drfs_context* pContext, const char* extension, drfs_archive_callbacks* pCallbacksOut)
-{
- if (pContext == NULL || extension == NULL || extension[0] == '\0') {
- return DR_FALSE;
- }
-
- for (unsigned int i = 0; i < pContext->archiveCallbacks.count; ++i)
- {
- if (pContext->archiveCallbacks.pBuffer[i].is_valid_extension) {
- if (pContext->archiveCallbacks.pBuffer[i].is_valid_extension(extension)) {
- if (pCallbacksOut) {
- *pCallbacksOut = pContext->archiveCallbacks.pBuffer[i];
- }
- return DR_TRUE;
- }
- }
- }
-
- return DR_FALSE;
-}
-
-// Recursively claims ownership of parent archives so that when the child archive is deleted, so are it's parents.
-static void drfs_recursively_claim_ownership_or_parent_archive(drfs_archive* pArchive)
-{
- if (pArchive == NULL) {
- return;
- }
-
- pArchive->flags |= DR_FS_OWNS_PARENT_ARCHIVE;
- drfs_recursively_claim_ownership_or_parent_archive(pArchive->pParentArchive);
-}
-
-// Opens a native archive.
-static drfs_result drfs_open_native_archive(drfs_context* pContext, const char* absolutePath, unsigned int accessMode, drfs_archive** ppArchive)
-{
- assert(pContext != NULL);
- assert(ppArchive != NULL);
- assert(absolutePath != NULL);
-
- *ppArchive = NULL;
-
- drfs_handle internalArchiveHandle;
- drfs_result result = drfs_open_archive__native__special(absolutePath, accessMode, &internalArchiveHandle);
- if (result != drfs_success) {
- return result;
- }
-
- drfs_archive* pArchive = (drfs_archive*)malloc(sizeof(*pArchive));
- if (pArchive == NULL) {
- drfs_close_archive__native(internalArchiveHandle);
- return drfs_out_of_memory;
- }
-
- pArchive->pContext = pContext;
- pArchive->pParentArchive = NULL;
- pArchive->pFile = NULL;
- pArchive->internalArchiveHandle = internalArchiveHandle;
- pArchive->flags = 0;
- pArchive->callbacks.is_valid_extension = NULL;
- pArchive->callbacks.open_archive = drfs_open_archive__native;
- pArchive->callbacks.close_archive = drfs_close_archive__native;
- pArchive->callbacks.get_file_info = drfs_get_file_info__native;
- pArchive->callbacks.begin_iteration = drfs_begin_iteration__native;
- pArchive->callbacks.end_iteration = drfs_end_iteration__native;
- pArchive->callbacks.next_iteration = drfs_next_iteration__native;
- pArchive->callbacks.delete_file = drfs_delete_file__native;
- pArchive->callbacks.create_directory = drfs_create_directory__native;
- pArchive->callbacks.move_file = drfs_move_file__native;
- pArchive->callbacks.copy_file = drfs_copy_file__native;
- pArchive->callbacks.open_file = drfs_open_file__native;
- pArchive->callbacks.close_file = drfs_close_file__native;
- pArchive->callbacks.read_file = drfs_read_file__native;
- pArchive->callbacks.write_file = drfs_write_file__native;
- pArchive->callbacks.seek_file = drfs_seek_file__native;
- pArchive->callbacks.tell_file = drfs_tell_file__native;
- pArchive->callbacks.file_size = drfs_file_size__native;
- pArchive->callbacks.flush_file = drfs_flush__native;
- drfs__strcpy_s(pArchive->absolutePath, sizeof(pArchive->absolutePath), absolutePath);
-
- *ppArchive = pArchive;
- return drfs_success;
-}
-
-// Opens an archive from a file and callbacks.
-static drfs_result drfs_open_non_native_archive(drfs_archive* pParentArchive, drfs_file* pArchiveFile, drfs_archive_callbacks* pBackEndCallbacks, const char* relativePath, unsigned int accessMode, drfs_archive** ppArchiveOut)
-{
- assert(pParentArchive != NULL);
- assert(pArchiveFile != NULL);
- assert(pBackEndCallbacks != NULL);
- assert(ppArchiveOut != NULL);
-
- *ppArchiveOut = NULL;
-
- if (pBackEndCallbacks->open_archive == NULL) {
- return drfs_no_backend;
- }
-
- drfs_handle internalArchiveHandle;
- drfs_result result = pBackEndCallbacks->open_archive(pArchiveFile, accessMode, &internalArchiveHandle);
- if (result != drfs_success) {
- return result;
- }
-
- drfs_archive* pArchive = (drfs_archive*)malloc(sizeof(*pArchive));
- if (pArchive == NULL) {
- pBackEndCallbacks->close_archive(internalArchiveHandle);
- return drfs_out_of_memory;
- }
-
- pArchive->pContext = pParentArchive->pContext;
- pArchive->pParentArchive = pParentArchive;
- pArchive->pFile = pArchiveFile;
- pArchive->internalArchiveHandle = internalArchiveHandle;
- pArchive->flags = 0;
- pArchive->callbacks = *pBackEndCallbacks;
- drfs_drpath_copy_and_append(pArchive->absolutePath, sizeof(pArchive->absolutePath), pParentArchive->absolutePath, relativePath);
-
- *ppArchiveOut = pArchive;
- return drfs_success;
-}
-
-// Attempts to open an archive from another archive.
-static drfs_result drfs_open_non_native_archive_from_path(drfs_archive* pParentArchive, const char* relativePath, unsigned int accessMode, drfs_archive** ppArchiveOut)
-{
- assert(pParentArchive != NULL);
- assert(ppArchiveOut != NULL);
- assert(relativePath != NULL);
-
- *ppArchiveOut = NULL;
-
- drfs_archive_callbacks backendCallbacks;
- if (!drfs_find_backend_by_extension(pParentArchive->pContext, drfs_drpath_extension(relativePath), &backendCallbacks)) {
- return drfs_no_backend;
- }
-
- drfs_file* pArchiveFile;
- drfs_result result = drfs_open_file_from_archive(pParentArchive, relativePath, accessMode, &pArchiveFile);
- if (result != drfs_success) {
- return result;
- }
-
- drfs_archive* pArchive;
- result = drfs_open_non_native_archive(pParentArchive, pArchiveFile, &backendCallbacks, relativePath, accessMode, &pArchive);
- if (pArchive == NULL) {
- return result;
- }
-
- *ppArchiveOut = pArchive;
- return drfs_success;
-}
-
-
-// Recursively opens the archive that owns the file at the given verbose path.
-static drfs_result drfs_open_owner_archive_recursively_from_verbose_path(drfs_archive* pParentArchive, const char* relativePath, unsigned int accessMode, char* relativePathOut, size_t relativePathOutSize, drfs_archive** ppArchiveOut)
-{
- assert(pParentArchive != NULL);
- assert(relativePath != NULL);
- assert(ppArchiveOut != NULL);
-
- *ppArchiveOut = NULL;
-
- if (pParentArchive->callbacks.get_file_info == NULL) {
- return drfs_no_backend;
- }
-
- drfs_file_info fi;
- if (pParentArchive->callbacks.get_file_info(pParentArchive->internalArchiveHandle, relativePath, &fi) == drfs_success)
- {
- // The file is in this archive.
- drfs__strcpy_s(relativePathOut, relativePathOutSize, relativePath);
- *ppArchiveOut = pParentArchive;
- return drfs_success;
- }
- else
- {
- char runningPath[DRFS_MAX_PATH];
- runningPath[0] = '\0';
-
- drfs_drpath_iterator segment;
- if (drfs_drpath_first(relativePath, &segment))
- {
- do
- {
- if (!drfs_drpath_append_iterator(runningPath, sizeof(runningPath), segment)) {
- return drfs_path_too_long;
- }
-
- if (pParentArchive->callbacks.get_file_info(pParentArchive->internalArchiveHandle, runningPath, &fi) == drfs_success)
- {
- if ((fi.attributes & DRFS_FILE_ATTRIBUTE_DIRECTORY) == 0)
- {
- // The running path points to an actual file. It could be a sub-archive.
- drfs_archive_callbacks backendCallbacks;
- if (drfs_find_backend_by_extension(pParentArchive->pContext, drfs_drpath_extension(runningPath), &backendCallbacks))
- {
- drfs_file* pNextArchiveFile;
- drfs_open_file_from_archive(pParentArchive, runningPath, accessMode, &pNextArchiveFile);
- if (pNextArchiveFile == NULL) {
- break; // Failed to open the archive file.
- }
-
- drfs_archive* pNextArchive;
- drfs_open_non_native_archive(pParentArchive, pNextArchiveFile, &backendCallbacks, runningPath, accessMode, &pNextArchive);
- if (pNextArchive == NULL) {
- drfs_close(pNextArchiveFile);
- break;
- }
-
- // At this point we should have an archive. We now need to call this function recursively if there are any segments left.
- drfs_drpath_iterator nextsegment = segment;
- if (drfs_drpath_next(&nextsegment))
- {
- drfs_archive* pOwnerArchive;
- drfs_open_owner_archive_recursively_from_verbose_path(pNextArchive, nextsegment.path + nextsegment.segment.offset, accessMode, relativePathOut, relativePathOutSize, &pOwnerArchive);
- if (pOwnerArchive == NULL) {
- drfs_close_archive(pNextArchive);
- break;
- }
-
- *ppArchiveOut = pOwnerArchive;
- return drfs_success;
- }
- else
- {
- // We reached the end of the path. If we get here it means the file doesn't exist, because otherwise we would have caught it in the check right at the top.
- drfs_close_archive(pNextArchive);
- break;
- }
- }
- }
- }
- } while (drfs_drpath_next(&segment));
- }
-
- // The running path is not part of this archive.
- // NOTE: Is this the correct return value we should be using? Should we be returning an error and setting the output archive to NULL?
- drfs__strcpy_s(relativePathOut, relativePathOutSize, relativePath);
- *ppArchiveOut = pParentArchive;
- return drfs_does_not_exist;
- }
-}
-
-// Opens the archive that owns the file at the given verbose path.
-static drfs_result drfs_open_owner_archive_from_absolute_path(drfs_context* pContext, const char* absolutePath, unsigned int accessMode, char* relativePathOut, size_t relativePathOutSize, drfs_archive** ppArchiveOut)
-{
- assert(pContext != NULL);
- assert(absolutePath != NULL);
- assert(ppArchiveOut != NULL);
-
- *ppArchiveOut = NULL;
-
- // We are currently assuming absolute path's are verbose. This means we don't need to do any searching on the file system. We just
- // move through the path and look for a segment with an extension matching one of the registered back-ends.
-
- char runningPath[DRFS_MAX_PATH];
- runningPath[0] = '\0';
-
- if (absolutePath[0] == '/') {
- runningPath[0] = '/';
- runningPath[1] = '\0';
- }
-
- drfs_drpath_iterator segment;
- if (drfs_drpath_first(absolutePath, &segment))
- {
- do
- {
- if (!drfs_drpath_append_iterator(runningPath, sizeof(runningPath), segment)) {
- return drfs_path_too_long;
- }
-
- if (!drfs_is_native_directory(runningPath))
- {
- char dirAbsolutePath[DRFS_MAX_PATH];
- drfs_drpath_copy_base_path(runningPath, dirAbsolutePath, sizeof(dirAbsolutePath));
-
- drfs_archive* pNativeArchive;
- drfs_result result = drfs_open_native_archive(pContext, dirAbsolutePath, accessMode, &pNativeArchive);
- if (result != drfs_success) {
- return result; // Failed to open the native archive.
- }
-
-
- // The running path is not a native directory. It could be an archive file.
- drfs_archive_callbacks backendCallbacks;
- if (drfs_find_backend_by_extension(pContext, drfs_drpath_extension(runningPath), &backendCallbacks))
- {
- // The running path refers to an archive file. We need to try opening the archive here. If this fails, we
- // need to return NULL.
- drfs_archive* pArchive;
- result = drfs_open_owner_archive_recursively_from_verbose_path(pNativeArchive, segment.path + segment.segment.offset, accessMode, relativePathOut, relativePathOutSize, &pArchive);
- if (pArchive == NULL) {
- drfs_close_archive(pNativeArchive);
- return result;
- }
-
- *ppArchiveOut = pArchive;
- return drfs_success;
- }
- else
- {
- drfs__strcpy_s(relativePathOut, relativePathOutSize, segment.path + segment.segment.offset);
- *ppArchiveOut = pNativeArchive;
- return drfs_success;
- }
- }
-
- } while (drfs_drpath_next(&segment));
- }
-
- return drfs_does_not_exist;
-}
-
-// Recursively opens the archive that owns the file specified by the given relative path.
-static drfs_result drfs_open_owner_archive_recursively_from_relative_path(drfs_archive* pParentArchive, const char* rootSearchPath, const char* relativePath, unsigned int accessMode, char* relativePathOut, size_t relativePathOutSize, drfs_archive** ppArchiveOut)
-{
- assert(pParentArchive != NULL);
- assert(relativePath != NULL);
- assert(ppArchiveOut != NULL);
-
- *ppArchiveOut = NULL;
-
- if (pParentArchive->callbacks.get_file_info == NULL) {
- return drfs_no_backend;
- }
-
- // Always try the direct route by checking if the file exists within the archive first.
- drfs_file_info fi;
- if (pParentArchive->callbacks.get_file_info(pParentArchive->internalArchiveHandle, relativePath, &fi) == drfs_success)
- {
- // The file is in this archive.
- drfs__strcpy_s(relativePathOut, relativePathOutSize, relativePath);
- *ppArchiveOut = pParentArchive;
- return drfs_success;
- }
- else
- {
- // The file does not exist within this archive. We need to search the parent archive recursively. We never search above
- // the path specified by rootSearchPath.
- char runningPath[DRFS_MAX_PATH];
- drfs__strcpy_s(runningPath, sizeof(runningPath), rootSearchPath);
-
- // Part of rootSearchPath and relativePath will be overlapping. We want to begin searching at the non-overlapping section.
- drfs_drpath_iterator pathSeg0;
- drfs_drpath_iterator pathSeg1;
- dr_bool32 isSeg0Valid = drfs_drpath_first(rootSearchPath, &pathSeg0);
- dr_bool32 isSeg1Valid = drfs_drpath_first(relativePath, &pathSeg1);
- while (isSeg0Valid && isSeg1Valid) {
- isSeg0Valid = drfs_drpath_next(&pathSeg0);
- isSeg1Valid = drfs_drpath_next(&pathSeg1);
- }
-
- relativePath = pathSeg1.path + pathSeg1.segment.offset;
-
- // Searching works as such:
- // For each segment in "relativePath"
- // If segment is archive then
- // Open and search archive
- // Else
- // Search each archive file in this directory
- // Endif
- drfs_drpath_iterator pathseg;
- if (drfs_drpath_first(relativePath, &pathseg))
- {
- do
- {
- char runningPathBase[DRFS_MAX_PATH];
- drfs__strcpy_s(runningPathBase, sizeof(runningPathBase), runningPath);
-
- if (!drfs_drpath_append_iterator(runningPath, sizeof(runningPath), pathseg)) {
- return drfs_path_too_long;
- }
-
- drfs_archive* pNextArchive;
- drfs_open_non_native_archive_from_path(pParentArchive, runningPath, accessMode, &pNextArchive);
- if (pNextArchive != NULL)
- {
- // It's an archive segment. We need to check this archive recursively, starting from the next segment.
- drfs_drpath_iterator nextseg = pathseg;
- if (!drfs_drpath_next(&nextseg)) {
- // Should never actually get here, but if we do it means we've reached the end of the path. Assume the file could not be found.
- drfs_close_archive(pNextArchive);
- return drfs_unknown_error;
- }
-
- drfs_archive* pOwnerArchive;
- drfs_open_owner_archive_recursively_from_relative_path(pNextArchive, "", nextseg.path + nextseg.segment.offset, accessMode, relativePathOut, relativePathOutSize, &pOwnerArchive);
- if (pOwnerArchive == NULL) {
- drfs_close_archive(pNextArchive);
- return drfs_does_not_exist;
- }
-
- *ppArchiveOut = pOwnerArchive;
- return drfs_success;
- }
- else
- {
- // It's not an archive segment. We need to search.
- if (pParentArchive->callbacks.begin_iteration == NULL || pParentArchive->callbacks.next_iteration == NULL || pParentArchive->callbacks.end_iteration == NULL) {
- return drfs_no_backend;
- }
-
- drfs_handle iterator = pParentArchive->callbacks.begin_iteration(pParentArchive->internalArchiveHandle, runningPathBase);
- if (iterator == NULL) {
- return drfs_no_backend;
- }
-
- while (pParentArchive->callbacks.next_iteration(pParentArchive->internalArchiveHandle, iterator, &fi))
- {
- if ((fi.attributes & DRFS_FILE_ATTRIBUTE_DIRECTORY) == 0)
- {
- // It's a file which means it could be an archive. Note that fi.absolutePath is actually relative to the parent archive.
- drfs_open_non_native_archive_from_path(pParentArchive, fi.absolutePath, accessMode, &pNextArchive);
- if (pNextArchive != NULL)
- {
- // It's an archive, so check it.
- drfs_archive* pOwnerArchive;
- drfs_open_owner_archive_recursively_from_relative_path(pNextArchive, "", pathseg.path + pathseg.segment.offset, accessMode, relativePathOut, relativePathOutSize, &pOwnerArchive);
- if (pOwnerArchive != NULL) {
- pParentArchive->callbacks.end_iteration(pParentArchive->internalArchiveHandle, iterator);
- *ppArchiveOut = pOwnerArchive;
- return drfs_success;
- } else {
- drfs_close_archive(pNextArchive);
- }
- }
- }
- }
-
- pParentArchive->callbacks.end_iteration(pParentArchive->internalArchiveHandle, iterator);
- }
-
- } while (drfs_drpath_next(&pathseg));
- }
- }
-
- return drfs_does_not_exist;
-}
-
-// Opens the archive that owns the file at the given path that's relative to the given base path. This supports non-verbose paths and will search
-// the file system for the archive.
-static drfs_result drfs_open_owner_archive_from_relative_path(drfs_context* pContext, const char* absoluteBasePath, const char* relativePath, unsigned int accessMode, char* relativePathOut, size_t relativePathOutSize, drfs_archive** ppArchiveOut)
-{
- assert(pContext != NULL);
- assert(absoluteBasePath != NULL);
- assert(relativePath != NULL);
- assert(drfs_drpath_is_absolute(absoluteBasePath));
- assert(ppArchiveOut != NULL);
-
- *ppArchiveOut = NULL;
-
- char adjustedRelativePath[DRFS_MAX_PATH];
- char relativeBasePath[DRFS_MAX_PATH];
- relativeBasePath[0] = '\0';
-
- // The base path should be absolute and verbose. It does not need to be an actual directory.
- drfs_archive* pBaseArchive = NULL;
- if (drfs_is_native_directory(absoluteBasePath))
- {
- drfs_result result = drfs_open_native_archive(pContext, absoluteBasePath, accessMode, &pBaseArchive);
- if (result != drfs_success) {
- return result;
- }
-
- if (drfs__strcpy_s(adjustedRelativePath, sizeof(adjustedRelativePath), relativePath) != 0) {
- drfs_close_archive(pBaseArchive);
- return drfs_path_too_long;
- }
- }
- else
- {
- drfs_result result = drfs_open_owner_archive(pContext, absoluteBasePath, accessMode, relativeBasePath, sizeof(relativeBasePath), &pBaseArchive);
- if (result != drfs_success) {
- return result;
- }
-
- if (!drfs_drpath_copy_and_append(adjustedRelativePath, sizeof(adjustedRelativePath), relativeBasePath, relativePath)) {
- drfs_close_archive(pBaseArchive);
- return drfs_path_too_long;
- }
- }
-
-
- // We couldn't open the archive file from here, so we'll need to search for the owner archive recursively.
- drfs_archive* pOwnerArchive;
- drfs_result result = drfs_open_owner_archive_recursively_from_relative_path(pBaseArchive, relativeBasePath, adjustedRelativePath, accessMode, relativePathOut, relativePathOutSize, &pOwnerArchive);
- if (pOwnerArchive == NULL) {
- return result;
- }
-
- drfs_recursively_claim_ownership_or_parent_archive(pOwnerArchive);
-
- *ppArchiveOut = pOwnerArchive;
- return drfs_success;
-}
-
-// Opens an archive from a path that's relative to the given base path. This supports non-verbose paths and will search
-// the file system for the archive.
-static drfs_result drfs_open_archive_from_relative_path(drfs_context* pContext, const char* absoluteBasePath, const char* relativePath, unsigned int accessMode, drfs_archive** ppArchiveOut)
-{
- assert(pContext != NULL);
- assert(absoluteBasePath != NULL);
- assert(relativePath != NULL);
- assert(drfs_drpath_is_absolute(absoluteBasePath));
- assert(ppArchiveOut != NULL);
-
- *ppArchiveOut = NULL;
-
- char adjustedRelativePath[DRFS_MAX_PATH];
- char relativeBasePath[DRFS_MAX_PATH];
- relativeBasePath[0] = '\0';
-
- // The base path should be absolute and verbose. It does not need to be an actual directory.
- drfs_archive* pBaseArchive = NULL;
- if (drfs_is_native_directory(absoluteBasePath))
- {
- drfs_result result = drfs_open_native_archive(pContext, absoluteBasePath, accessMode, &pBaseArchive);
- if (result != drfs_success) {
- return result;
- }
-
- if (drfs__strcpy_s(adjustedRelativePath, sizeof(adjustedRelativePath), relativePath) != 0) {
- drfs_close_archive(pBaseArchive);
- return drfs_path_too_long;
- }
- }
- else
- {
- drfs_result result = drfs_open_owner_archive(pContext, absoluteBasePath, accessMode, relativeBasePath, sizeof(relativeBasePath), &pBaseArchive);
- if (result != drfs_success) {
- return result;
- }
-
- if (!drfs_drpath_copy_and_append(adjustedRelativePath, sizeof(adjustedRelativePath), relativeBasePath, relativePath)) {
- drfs_close_archive(pBaseArchive);
- return drfs_path_too_long;
- }
- }
-
-
- // We can try opening the file directly from the base path. If this doesn't work, we can try recursively opening the owner archive
- // an open from there.
- drfs_archive* pArchive;
- drfs_result result = drfs_open_non_native_archive_from_path(pBaseArchive, adjustedRelativePath, accessMode, &pArchive);
- if (pArchive == NULL)
- {
- // We couldn't open the archive file from here, so we'll need to search for the owner archive recursively.
- char archiveRelativePath[DRFS_MAX_PATH];
- drfs_archive* pOwnerArchive;
- result = drfs_open_owner_archive_recursively_from_relative_path(pBaseArchive, relativeBasePath, adjustedRelativePath, accessMode, archiveRelativePath, sizeof(archiveRelativePath), &pOwnerArchive);
- if (pOwnerArchive != NULL)
- {
- drfs_recursively_claim_ownership_or_parent_archive(pOwnerArchive);
-
- result = drfs_open_non_native_archive_from_path(pOwnerArchive, archiveRelativePath, accessMode, &pArchive);
- if (pArchive == NULL) {
- drfs_close_archive(pOwnerArchive);
- return result;
- }
- }
- }
-
- if (pArchive == NULL) {
- drfs_close_archive(pBaseArchive);
- return drfs_does_not_exist;
- }
-
- *ppArchiveOut = pArchive;
- return drfs_success;
-}
-
-//// Back-End Registration ////
-
-#ifndef DR_FS_NO_ZIP
-// Registers the archive callbacks which enables support for Zip files.
-static void drfs_register_zip_backend(drfs_context* pContext);
-#endif
-
-#ifndef DR_FS_NO_PAK
-// Registers the archive callbacks which enables support for Quake 2 pak files.
-static void drfs_register_pak_backend(drfs_context* pContext);
-#endif
-
-#ifndef DR_FS_NO_MTL
-// Registers the archive callbacks which enables support for Quake 2 pak files.
-static void drfs_register_mtl_backend(drfs_context* pContext);
-#endif
-
-
-
-//// Public API Implementation ////
-
-drfs_result drfs_init(drfs_context* pContext)
-{
- if (pContext == NULL) return drfs_invalid_args;
- memset(pContext, 0, sizeof(*pContext));
-
- if (!drfs_callbacklist_init(&pContext->archiveCallbacks) || !drfs_basedirs_init(&pContext->baseDirectories)) {
- return drfs_unknown_error;
- }
-
- pContext->isWriteGuardEnabled = 0;
-
-#ifndef DR_FS_NO_ZIP
- drfs_register_zip_backend(pContext);
-#endif
-
-#ifndef DR_FS_NO_PAK
- drfs_register_pak_backend(pContext);
-#endif
-
-#ifndef DR_FS_NO_MTL
- drfs_register_mtl_backend(pContext);
-#endif
-
- return drfs_success;
-}
-
-drfs_result drfs_uninit(drfs_context* pContext)
-{
- if (pContext == NULL) return drfs_invalid_args;
-
- drfs_basedirs_uninit(&pContext->baseDirectories);
- drfs_callbacklist_uninit(&pContext->archiveCallbacks);
- return drfs_success;
-}
-
-
-drfs_context* drfs_create_context()
-{
- drfs_context* pContext = (drfs_context*)malloc(sizeof(*pContext));
- if (pContext == NULL) {
- return NULL;
- }
-
- if (drfs_init(pContext) != drfs_success) {
- free(pContext);
- return NULL;
- }
-
- return pContext;
-}
-
-void drfs_delete_context(drfs_context* pContext)
-{
- if (pContext == NULL) {
- return;
- }
-
- drfs_uninit(pContext);
- free(pContext);
-}
-
-
-void drfs_register_archive_backend(drfs_context* pContext, drfs_archive_callbacks callbacks)
-{
- if (pContext == NULL) {
- return;
- }
-
- drfs_callbacklist_pushback(&pContext->archiveCallbacks, callbacks);
-}
-
-
-void drfs_insert_base_directory(drfs_context* pContext, const char* absolutePath, unsigned int index)
-{
- if (pContext == NULL) {
- return;
- }
-
- drfs_basedirs_insert(&pContext->baseDirectories, absolutePath, index);
-}
-
-void drfs_add_base_directory(drfs_context* pContext, const char* absolutePath)
-{
- if (pContext == NULL) {
- return;
- }
-
- drfs_insert_base_directory(pContext, absolutePath, drfs_get_base_directory_count(pContext));
-}
-
-void drfs_remove_base_directory(drfs_context* pContext, const char* absolutePath)
-{
- if (pContext == NULL) {
- return;
- }
-
- for (unsigned int iPath = 0; iPath < pContext->baseDirectories.count; /*DO NOTHING*/) {
- if (drfs_drpath_equal(pContext->baseDirectories.pBuffer[iPath].absolutePath, absolutePath)) {
- drfs_basedirs_remove(&pContext->baseDirectories, iPath);
- } else {
- ++iPath;
- }
- }
-}
-
-void drfs_remove_base_directory_by_index(drfs_context* pContext, unsigned int index)
-{
- if (pContext == NULL) {
- return;
- }
-
- drfs_basedirs_remove(&pContext->baseDirectories, index);
-}
-
-void drfs_remove_all_base_directories(drfs_context* pContext)
-{
- if (pContext == NULL) {
- return;
- }
-
- drfs_basedirs_clear(&pContext->baseDirectories);
-}
-
-unsigned int drfs_get_base_directory_count(drfs_context* pContext)
-{
- if (pContext == NULL) {
- return 0;
- }
-
- return pContext->baseDirectories.count;
-}
-
-const char* drfs_get_base_directory_by_index(drfs_context* pContext, unsigned int index)
-{
- if (pContext == NULL || index >= pContext->baseDirectories.count) {
- return NULL;
- }
-
- return pContext->baseDirectories.pBuffer[index].absolutePath;
-}
-
-
-void drfs_set_base_write_directory(drfs_context* pContext, const char* absolutePath)
-{
- if (pContext == NULL) {
- return;
- }
-
- if (absolutePath == NULL) {
- memset(pContext->writeBaseDirectory, 0, sizeof(pContext->writeBaseDirectory));
- } else {
- drfs__strcpy_s(pContext->writeBaseDirectory, sizeof(pContext->writeBaseDirectory), absolutePath);
- }
-}
-
-dr_bool32 drfs_get_base_write_directory(drfs_context* pContext, char* absolutePathOut, unsigned int absolutePathOutSize)
-{
- if (pContext == NULL || absolutePathOut == NULL || absolutePathOutSize == 0) {
- return DR_FALSE;
- }
-
- return drfs__strcpy_s(absolutePathOut, absolutePathOutSize, pContext->writeBaseDirectory) == 0;
-}
-
-void drfs_enable_write_directory_guard(drfs_context* pContext)
-{
- if (pContext == NULL) {
- return;
- }
-
- pContext->isWriteGuardEnabled = DR_TRUE;
-}
-
-void drfs_disable_write_directory_guard(drfs_context* pContext)
-{
- if (pContext == NULL) {
- return;
- }
-
- pContext->isWriteGuardEnabled = DR_FALSE;
-}
-
-dr_bool32 drfs_is_write_directory_guard_enabled(drfs_context* pContext)
-{
- if (pContext == NULL) {
- return DR_FALSE;
- }
-
- return pContext->isWriteGuardEnabled;
-}
-
-
-
-
-drfs_result drfs_open_archive(drfs_context* pContext, const char* absoluteOrRelativePath, unsigned int accessMode, drfs_archive** ppArchiveOut)
-{
- if (ppArchiveOut == NULL) {
- return drfs_invalid_args;
- }
-
- *ppArchiveOut = NULL;
-
- if (pContext == NULL || absoluteOrRelativePath == NULL) {
- return drfs_invalid_args;
- }
-
- if (drfs_drpath_is_absolute(absoluteOrRelativePath))
- {
- if (drfs_is_native_directory(absoluteOrRelativePath))
- {
- // It's a native directory.
- return drfs_open_native_archive(pContext, absoluteOrRelativePath, accessMode, ppArchiveOut);
- }
- else
- {
- // It's not a native directory. We can just use drfs_open_owner_archive() in this case.
- char relativePath[DRFS_MAX_PATH];
- drfs_archive* pOwnerArchive;
- drfs_result result = drfs_open_owner_archive(pContext, absoluteOrRelativePath, accessMode, relativePath, sizeof(relativePath), &pOwnerArchive);
- if (result != drfs_success) {
- return result;
- }
-
- drfs_archive* pArchive;
- result = drfs_open_non_native_archive_from_path(pOwnerArchive, relativePath, accessMode, &pArchive);
- if (result != drfs_success) {
- drfs_close_archive(pOwnerArchive);
- return result;
- }
-
- drfs_recursively_claim_ownership_or_parent_archive(pArchive);
-
- *ppArchiveOut = pArchive;
- return drfs_success;
- }
- }
- else
- {
- // The input path is not absolute. We need to check each base directory.
-
- for (unsigned int iBaseDir = 0; iBaseDir < drfs_get_base_directory_count(pContext); ++iBaseDir)
- {
- drfs_archive* pArchive;
- drfs_result result = drfs_open_archive_from_relative_path(pContext, drfs_get_base_directory_by_index(pContext, iBaseDir), absoluteOrRelativePath, accessMode, &pArchive);
- if (result == drfs_success && pArchive != NULL) {
- drfs_recursively_claim_ownership_or_parent_archive(pArchive);
-
- *ppArchiveOut = pArchive;
- return drfs_success;
- }
- }
- }
-
- return drfs_does_not_exist;
-}
-
-drfs_result drfs_open_owner_archive(drfs_context* pContext, const char* absoluteOrRelativePath, unsigned int accessMode, char* relativePathOut, size_t relativePathOutSize, drfs_archive** ppArchiveOut)
-{
- if (ppArchiveOut == NULL) {
- return drfs_invalid_args;
- }
-
- *ppArchiveOut = NULL;
-
- if (pContext == NULL || absoluteOrRelativePath == NULL) {
- return drfs_invalid_args;
- }
-
- if (drfs_drpath_is_absolute(absoluteOrRelativePath))
- {
- if (drfs_is_native_file(absoluteOrRelativePath) || drfs_is_native_directory(absoluteOrRelativePath))
- {
- // It's a native file. The owner archive is the directory it's directly sitting in.
- char dirAbsolutePath[DRFS_MAX_PATH];
- drfs_drpath_copy_base_path(absoluteOrRelativePath, dirAbsolutePath, sizeof(dirAbsolutePath));
-
- drfs_archive* pArchive;
- drfs_result result = drfs_open_archive(pContext, dirAbsolutePath, accessMode, &pArchive);
- if (result != drfs_success) {
- return result;
- }
-
- if (relativePathOut) {
- if (drfs__strcpy_s(relativePathOut, relativePathOutSize, drfs_drpath_file_name(absoluteOrRelativePath)) != 0) {
- drfs_close_archive(pArchive);
- return drfs_path_too_long;
- }
- }
-
- *ppArchiveOut = pArchive;
- return drfs_success;
- }
- else
- {
- // It's not a native file or directory.
- drfs_archive* pArchive;
- drfs_result result = drfs_open_owner_archive_from_absolute_path(pContext, absoluteOrRelativePath, accessMode, relativePathOut, relativePathOutSize, &pArchive);
- if (pArchive == NULL) {
- return result;
- }
-
- drfs_recursively_claim_ownership_or_parent_archive(pArchive);
-
- *ppArchiveOut = pArchive;
- return drfs_success;
- }
- }
- else
- {
- // The input path is not absolute. We need to loop through each base directory.
-
- for (unsigned int iBaseDir = 0; iBaseDir < drfs_get_base_directory_count(pContext); ++iBaseDir)
- {
- drfs_archive* pArchive;
- drfs_result result = drfs_open_owner_archive_from_relative_path(pContext, drfs_get_base_directory_by_index(pContext, iBaseDir), absoluteOrRelativePath, accessMode, relativePathOut, relativePathOutSize, &pArchive);
- if (result == drfs_success && pArchive != NULL) {
- drfs_recursively_claim_ownership_or_parent_archive(pArchive);
-
- *ppArchiveOut = pArchive;
- return drfs_success;
- }
- }
- }
-
- return drfs_does_not_exist;
-}
-
-void drfs_close_archive(drfs_archive* pArchive)
-{
- if (pArchive == NULL) {
- return;
- }
-
- // The internal handle needs to be closed.
- if (pArchive->callbacks.close_archive) {
- pArchive->callbacks.close_archive(pArchive->internalArchiveHandle);
- }
-
- drfs_close(pArchive->pFile);
-
-
- if ((pArchive->flags & DR_FS_OWNS_PARENT_ARCHIVE) != 0) {
- drfs_close_archive(pArchive->pParentArchive);
- }
-
- free(pArchive);
-}
-
-drfs_result drfs_open_file_from_archive(drfs_archive* pArchive, const char* relativePath, unsigned int accessMode, drfs_file** ppFileOut)
-{
- if (ppFileOut == NULL) {
- return drfs_invalid_args;
- }
-
- *ppFileOut = NULL;
-
- if (pArchive == NULL || relativePath == NULL || pArchive->callbacks.open_file == NULL) {
- return drfs_invalid_args;
- }
-
- drfs_handle internalFileHandle;
- drfs_result result = pArchive->callbacks.open_file(pArchive->internalArchiveHandle, relativePath, accessMode, &internalFileHandle);
- if (result != drfs_success) {
- return result;
- }
-
- // At this point the file is opened and we can create the file object.
- drfs_file* pFile = (drfs_file*)malloc(sizeof(*pFile));
- if (pFile == NULL) {
- pArchive->callbacks.close_file(pArchive->internalArchiveHandle, internalFileHandle);
- return drfs_out_of_memory;
- }
-
- pFile->pArchive = pArchive;
- pFile->internalFileHandle = internalFileHandle;
- pFile->flags = 0;
-
- // The lock.
-#ifdef _WIN32
-#ifdef DR_FS_WIN32_USE_EVENT_MUTEX
- pFile->lock = CreateEvent(NULL, FALSE, TRUE, NULL);
-#else
- InitializeCriticalSection(&pFile->lock);
-#endif
-#else
- if (pthread_mutex_init(&pFile->lock, NULL) != 0) {
- drfs_close(pFile);
- return drfs_unknown_error;
- }
-#endif
-
- *ppFileOut = pFile;
- return drfs_success;
-}
-
-
-drfs_result drfs_open(drfs_context* pContext, const char* absoluteOrRelativePath, unsigned int accessMode, drfs_file** ppFile)
-{
- if (ppFile == NULL) {
- return drfs_invalid_args;
- }
-
- // Always set the output file to null at the start for safety.
- *ppFile = NULL;
-
- if (pContext == NULL || absoluteOrRelativePath == NULL) {
- return drfs_invalid_args;
- }
-
- char absolutePathForWriteMode[DRFS_MAX_PATH];
- if ((accessMode & DRFS_WRITE) != 0) {
- if (drfs_validate_write_path(pContext, absoluteOrRelativePath, absolutePathForWriteMode, sizeof(absolutePathForWriteMode))) {
- absoluteOrRelativePath = absolutePathForWriteMode;
- } else {
- return drfs_not_in_write_directory;
- }
- }
-
- char relativePath[DRFS_MAX_PATH];
- drfs_archive* pArchive;
- drfs_result result = drfs_open_owner_archive(pContext, absoluteOrRelativePath, drfs_archive_access_mode(accessMode), relativePath, sizeof(relativePath), &pArchive);
- if (result != drfs_success) {
- return result;
- }
-
- drfs_file* pFile;
- result = drfs_open_file_from_archive(pArchive, relativePath, accessMode, &pFile);
- if (result != drfs_success) {
- drfs_close_archive(pArchive);
- return result;
- }
-
- // When using this API, we want to claim ownership of the archive so that it's closed when we close this file.
- pFile->flags |= DR_FS_OWNS_PARENT_ARCHIVE;
-
- *ppFile = pFile;
- return drfs_success;
-}
-
-void drfs_close(drfs_file* pFile)
-{
- if (!drfs_lock(pFile)) {
- return;
- }
-
- if (pFile->pArchive != NULL && pFile->pArchive->callbacks.close_file) {
- pFile->pArchive->callbacks.close_file(pFile->pArchive->internalArchiveHandle, pFile->internalFileHandle);
- pFile->internalFileHandle = NULL;
- }
-
- if ((pFile->flags & DR_FS_OWNS_PARENT_ARCHIVE) != 0) {
- drfs_close_archive(pFile->pArchive);
- pFile->pArchive = NULL;
- }
-
- drfs_unlock(pFile);
-
-
- // The lock.
-#ifdef _WIN32
-#ifdef DR_FS_WIN32_USE_EVENT_MUTEX
- CloseHandle(pFile->lock);
-#else
- DeleteCriticalSection(&pFile->lock);
-#endif
-#else
- pthread_mutex_destroy(&pFile->lock);
-#endif
-
- free(pFile);
-}
-
-drfs_result drfs_read_nolock(drfs_file* pFile, void* pDataOut, size_t bytesToRead, size_t* pBytesReadOut)
-{
- if (pFile == NULL || pDataOut == NULL || pFile->pArchive == NULL || pFile->pArchive->callbacks.read_file == NULL) {
- return drfs_invalid_args;
- }
-
- return pFile->pArchive->callbacks.read_file(pFile->pArchive->internalArchiveHandle, pFile->internalFileHandle, pDataOut, bytesToRead, pBytesReadOut);
-}
-
-drfs_result drfs_read(drfs_file* pFile, void* pDataOut, size_t bytesToRead, size_t* pBytesReadOut)
-{
- if (!drfs_lock(pFile)) {
- return drfs_unknown_error;
- }
-
- drfs_result result = drfs_read_nolock(pFile, pDataOut, bytesToRead, pBytesReadOut);
-
- drfs_unlock(pFile);
- return result;
-}
-
-drfs_result drfs_write_nolock(drfs_file* pFile, const void* pData, size_t bytesToWrite, size_t* pBytesWrittenOut)
-{
- if (pFile == NULL || pData == NULL || pFile->pArchive == NULL || pFile->pArchive->callbacks.write_file == NULL) {
- return drfs_invalid_args;
- }
-
- return pFile->pArchive->callbacks.write_file(pFile->pArchive->internalArchiveHandle, pFile->internalFileHandle, pData, bytesToWrite, pBytesWrittenOut);
-}
-
-drfs_result drfs_write(drfs_file* pFile, const void* pData, size_t bytesToWrite, size_t* pBytesWrittenOut)
-{
- if (!drfs_lock(pFile)) {
- return drfs_unknown_error;
- }
-
- drfs_result result = drfs_write_nolock(pFile, pData, bytesToWrite, pBytesWrittenOut);
-
- drfs_unlock(pFile);
- return result;
-}
-
-drfs_result drfs_seek_nolock(drfs_file* pFile, dr_int64 bytesToSeek, drfs_seek_origin origin)
-{
- if (pFile == NULL || pFile->pArchive == NULL || pFile->pArchive->callbacks.seek_file == NULL) {
- return drfs_invalid_args;
- }
-
- return pFile->pArchive->callbacks.seek_file(pFile->pArchive->internalArchiveHandle, pFile->internalFileHandle, bytesToSeek, origin);
-}
-
-drfs_result drfs_seek(drfs_file* pFile, dr_int64 bytesToSeek, drfs_seek_origin origin)
-{
- if (!drfs_lock(pFile)) {
- return drfs_unknown_error;
- }
-
- drfs_result result = drfs_seek_nolock(pFile, bytesToSeek, origin);
-
- drfs_unlock(pFile);
- return result;
-}
-
-dr_uint64 drfs_tell_nolock(drfs_file* pFile)
-{
- if (pFile == NULL || pFile->pArchive == NULL || pFile->pArchive->callbacks.tell_file == NULL) {
- return DR_FALSE;
- }
-
- return pFile->pArchive->callbacks.tell_file(pFile->pArchive->internalArchiveHandle, pFile->internalFileHandle);
-}
-
-dr_uint64 drfs_tell(drfs_file* pFile)
-{
- if (!drfs_lock(pFile)) {
- return 0;
- }
-
- dr_uint64 result = drfs_tell_nolock(pFile);
-
- drfs_unlock(pFile);
- return result;
-}
-
-dr_uint64 drfs_size_nolock(drfs_file* pFile)
-{
- if (pFile == NULL || pFile->pArchive == NULL || pFile->pArchive->callbacks.file_size == NULL) {
- return 0;
- }
-
- return pFile->pArchive->callbacks.file_size(pFile->pArchive->internalArchiveHandle, pFile->internalFileHandle);
-}
-
-dr_uint64 drfs_size(drfs_file* pFile)
-{
- if (!drfs_lock(pFile)) {
- return DR_FALSE;
- }
-
- dr_uint64 result = drfs_size_nolock(pFile);
-
- drfs_unlock(pFile);
- return result;
-}
-
-void drfs_flush(drfs_file* pFile)
-{
- if (pFile == NULL || pFile->pArchive == NULL || pFile->pArchive->callbacks.flush_file == NULL) {
- return;
- }
-
- pFile->pArchive->callbacks.flush_file(pFile->pArchive->internalArchiveHandle, pFile->internalFileHandle);
-}
-
-
-dr_bool32 drfs_lock(drfs_file* pFile)
-{
- if (pFile == NULL || pFile->internalFileHandle == NULL) {
- return DR_FALSE;
- }
-
-#ifdef _WIN32
-#ifdef DR_FS_WIN32_USE_EVENT_MUTEX
- WaitForSingleObject(pFile->lock, INFINITE);
-#else
- EnterCriticalSection(&pFile->lock);
-#endif
-#else
- pthread_mutex_lock(&pFile->lock);
-#endif
-
- return DR_TRUE;
-}
-
-void drfs_unlock(drfs_file* pFile)
-{
- if (pFile == NULL || pFile->internalFileHandle == NULL) {
- return;
- }
-
-#ifdef _WIN32
-#ifdef DR_FS_WIN32_USE_EVENT_MUTEX
- SetEvent(pFile->lock);
-#else
- LeaveCriticalSection(&pFile->lock);
-#endif
-#else
- pthread_mutex_unlock(&pFile->lock);
-#endif
-}
-
-
-drfs_result drfs_get_file_info(drfs_context* pContext, const char* absoluteOrRelativePath, drfs_file_info* fi)
-{
- if (pContext == NULL || absoluteOrRelativePath == NULL) {
- return drfs_invalid_args;
- }
-
- char relativePath[DRFS_MAX_PATH];
- drfs_archive* pOwnerArchive;
- drfs_result result = drfs_open_owner_archive(pContext, absoluteOrRelativePath, DRFS_READ, relativePath, sizeof(relativePath), &pOwnerArchive);
- if (result != drfs_success) {
- return result;
- }
-
- result = drfs_no_backend;
- if (pOwnerArchive->callbacks.get_file_info) {
- result = pOwnerArchive->callbacks.get_file_info(pOwnerArchive->internalArchiveHandle, relativePath, fi);
- }
-
- if (result == drfs_success && fi != NULL) {
- drfs_drpath_copy_and_append(fi->absolutePath, sizeof(fi->absolutePath), pOwnerArchive->absolutePath, relativePath);
- }
-
- drfs_close_archive(pOwnerArchive);
- return result;
-}
-
-
-dr_bool32 drfs_begin(drfs_context* pContext, const char* absoluteOrRelativePath, drfs_iterator* pIteratorOut)
-{
- if (pIteratorOut == NULL) return DR_FALSE;
- memset(pIteratorOut, 0, sizeof(*pIteratorOut));
-
- if (pContext == NULL || absoluteOrRelativePath == NULL) {
- return DR_FALSE;
- }
-
- // We need to open the archive that the given folder is in. The path could, however, point to an actual archive which is allowed
- // in which case we just iterate over the root directory within that archive. What we do is first try using the path as an actual
- // archive. If it fails we assume the path is to a folder within an archive (such as a zip file) in which case we just try opening
- // the owner archive. If both fail, we return DR_FALSE.
-
- char relativePath[DRFS_MAX_PATH];
- drfs_result result = drfs_open_archive(pContext, absoluteOrRelativePath, DRFS_READ, &pIteratorOut->pArchive);
- if (result == drfs_success) {
- relativePath[0] = '\0';
- } else {
- result = drfs_open_owner_archive(pContext, absoluteOrRelativePath, DRFS_READ, relativePath, sizeof(relativePath), &pIteratorOut->pArchive);
- if (result != drfs_success) {
- return DR_FALSE;
- }
- }
-
- assert(pIteratorOut->pArchive != NULL);
-
-
- if (pIteratorOut->pArchive->callbacks.begin_iteration) {
- pIteratorOut->internalIteratorHandle = pIteratorOut->pArchive->callbacks.begin_iteration(pIteratorOut->pArchive->internalArchiveHandle, relativePath);
- }
-
- if (pIteratorOut->internalIteratorHandle == NULL) {
- drfs_close_archive(pIteratorOut->pArchive);
- pIteratorOut->pArchive = NULL;
- return DR_FALSE;
- }
-
-
- dr_bool32 foundFirst = drfs_next(pContext, pIteratorOut);
- if (!foundFirst) {
- drfs_end(pContext, pIteratorOut);
- }
-
- return foundFirst;
-}
-
-dr_bool32 drfs_next(drfs_context* pContext, drfs_iterator* pIterator)
-{
- if (pIterator == NULL) {
- return DR_FALSE;
- }
-
- memset(&pIterator->info, 0, sizeof(pIterator->info));
-
- if (pContext == NULL || pIterator->pArchive == NULL) {
- return DR_FALSE;
- }
-
- dr_bool32 result = DR_FALSE;
- if (pIterator->pArchive->callbacks.next_iteration) {
- result = pIterator->pArchive->callbacks.next_iteration(pIterator->pArchive->internalArchiveHandle, pIterator->internalIteratorHandle, &pIterator->info);
- }
-
- // At this point the pIterator->info.absolutePath is actually referring to a relative path. We need to convert it to an absolute path.
- if (result) {
- char relativePath[DRFS_MAX_PATH];
- drfs__strcpy_s(relativePath, sizeof(relativePath), pIterator->info.absolutePath);
- drfs_drpath_copy_and_append(pIterator->info.absolutePath, sizeof(pIterator->info.absolutePath), pIterator->pArchive->absolutePath, relativePath);
- }
-
- return result;
-}
-
-void drfs_end(drfs_context* pContext, drfs_iterator* pIterator)
-{
- if (pContext == NULL || pIterator == NULL) {
- return;
- }
-
- if (pIterator->pArchive != NULL && pIterator->pArchive->callbacks.end_iteration) {
- pIterator->pArchive->callbacks.end_iteration(pIterator->pArchive->internalArchiveHandle, pIterator->internalIteratorHandle);
- }
-
- drfs_close_archive(pIterator->pArchive);
- memset(pIterator, 0, sizeof(*pIterator));
-}
-
-drfs_result drfs_delete_file(drfs_context* pContext, const char* path)
-{
- if (pContext == NULL || path == NULL) {
- return drfs_invalid_args;
- }
-
- char absolutePath[DRFS_MAX_PATH];
- if (!drfs_validate_write_path(pContext, path, absolutePath, sizeof(absolutePath))) {
- return drfs_not_in_write_directory;
- }
-
-
- char relativePath[DRFS_MAX_PATH];
- drfs_archive* pArchive;
- drfs_result result = drfs_open_owner_archive(pContext, absolutePath, drfs_archive_access_mode(DRFS_READ | DRFS_WRITE), relativePath, sizeof(relativePath), &pArchive);
- if (result != drfs_success) {
- return result;
- }
-
- result = drfs_no_backend;
- if (pArchive->callbacks.delete_file) {
- result = pArchive->callbacks.delete_file(pArchive->internalArchiveHandle, relativePath);
- }
-
- drfs_close_archive(pArchive);
- return result;
-}
-
-drfs_result drfs_create_directory(drfs_context* pContext, const char* path)
-{
- if (pContext == NULL || path == NULL) {
- return drfs_invalid_args;
- }
-
- char absolutePath[DRFS_MAX_PATH];
- if (!drfs_validate_write_path(pContext, path, absolutePath, sizeof(absolutePath))) {
- return drfs_not_in_write_directory;
- }
-
- char relativePath[DRFS_MAX_PATH];
- drfs_archive* pArchive;
- drfs_result result = drfs_open_owner_archive(pContext, absolutePath, drfs_archive_access_mode(DRFS_READ | DRFS_WRITE), relativePath, sizeof(relativePath), &pArchive);
- if (result != drfs_success) {
- return result;
- }
-
- result = drfs_no_backend;
- if (pArchive->callbacks.create_directory) {
- result = pArchive->callbacks.create_directory(pArchive->internalArchiveHandle, relativePath);
- }
-
- drfs_close_archive(pArchive);
- return result;
-}
-
-drfs_result drfs_move_file(drfs_context* pContext, const char* pathOld, const char* pathNew)
-{
- // Renaming/moving is not supported across different archives.
-
- if (pContext == NULL || pathOld == NULL || pathNew == NULL) {
- return drfs_invalid_args;
- }
-
-
- char absolutePathOld[DRFS_MAX_PATH];
- if (drfs_validate_write_path(pContext, pathOld, absolutePathOld, sizeof(absolutePathOld))) {
- pathOld = absolutePathOld;
- } else {
- return drfs_not_in_write_directory;
- }
-
- char absolutePathNew[DRFS_MAX_PATH];
- if (drfs_validate_write_path(pContext, pathNew, absolutePathNew, sizeof(absolutePathNew))) {
- pathNew = absolutePathNew;
- } else {
- return drfs_not_in_write_directory;
- }
-
-
-
-
-
- char relativePathOld[DRFS_MAX_PATH];
- drfs_archive* pArchiveOld;
- drfs_result result = drfs_open_owner_archive(pContext, pathOld, drfs_archive_access_mode(DRFS_READ | DRFS_WRITE), relativePathOld, sizeof(relativePathOld), &pArchiveOld);
- if (pArchiveOld != NULL)
- {
- char relativePathNew[DRFS_MAX_PATH];
- drfs_archive* pArchiveNew;
- result = drfs_open_owner_archive(pContext, pathNew, drfs_archive_access_mode(DRFS_READ | DRFS_WRITE), relativePathNew, sizeof(relativePathNew), &pArchiveNew);
- if (pArchiveNew != NULL)
- {
- dr_bool32 areBothArchivesNative = (pArchiveOld->callbacks.move_file == pArchiveNew->callbacks.move_file && pArchiveNew->callbacks.move_file == drfs_move_file__native);
- if (areBothArchivesNative)
- {
- result = drfs_move_native_file(absolutePathOld, absolutePathNew);
- }
- else
- {
- if (drfs_drpath_equal(pArchiveOld->absolutePath, pArchiveNew->absolutePath) && pArchiveOld->callbacks.move_file) {
- result = pArchiveOld->callbacks.move_file(pArchiveOld, relativePathOld, relativePathNew);
- } else {
- result = drfs_permission_denied; // Attempting to rename across different archives which is not supported.
- }
- }
-
- drfs_close_archive(pArchiveNew);
-
- }
-
- drfs_close_archive(pArchiveOld);
- }
-
- return result;
-}
-
-drfs_result drfs_copy_file(drfs_context* pContext, const char* srcPath, const char* dstPath, dr_bool32 failIfExists)
-{
- if (pContext == NULL || srcPath == NULL || dstPath == NULL) {
- return drfs_invalid_args;
- }
-
- char dstPathAbsolute[DRFS_MAX_PATH];
- if (!drfs_validate_write_path(pContext, dstPath, dstPathAbsolute, sizeof(dstPathAbsolute))) {
- return drfs_not_in_write_directory;
- }
-
-
- // We want to open the archive of both the source and destination. If they are the same archive we'll do an intra-archive copy.
- char srcRelativePath[DRFS_MAX_PATH];
- drfs_archive* pSrcArchive;
- drfs_result result = drfs_open_owner_archive(pContext, srcPath, drfs_archive_access_mode(DRFS_READ), srcRelativePath, sizeof(srcRelativePath), &pSrcArchive);
- if (result != drfs_success) {
- return result;
- }
-
- char dstRelativePath[DRFS_MAX_PATH];
- drfs_archive* pDstArchive;
- result = drfs_open_owner_archive(pContext, dstPathAbsolute, drfs_archive_access_mode(DRFS_READ | DRFS_WRITE), dstRelativePath, sizeof(dstRelativePath), &pDstArchive);
- if (result != drfs_success) {
- drfs_close_archive(pSrcArchive);
- return result;
- }
-
-
-
- result = drfs_success;
- if (strcmp(pSrcArchive->absolutePath, pDstArchive->absolutePath) == 0 && pDstArchive->callbacks.copy_file)
- {
- // Intra-archive copy.
- result = pDstArchive->callbacks.copy_file(pDstArchive->internalArchiveHandle, srcRelativePath, dstRelativePath, failIfExists);
- }
- else
- {
- dr_bool32 areBothArchivesNative = (pSrcArchive->callbacks.copy_file == pDstArchive->callbacks.copy_file && pDstArchive->callbacks.copy_file == drfs_copy_file__native);
- if (areBothArchivesNative)
- {
- char srcPathAbsolute[DRFS_MAX_PATH];
- drfs_drpath_copy_and_append(srcPathAbsolute, sizeof(srcPathAbsolute), pSrcArchive->absolutePath, srcPath);
-
- result = drfs_copy_native_file(srcPathAbsolute, dstPathAbsolute, failIfExists);
- }
- else
- {
- // Inter-archive copy. This is a theoretically slower path because we do everything manually. Probably not much slower in practice, though.
- if (failIfExists && pDstArchive->callbacks.get_file_info(pDstArchive, dstRelativePath, NULL) == drfs_success)
- {
- result = drfs_already_exists;
- }
- else
- {
- drfs_file* pSrcFile;
- result = drfs_open_file_from_archive(pSrcArchive, srcRelativePath, DRFS_READ, &pSrcFile);
- if (result != drfs_success) {
- return result;
- }
-
- drfs_file* pDstFile;
- result = drfs_open_file_from_archive(pDstArchive, dstRelativePath, DRFS_WRITE | DRFS_TRUNCATE, &pDstFile);
- if (result != drfs_success) {
- drfs_close(pSrcFile);
- return result;
- }
-
- assert(pSrcFile != NULL);
- assert(pDstFile != NULL);
-
- char chunk[4096];
- size_t bytesRead;
- while (drfs_read(pSrcFile, chunk, sizeof(chunk), &bytesRead) == drfs_success && bytesRead > 0)
- {
- result = drfs_write(pDstFile, chunk, bytesRead, NULL);
- if (result != drfs_success) {
- break;
- }
- }
-
- drfs_close(pSrcFile);
- drfs_close(pDstFile);
- }
- }
- }
-
-
- drfs_close_archive(pSrcArchive);
- drfs_close_archive(pDstArchive);
-
- return result;
-}
-
-
-dr_bool32 drfs_is_archive_path(drfs_context* pContext, const char* path)
-{
- return drfs_find_backend_by_extension(pContext, drfs_drpath_extension(path), NULL);
-}
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// High Level API
-//
-///////////////////////////////////////////////////////////////////////////////
-void drfs_free(void* p)
-{
- free(p);
-}
-
-drfs_result drfs_find_absolute_path(drfs_context* pContext, const char* relativePath, char* absolutePathOut, size_t absolutePathOutSize)
-{
- if (absolutePathOut == NULL) return drfs_invalid_args;
- if (absolutePathOutSize > 0) absolutePathOut[0] = '\0';
-
- if (pContext == NULL || relativePath == NULL || absolutePathOutSize == 0) {
- return drfs_invalid_args;
- }
-
- drfs_file_info fi;
- if (drfs_get_file_info(pContext, relativePath, &fi) == drfs_success) {
- if (drfs__strcpy_s(absolutePathOut, absolutePathOutSize, fi.absolutePath) == 0) {
- return drfs_success;
- }
- }
-
- return drfs_does_not_exist;
-}
-
-drfs_result drfs_find_absolute_path_explicit_base(drfs_context* pContext, const char* relativePath, const char* highestPriorityBasePath, char* absolutePathOut, size_t absolutePathOutSize)
-{
- if (absolutePathOut == NULL) return drfs_invalid_args;
- if (absolutePathOutSize > 0) absolutePathOut[0] = '\0';
-
- if (pContext == NULL || relativePath == NULL || highestPriorityBasePath == NULL || absolutePathOutSize == 0) {
- return drfs_invalid_args;
- }
-
- drfs_result result = drfs_unknown_error;
- drfs_insert_base_directory(pContext, highestPriorityBasePath, 0);
- {
- result = drfs_find_absolute_path(pContext, relativePath, absolutePathOut, absolutePathOutSize);
- }
- drfs_remove_base_directory_by_index(pContext, 0);
-
- return result;
-}
-
-dr_bool32 drfs_is_base_directory(drfs_context* pContext, const char* baseDir)
-{
- if (pContext == NULL) {
- return DR_FALSE;
- }
-
- for (unsigned int i = 0; i < drfs_get_base_directory_count(pContext); ++i) {
- if (drfs_drpath_equal(drfs_get_base_directory_by_index(pContext, i), baseDir)) {
- return DR_TRUE;
- }
- }
-
- return DR_FALSE;
-}
-
-drfs_result drfs_write_string(drfs_file* pFile, const char* str)
-{
- return drfs_write(pFile, str, (unsigned int)strlen(str), NULL);
-}
-
-drfs_result drfs_write_line(drfs_file* pFile, const char* str)
-{
- drfs_result result = drfs_write_string(pFile, str);
- if (result != drfs_success) {
- return result;
- }
-
- return drfs_write_string(pFile, "\n");
-}
-
-
-void* drfs_open_and_read_binary_file(drfs_context* pContext, const char* absoluteOrRelativePath, size_t* pSizeInBytesOut)
-{
- drfs_file* pFile;
- if (drfs_open(pContext, absoluteOrRelativePath, DRFS_READ, &pFile) != drfs_success) {
- return NULL;
- }
-
- dr_uint64 fileSize = drfs_size(pFile);
- if (fileSize > SIZE_MAX)
- {
- // File's too big.
- drfs_close(pFile);
- return NULL;
- }
-
-
- void* pData = malloc((size_t)fileSize);
- if (pData == NULL)
- {
- // Failed to allocate memory.
- drfs_close(pFile);
- return NULL;
- }
-
-
- if (drfs_read(pFile, pData, (size_t)fileSize, NULL) != drfs_success)
- {
- free(pData);
- if (pSizeInBytesOut != NULL) {
- *pSizeInBytesOut = 0;
- }
-
- drfs_close(pFile);
- return NULL;
- }
-
-
- if (pSizeInBytesOut != NULL) {
- *pSizeInBytesOut = (size_t)fileSize;
- }
-
- drfs_close(pFile);
- return pData;
-}
-
-char* drfs_open_and_read_text_file(drfs_context* pContext, const char* absoluteOrRelativePath, size_t* pSizeInBytesOut)
-{
- drfs_file* pFile;
- if (drfs_open(pContext, absoluteOrRelativePath, DRFS_READ, &pFile) != drfs_success) {
- return NULL;
- }
-
- dr_uint64 fileSize = drfs_size(pFile);
- if (fileSize > SIZE_MAX)
- {
- // File's too big.
- drfs_close(pFile);
- return NULL;
- }
-
-
- void* pData = malloc((size_t)fileSize + 1); // +1 for null terminator.
- if (pData == NULL)
- {
- // Failed to allocate memory.
- drfs_close(pFile);
- return NULL;
- }
-
-
- if (drfs_read(pFile, pData, (size_t)fileSize, NULL) != drfs_success)
- {
- free(pData);
- if (pSizeInBytesOut != NULL) {
- *pSizeInBytesOut = 0;
- }
-
- drfs_close(pFile);
- return NULL;
- }
-
- // Null terminator.
- ((char*)pData)[fileSize] = '\0';
-
-
- if (pSizeInBytesOut != NULL) {
- *pSizeInBytesOut = (size_t)fileSize;
- }
-
- drfs_close(pFile);
- return (char*)pData;
-}
-
-drfs_result drfs_open_and_write_binary_file(drfs_context* pContext, const char* absoluteOrRelativePath, const void* pData, size_t dataSize)
-{
- drfs_result result = drfs_unknown_error;
-
- drfs_file* pFile;
- result = drfs_open(pContext, absoluteOrRelativePath, DRFS_WRITE | DRFS_TRUNCATE, &pFile);
- if (result != drfs_success) {
- return result;
- }
-
- result = drfs_write(pFile, pData, dataSize, NULL);
-
- drfs_close(pFile);
- return result;
-}
-
-drfs_result drfs_open_and_write_text_file(drfs_context* pContext, const char* absoluteOrRelativePath, const char* pTextData)
-{
- return drfs_open_and_write_binary_file(pContext, absoluteOrRelativePath, pTextData, strlen(pTextData));
-}
-
-
-dr_bool32 drfs_exists(drfs_context* pContext, const char* absoluteOrRelativePath)
-{
- drfs_file_info fi;
- return drfs_get_file_info(pContext, absoluteOrRelativePath, &fi) == drfs_success;
-}
-
-dr_bool32 drfs_is_existing_file(drfs_context* pContext, const char* absoluteOrRelativePath)
-{
- drfs_file_info fi;
- if (drfs_get_file_info(pContext, absoluteOrRelativePath, &fi) == drfs_success)
- {
- return (fi.attributes & DRFS_FILE_ATTRIBUTE_DIRECTORY) == 0;
- }
-
- return DR_FALSE;
-}
-
-dr_bool32 drfs_is_existing_directory(drfs_context* pContext, const char* absoluteOrRelativePath)
-{
- drfs_file_info fi;
- if (drfs_get_file_info(pContext, absoluteOrRelativePath, &fi) == drfs_success)
- {
- return (fi.attributes & DRFS_FILE_ATTRIBUTE_DIRECTORY) != 0;
- }
-
- return DR_FALSE;
-}
-
-drfs_result drfs_create_directory_recursive(drfs_context* pContext, const char* path)
-{
- if (pContext == NULL || path == NULL) {
- return drfs_invalid_args;
- }
-
- // We just iterate over each segment and try creating each directory if it doesn't exist.
- char absolutePath[DRFS_MAX_PATH];
- if (drfs_validate_write_path(pContext, path, absolutePath, DRFS_MAX_PATH)) {
- path = absolutePath;
- } else {
- return drfs_not_in_write_directory;
- }
-
-
- char runningPath[DRFS_MAX_PATH];
- runningPath[0] = '\0';
-
- drfs_drpath_iterator iPathSeg;
- if (!drfs_drpath_first(absolutePath, &iPathSeg)) {
- return drfs_invalid_args;
- }
-
- // Never check the first segment because we can assume it always exists - it will always be the drive root.
- if (drfs_drpath_append_iterator(runningPath, sizeof(runningPath), iPathSeg))
- {
- // Loop over every directory until we find one that does not exist.
- while (drfs_drpath_next(&iPathSeg))
- {
- if (!drfs_drpath_append_iterator(runningPath, sizeof(runningPath), iPathSeg)) {
- return drfs_path_too_long;
- }
-
- if (!drfs_is_existing_directory(pContext, runningPath)) {
- drfs_result result = drfs_create_directory(pContext, runningPath);
- if (result != drfs_success) {
- return result;
- }
-
- break;
- }
- }
-
-
- // At this point all we need to do is create the remaining directories - we can assert that the directory does not exist
- // rather than actually checking it which should be a bit more efficient.
- while (drfs_drpath_next(&iPathSeg))
- {
- if (!drfs_drpath_append_iterator(runningPath, sizeof(runningPath), iPathSeg)) {
- return drfs_path_too_long;
- }
-
- assert(!drfs_is_existing_directory(pContext, runningPath));
-
- drfs_result result = drfs_create_directory(pContext, runningPath);
- if (result != drfs_success) {
- return result;
- }
- }
-
-
- return drfs_success;
- }
- else
- {
- return drfs_invalid_args;
- }
-}
-
-dr_bool32 drfs_eof(drfs_file* pFile)
-{
- return drfs_tell(pFile) == drfs_size(pFile);
-}
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// ZIP
-//
-///////////////////////////////////////////////////////////////////////////////
-#ifndef DR_FS_NO_ZIP
-
-#if defined(_MSC_VER)
- #pragma warning(push)
- #pragma warning(disable:4334)
-#endif
-#if defined(__GNUC__)
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wunused-macros"
- #pragma GCC diagnostic ignored "-Wcast-align"
- #pragma GCC diagnostic ignored "-Wextra"
-
-#if __GNUC__ >= 6 && !defined __clang__
- #pragma GCC diagnostic ignored "-Wmisleading-indentation"
-#endif
-#endif
-
-#ifndef DRFS_MINIZ_HEADER_INCLUDED
-#define DRFS_MINIZ_HEADER_INCLUDED
-
-// If DRFS_MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or
-// get/set file times, and the C run-time funcs that get/set times won't be called.
-// The current downside is the times written to your archives will be from 1979.
-//#define DRFS_MINIZ_NO_TIME
-
-// Define DRFS_MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's.
-//#define DRFS_MINIZ_NO_ARCHIVE_APIS
-
-#if defined(__TINYC__) && (defined(__linux) || defined(__linux__))
- // TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux
- #define DRFS_MINIZ_NO_TIME
-#endif
-
-#if !defined(DRFS_MINIZ_NO_TIME) && !defined(DRFS_MINIZ_NO_ARCHIVE_APIS)
- #include
-#endif
-
-#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__)
-// DRFS_MINIZ_X86_OR_X64_CPU is only used to help set the below macros.
-#define DRFS_MINIZ_X86_OR_X64_CPU 1
-#endif
-
-#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || DRFS_MINIZ_X86_OR_X64_CPU
-// Set DRFS_MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian.
-#define DRFS_MINIZ_LITTLE_ENDIAN 1
-#endif
-
-#if DRFS_MINIZ_X86_OR_X64_CPU
-// Set DRFS_MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses.
-#define DRFS_MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
-#endif
-
-#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__)
-// Set DRFS_MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions).
-#define DRFS_MINIZ_HAS_64BIT_REGISTERS 1
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// ------------------- zlib-style API Definitions.
-
-// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: drfs_mz_ulong can be either 32 or 64-bits!
-typedef unsigned long drfs_mz_ulong;
-
-// drfs_mz_free() internally uses the DRFS_MZ_FREE() macro (which by default calls free() unless you've modified the DRFS_MZ_MALLOC macro) to release a block allocated from the heap.
-void drfs_mz_free(void *p);
-
-#define DRFS_MZ_ADLER32_INIT (1)
-// drfs_mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL.
-drfs_mz_ulong drfs_mz_adler32(drfs_mz_ulong adler, const unsigned char *ptr, size_t buf_len);
-
-#define DRFS_MZ_CRC32_INIT (0)
-// drfs_mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL.
-drfs_mz_ulong drfs_mz_crc32(drfs_mz_ulong crc, const unsigned char *ptr, size_t buf_len);
-
-// Compression strategies.
-enum { DRFS_MZ_DEFAULT_STRATEGY = 0, DRFS_MZ_FILTERED = 1, DRFS_MZ_HUFFMAN_ONLY = 2, DRFS_MZ_RLE = 3, DRFS_MZ_FIXED = 4 };
-
-// Method
-#define DRFS_MZ_DEFLATED 8
-
-// Heap allocation callbacks.
-// Note that drfs_mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long.
-typedef void *(*drfs_mz_alloc_func)(void *opaque, size_t items, size_t size);
-typedef void (*drfs_mz_free_func)(void *opaque, void *address);
-typedef void *(*drfs_mz_realloc_func)(void *opaque, void *address, size_t items, size_t size);
-
-// ------------------- Types and macros
-
-typedef unsigned char drfs_mz_uint8;
-typedef signed short drfs_mz_int16;
-typedef unsigned short drfs_drfs_mz_uint16;
-typedef unsigned int drfs_drfs_mz_uint32;
-typedef unsigned int drfs_mz_uint;
-typedef long long drfs_mz_int64;
-typedef unsigned long long drfs_mz_uint64;
-typedef int drfs_mz_bool;
-
-#define DRFS_MZ_FALSE (0)
-#define DRFS_MZ_TRUE (1)
-
-// An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message.
-#ifdef _MSC_VER
- #define DRFS_MZ_MACRO_END while (0, 0)
-#else
- #define DRFS_MZ_MACRO_END while (0)
-#endif
-
-// ------------------- ZIP archive reading/writing
-
-#ifndef DRFS_MINIZ_NO_ARCHIVE_APIS
-
-enum
-{
- DRFS_MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024,
- DRFS_MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260,
- DRFS_MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256
-};
-
-typedef struct
-{
- drfs_drfs_mz_uint32 m_file_index;
- drfs_drfs_mz_uint32 m_central_dir_ofs;
- drfs_drfs_mz_uint16 m_version_made_by;
- drfs_drfs_mz_uint16 m_version_needed;
- drfs_drfs_mz_uint16 m_bit_flag;
- drfs_drfs_mz_uint16 m_method;
-#ifndef DRFS_MINIZ_NO_TIME
- time_t m_time;
-#endif
- drfs_drfs_mz_uint32 m_crc32;
- drfs_mz_uint64 m_comp_size;
- drfs_mz_uint64 m_uncomp_size;
- drfs_drfs_mz_uint16 m_internal_attr;
- drfs_drfs_mz_uint32 m_external_attr;
- drfs_mz_uint64 m_local_header_ofs;
- drfs_drfs_mz_uint32 m_comment_size;
- char m_filename[DRFS_MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE];
- char m_comment[DRFS_MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE];
-} drfs_drfs_mz_zip_archive_file_stat;
-
-typedef size_t (*drfs_mz_file_read_func)(void *pOpaque, drfs_mz_uint64 file_ofs, void *pBuf, size_t n);
-typedef size_t (*drfs_mz_file_write_func)(void *pOpaque, drfs_mz_uint64 file_ofs, const void *pBuf, size_t n);
-
-struct drfs_mz_zip_internal_state_tag;
-typedef struct drfs_mz_zip_internal_state_tag drfs_mz_zip_internal_state;
-
-typedef enum
-{
- DRFS_MZ_ZIP_MODE_INVALID = 0,
- DRFS_MZ_ZIP_MODE_READING = 1,
- DRFS_MZ_ZIP_MODE_WRITING = 2,
- DRFS_MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3
-} drfs_mz_zip_mode;
-
-typedef struct drfs_mz_zip_archive_tag
-{
- drfs_mz_uint64 m_archive_size;
- drfs_mz_uint64 m_central_directory_file_ofs;
- drfs_mz_uint m_total_files;
- drfs_mz_zip_mode m_zip_mode;
-
- drfs_mz_uint m_file_offset_alignment;
-
- drfs_mz_alloc_func m_pAlloc;
- drfs_mz_free_func m_pFree;
- drfs_mz_realloc_func m_pRealloc;
- void *m_pAlloc_opaque;
-
- drfs_mz_file_read_func m_pRead;
- drfs_mz_file_write_func m_pWrite;
- void *m_pIO_opaque;
-
- drfs_mz_zip_internal_state *m_pState;
-
-} drfs_mz_zip_archive;
-
-typedef enum
-{
- DRFS_MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100,
- DRFS_MZ_ZIP_FLAG_IGNORE_PATH = 0x0200,
- DRFS_MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400,
- DRFS_MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800
-} drfs_mz_zip_flags;
-
-// ZIP archive reading
-
-// Inits a ZIP archive reader.
-// These functions read and validate the archive's central directory.
-drfs_mz_bool drfs_mz_zip_reader_init(drfs_mz_zip_archive *pZip, drfs_mz_uint64 size, drfs_drfs_mz_uint32 flags);
-
-// Returns the total number of files in the archive.
-drfs_mz_uint drfs_mz_zip_reader_get_num_files(drfs_mz_zip_archive *pZip);
-
-// Returns detailed information about an archive file entry.
-drfs_mz_bool drfs_mz_zip_reader_file_stat(drfs_mz_zip_archive *pZip, drfs_mz_uint file_index, drfs_drfs_mz_zip_archive_file_stat *pStat);
-
-// Determines if an archive file entry is a directory entry.
-drfs_mz_bool drfs_mz_zip_reader_is_file_a_directory(drfs_mz_zip_archive *pZip, drfs_mz_uint file_index);
-
-// Retrieves the filename of an archive file entry.
-// Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename.
-drfs_mz_uint drfs_mz_zip_reader_get_filename(drfs_mz_zip_archive *pZip, drfs_mz_uint file_index, char *pFilename, drfs_mz_uint filename_buf_size);
-
-// Attempts to locates a file in the archive's central directory.
-// Valid flags: DRFS_MZ_ZIP_FLAG_CASE_SENSITIVE, DRFS_MZ_ZIP_FLAG_IGNORE_PATH
-// Returns -1 if the file cannot be found.
-int drfs_mz_zip_reader_locate_file(drfs_mz_zip_archive *pZip, const char *pName, const char *pComment, drfs_mz_uint flags);
-
-// Extracts a archive file to a memory buffer using no memory allocation.
-drfs_mz_bool drfs_mz_zip_reader_extract_to_mem_no_alloc(drfs_mz_zip_archive *pZip, drfs_mz_uint file_index, void *pBuf, size_t buf_size, drfs_mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
-drfs_mz_bool drfs_mz_zip_reader_extract_file_to_mem_no_alloc(drfs_mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, drfs_mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
-
-// Extracts a archive file to a memory buffer.
-drfs_mz_bool drfs_mz_zip_reader_extract_to_mem(drfs_mz_zip_archive *pZip, drfs_mz_uint file_index, void *pBuf, size_t buf_size, drfs_mz_uint flags);
-drfs_mz_bool drfs_mz_zip_reader_extract_file_to_mem(drfs_mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, drfs_mz_uint flags);
-
-// Extracts a archive file to a dynamically allocated heap buffer.
-void *drfs_mz_zip_reader_extract_to_heap(drfs_mz_zip_archive *pZip, drfs_mz_uint file_index, size_t *pSize, drfs_mz_uint flags);
-
-// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used.
-drfs_mz_bool drfs_mz_zip_reader_end(drfs_mz_zip_archive *pZip);
-
-#endif // #ifndef DRFS_MINIZ_NO_ARCHIVE_APIS
-
-// ------------------- Low-level Decompression API Definitions
-
-// Decompression flags used by drfs_tinfl_decompress().
-// DRFS_TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream.
-// DRFS_TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input.
-// DRFS_TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB).
-// DRFS_TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes.
-enum
-{
- DRFS_TINFL_FLAG_PARSE_ZLIB_HEADER = 1,
- DRFS_TINFL_FLAG_HAS_MORE_INPUT = 2,
- DRFS_TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4,
- DRFS_TINFL_FLAG_COMPUTE_ADLER32 = 8
-};
-
-// High level decompression functions:
-// drfs_tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc().
-// On entry:
-// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress.
-// On return:
-// Function returns a pointer to the decompressed data, or NULL on failure.
-// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data.
-// The caller must call drfs_mz_free() on the returned block when it's no longer needed.
-void *drfs_tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
-
-// drfs_tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory.
-// Returns DRFS_TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success.
-#define DRFS_TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1))
-size_t drfs_tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
-
-// drfs_tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer.
-// Returns 1 on success or 0 on failure.
-typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser);
-int drfs_tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
-
-struct drfs_tinfl_decompressor_tag; typedef struct drfs_tinfl_decompressor_tag drfs_tinfl_decompressor;
-
-// Max size of LZ dictionary.
-#define DRFS_TINFL_LZ_DICT_SIZE 32768
-
-// Return status.
-typedef enum
-{
- DRFS_TINFL_STATUS_BAD_PARAM = -3,
- DRFS_TINFL_STATUS_ADLER32_MISMATCH = -2,
- DRFS_TINFL_STATUS_FAILED = -1,
- DRFS_TINFL_STATUS_DONE = 0,
- DRFS_TINFL_STATUS_NEEDS_MORE_INPUT = 1,
- DRFS_TINFL_STATUS_HAS_MORE_OUTPUT = 2
-} drfs_tinfl_status;
-
-// Initializes the decompressor to its initial state.
-#define drfs_tinfl_init(r) do { (r)->m_state = 0; } DRFS_MZ_MACRO_END
-#define drfs_tinfl_get_adler32(r) (r)->m_check_adler32
-
-// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability.
-// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output.
-drfs_tinfl_status drfs_tinfl_decompress(drfs_tinfl_decompressor *r, const drfs_mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, drfs_mz_uint8 *pOut_buf_start, drfs_mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const drfs_drfs_mz_uint32 decomp_flags);
-
-// Internal/private bits follow.
-enum
-{
- DRFS_TINFL_MAX_HUFF_TABLES = 3, DRFS_TINFL_MAX_HUFF_SYMBOLS_0 = 288, DRFS_TINFL_MAX_HUFF_SYMBOLS_1 = 32, DRFS_TINFL_MAX_HUFF_SYMBOLS_2 = 19,
- DRFS_TINFL_FAST_LOOKUP_BITS = 10, DRFS_TINFL_FAST_LOOKUP_SIZE = 1 << DRFS_TINFL_FAST_LOOKUP_BITS
-};
-
-typedef struct
-{
- drfs_mz_uint8 m_code_size[DRFS_TINFL_MAX_HUFF_SYMBOLS_0];
- drfs_mz_int16 m_look_up[DRFS_TINFL_FAST_LOOKUP_SIZE], m_tree[DRFS_TINFL_MAX_HUFF_SYMBOLS_0 * 2];
-} drfs_tinfl_huff_table;
-
-#if DRFS_MINIZ_HAS_64BIT_REGISTERS
- #define DRFS_TINFL_USE_64BIT_BITBUF 1
-#endif
-
-#if DRFS_TINFL_USE_64BIT_BITBUF
- typedef drfs_mz_uint64 drfs_tinfl_bit_buf_t;
- #define DRFS_TINFL_BITBUF_SIZE (64)
-#else
- typedef drfs_drfs_mz_uint32 drfs_tinfl_bit_buf_t;
- #define DRFS_TINFL_BITBUF_SIZE (32)
-#endif
-
-struct drfs_tinfl_decompressor_tag
-{
- drfs_drfs_mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[DRFS_TINFL_MAX_HUFF_TABLES];
- drfs_tinfl_bit_buf_t m_bit_buf;
- size_t m_dist_from_out_buf_start;
- drfs_tinfl_huff_table m_tables[DRFS_TINFL_MAX_HUFF_TABLES];
- drfs_mz_uint8 m_raw_header[4], m_len_codes[DRFS_TINFL_MAX_HUFF_SYMBOLS_0 + DRFS_TINFL_MAX_HUFF_SYMBOLS_1 + 137];
-};
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // MINIZ_HEADER_INCLUDED
-
-// ------------------- End of Header: Implementation follows. (If you only want the header, define DRFS_MINIZ_HEADER_FILE_ONLY.)
-
-#ifndef DRFS_MINIZ_HEADER_FILE_ONLY
-
-typedef unsigned char mz_validate_uint16[sizeof(drfs_drfs_mz_uint16)==2 ? 1 : -1];
-typedef unsigned char mz_validate_uint32[sizeof(drfs_drfs_mz_uint32)==4 ? 1 : -1];
-typedef unsigned char mz_validate_uint64[sizeof(drfs_mz_uint64)==8 ? 1 : -1];
-
-#include
-#include
-
-#define DRFS_MZ_ASSERT(x) assert(x)
-
-#define DRFS_MZ_MALLOC(x) malloc(x)
-#define DRFS_MZ_FREE(x) free(x)
-#define DRFS_MZ_REALLOC(p, x) realloc(p, x)
-
-#define DRFS_MZ_MAX(a,b) (((a)>(b))?(a):(b))
-#define DRFS_MZ_MIN(a,b) (((a)<(b))?(a):(b))
-#define DRFS_MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj))
-
-#if DRFS_MINIZ_USE_UNALIGNED_LOADS_AND_STORES && DRFS_MINIZ_LITTLE_ENDIAN
- #define DRFS_MZ_READ_LE16(p) *((const drfs_drfs_mz_uint16 *)(p))
- #define DRFS_MZ_READ_LE32(p) *((const drfs_drfs_mz_uint32 *)(p))
-#else
- #define DRFS_MZ_READ_LE16(p) ((drfs_drfs_mz_uint32)(((const drfs_mz_uint8 *)(p))[0]) | ((drfs_drfs_mz_uint32)(((const drfs_mz_uint8 *)(p))[1]) << 8U))
- #define DRFS_MZ_READ_LE32(p) ((drfs_drfs_mz_uint32)(((const drfs_mz_uint8 *)(p))[0]) | ((drfs_drfs_mz_uint32)(((const drfs_mz_uint8 *)(p))[1]) << 8U) | ((drfs_drfs_mz_uint32)(((const drfs_mz_uint8 *)(p))[2]) << 16U) | ((drfs_drfs_mz_uint32)(((const drfs_mz_uint8 *)(p))[3]) << 24U))
-#endif
-
-#ifdef _MSC_VER
- #define DRFS_MZ_FORCEINLINE __forceinline
-#elif defined(__GNUC__)
- #define DRFS_MZ_FORCEINLINE inline __attribute__((__always_inline__))
-#else
- #define DRFS_MZ_FORCEINLINE inline
-#endif
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-static void *drfs__def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return DRFS_MZ_MALLOC(items * size); }
-static void drfs__def_free_func(void *opaque, void *address) { (void)opaque, (void)address; DRFS_MZ_FREE(address); }
-static void *drfs__def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return DRFS_MZ_REALLOC(address, items * size); }
-
-// ------------------- zlib-style API's
-
-drfs_mz_ulong drfs_mz_adler32(drfs_mz_ulong adler, const unsigned char *ptr, size_t buf_len)
-{
- drfs_drfs_mz_uint32 i, s1 = (drfs_drfs_mz_uint32)(adler & 0xffff), s2 = (drfs_drfs_mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552;
- if (!ptr) return DRFS_MZ_ADLER32_INIT;
- while (buf_len) {
- for (i = 0; i + 7 < block_len; i += 8, ptr += 8) {
- s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1;
- s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1;
- }
- for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
- s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
- }
- return (s2 << 16) + s1;
-}
-
-// Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/
-drfs_mz_ulong drfs_mz_crc32(drfs_mz_ulong crc, const drfs_mz_uint8 *ptr, size_t buf_len)
-{
- static const drfs_drfs_mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
- 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c };
- drfs_drfs_mz_uint32 crcu32 = (drfs_drfs_mz_uint32)crc;
- if (!ptr) return DRFS_MZ_CRC32_INIT;
- crcu32 = ~crcu32; while (buf_len--) { drfs_mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; }
- return ~crcu32;
-}
-
-void drfs_mz_free(void *p)
-{
- DRFS_MZ_FREE(p);
-}
-
-
-// ------------------- Low-level Decompression (completely independent from all compression API's)
-
-#define DRFS_TINFL_MEMCPY(d, s, l) memcpy(d, s, l)
-#define DRFS_TINFL_MEMSET(p, c, l) memset(p, c, l)
-
-#define DRFS_TINFL_CR_BEGIN switch(r->m_state) { case 0:
-#define DRFS_TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } DRFS_MZ_MACRO_END
-#define DRFS_TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { DRFS_TINFL_CR_RETURN(state_index, result); } } DRFS_MZ_MACRO_END
-#define DRFS_TINFL_CR_FINISH }
-
-// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never
-// reads ahead more than it needs to. Currently DRFS_TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario.
-#define DRFS_TINFL_GET_BYTE(state_index, c) do { \
- if (pIn_buf_cur >= pIn_buf_end) { \
- for ( ; ; ) { \
- if (decomp_flags & DRFS_TINFL_FLAG_HAS_MORE_INPUT) { \
- DRFS_TINFL_CR_RETURN(state_index, DRFS_TINFL_STATUS_NEEDS_MORE_INPUT); \
- if (pIn_buf_cur < pIn_buf_end) { \
- c = *pIn_buf_cur++; \
- break; \
- } \
- } else { \
- c = 0; \
- break; \
- } \
- } \
- } else c = *pIn_buf_cur++; } DRFS_MZ_MACRO_END
-
-#define DRFS_TINFL_NEED_BITS(state_index, n) do { drfs_mz_uint c; DRFS_TINFL_GET_BYTE(state_index, c); bit_buf |= (((drfs_tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (drfs_mz_uint)(n))
-#define DRFS_TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (drfs_mz_uint)(n)) { DRFS_TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } DRFS_MZ_MACRO_END
-#define DRFS_TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (drfs_mz_uint)(n)) { DRFS_TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } DRFS_MZ_MACRO_END
-
-// DRFS_TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2.
-// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a
-// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the
-// bit buffer contains >=15 bits (deflate's max. Huffman code size).
-#define DRFS_TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \
- do { \
- temp = (pHuff)->m_look_up[bit_buf & (DRFS_TINFL_FAST_LOOKUP_SIZE - 1)]; \
- if (temp >= 0) { \
- code_len = temp >> 9; \
- if ((code_len) && (num_bits >= code_len)) \
- break; \
- } else if (num_bits > DRFS_TINFL_FAST_LOOKUP_BITS) { \
- code_len = DRFS_TINFL_FAST_LOOKUP_BITS; \
- do { \
- temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \
- } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \
- } DRFS_TINFL_GET_BYTE(state_index, c); bit_buf |= (((drfs_tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \
- } while (num_bits < 15);
-
-// DRFS_TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read
-// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully
-// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32.
-// The slow path is only executed at the very end of the input buffer.
-#define DRFS_TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \
- int temp; drfs_mz_uint code_len, c; \
- if (num_bits < 15) { \
- if ((pIn_buf_end - pIn_buf_cur) < 2) { \
- DRFS_TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \
- } else { \
- bit_buf |= (((drfs_tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((drfs_tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \
- } \
- } \
- if ((temp = (pHuff)->m_look_up[bit_buf & (DRFS_TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \
- code_len = temp >> 9, temp &= 511; \
- else { \
- code_len = DRFS_TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \
- } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } DRFS_MZ_MACRO_END
-
-drfs_tinfl_status drfs_tinfl_decompress(drfs_tinfl_decompressor *r, const drfs_mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, drfs_mz_uint8 *pOut_buf_start, drfs_mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const drfs_drfs_mz_uint32 decomp_flags)
-{
- static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 };
- static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
- static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
- static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
- static const drfs_mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
- static const int s_min_table_sizes[3] = { 257, 1, 4 };
-
- drfs_tinfl_status status = DRFS_TINFL_STATUS_FAILED; drfs_drfs_mz_uint32 num_bits, dist, counter, num_extra; drfs_tinfl_bit_buf_t bit_buf;
- const drfs_mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size;
- drfs_mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size;
- size_t out_buf_size_mask = (decomp_flags & DRFS_TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start;
-
- // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter).
- if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return DRFS_TINFL_STATUS_BAD_PARAM; }
-
- num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start;
- DRFS_TINFL_CR_BEGIN
-
- bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1;
- if (decomp_flags & DRFS_TINFL_FLAG_PARSE_ZLIB_HEADER)
- {
- DRFS_TINFL_GET_BYTE(1, r->m_zhdr0); DRFS_TINFL_GET_BYTE(2, r->m_zhdr1);
- counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8));
- if (!(decomp_flags & DRFS_TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4)))));
- if (counter) { DRFS_TINFL_CR_RETURN_FOREVER(36, DRFS_TINFL_STATUS_FAILED); }
- }
-
- do
- {
- DRFS_TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1;
- if (r->m_type == 0)
- {
- DRFS_TINFL_SKIP_BITS(5, num_bits & 7);
- for (counter = 0; counter < 4; ++counter) { if (num_bits) DRFS_TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else DRFS_TINFL_GET_BYTE(7, r->m_raw_header[counter]); }
- if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (drfs_mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { DRFS_TINFL_CR_RETURN_FOREVER(39, DRFS_TINFL_STATUS_FAILED); }
- while ((counter) && (num_bits))
- {
- DRFS_TINFL_GET_BITS(51, dist, 8);
- while (pOut_buf_cur >= pOut_buf_end) { DRFS_TINFL_CR_RETURN(52, DRFS_TINFL_STATUS_HAS_MORE_OUTPUT); }
- *pOut_buf_cur++ = (drfs_mz_uint8)dist;
- counter--;
- }
- while (counter)
- {
- size_t n; while (pOut_buf_cur >= pOut_buf_end) { DRFS_TINFL_CR_RETURN(9, DRFS_TINFL_STATUS_HAS_MORE_OUTPUT); }
- while (pIn_buf_cur >= pIn_buf_end)
- {
- if (decomp_flags & DRFS_TINFL_FLAG_HAS_MORE_INPUT)
- {
- DRFS_TINFL_CR_RETURN(38, DRFS_TINFL_STATUS_NEEDS_MORE_INPUT);
- }
- else
- {
- DRFS_TINFL_CR_RETURN_FOREVER(40, DRFS_TINFL_STATUS_FAILED);
- }
- }
- n = DRFS_MZ_MIN(DRFS_MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter);
- DRFS_TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (drfs_mz_uint)n;
- }
- }
- else if (r->m_type == 3)
- {
- DRFS_TINFL_CR_RETURN_FOREVER(10, DRFS_TINFL_STATUS_FAILED);
- }
- else
- {
- if (r->m_type == 1)
- {
- drfs_mz_uint8 *p = r->m_tables[0].m_code_size; drfs_mz_uint i;
- r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; DRFS_TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32);
- for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8;
- }
- else
- {
- for (counter = 0; counter < 3; counter++) { DRFS_TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; }
- DRFS_MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { drfs_mz_uint s; DRFS_TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (drfs_mz_uint8)s; }
- r->m_table_sizes[2] = 19;
- }
- for ( ; (int)r->m_type >= 0; r->m_type--)
- {
- int tree_next, tree_cur; drfs_tinfl_huff_table *pTable;
- drfs_mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; DRFS_MZ_CLEAR_OBJ(total_syms); DRFS_MZ_CLEAR_OBJ(pTable->m_look_up); DRFS_MZ_CLEAR_OBJ(pTable->m_tree);
- for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++;
- used_syms = 0, total = 0; next_code[0] = next_code[1] = 0;
- for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); }
- if ((65536 != total) && (used_syms > 1))
- {
- DRFS_TINFL_CR_RETURN_FOREVER(35, DRFS_TINFL_STATUS_FAILED);
- }
- for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index)
- {
- drfs_mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue;
- cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1);
- if (code_size <= DRFS_TINFL_FAST_LOOKUP_BITS) { drfs_mz_int16 k = (drfs_mz_int16)((code_size << 9) | sym_index); while (rev_code < DRFS_TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; }
- if (0 == (tree_cur = pTable->m_look_up[rev_code & (DRFS_TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (DRFS_TINFL_FAST_LOOKUP_SIZE - 1)] = (drfs_mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; }
- rev_code >>= (DRFS_TINFL_FAST_LOOKUP_BITS - 1);
- for (j = code_size; j > (DRFS_TINFL_FAST_LOOKUP_BITS + 1); j--)
- {
- tree_cur -= ((rev_code >>= 1) & 1);
- if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (drfs_mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1];
- }
- tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (drfs_mz_int16)sym_index;
- }
- if (r->m_type == 2)
- {
- for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); )
- {
- drfs_mz_uint s; DRFS_TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (drfs_mz_uint8)dist; continue; }
- if ((dist == 16) && (!counter))
- {
- DRFS_TINFL_CR_RETURN_FOREVER(17, DRFS_TINFL_STATUS_FAILED);
- }
- num_extra = "\02\03\07"[dist - 16]; DRFS_TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16];
- DRFS_TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s;
- }
- if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter)
- {
- DRFS_TINFL_CR_RETURN_FOREVER(21, DRFS_TINFL_STATUS_FAILED);
- }
- DRFS_TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); DRFS_TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]);
- }
- }
- for ( ; ; )
- {
- drfs_mz_uint8 *pSrc;
- for ( ; ; )
- {
- if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2))
- {
- DRFS_TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]);
- if (counter >= 256)
- break;
- while (pOut_buf_cur >= pOut_buf_end) { DRFS_TINFL_CR_RETURN(24, DRFS_TINFL_STATUS_HAS_MORE_OUTPUT); }
- *pOut_buf_cur++ = (drfs_mz_uint8)counter;
- }
- else
- {
- int sym2; drfs_mz_uint code_len;
-#if DRFS_TINFL_USE_64BIT_BITBUF
- if (num_bits < 30) { bit_buf |= (((drfs_tinfl_bit_buf_t)DRFS_MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; }
-#else
- if (num_bits < 15) { bit_buf |= (((drfs_tinfl_bit_buf_t)DRFS_MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
-#endif
- if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (DRFS_TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
- code_len = sym2 >> 9;
- else
- {
- code_len = DRFS_TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
- }
- counter = sym2; bit_buf >>= code_len; num_bits -= code_len;
- if (counter & 256)
- break;
-
-#if !DRFS_TINFL_USE_64BIT_BITBUF
- if (num_bits < 15) { bit_buf |= (((drfs_tinfl_bit_buf_t)DRFS_MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
-#endif
- if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (DRFS_TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
- code_len = sym2 >> 9;
- else
- {
- code_len = DRFS_TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
- }
- bit_buf >>= code_len; num_bits -= code_len;
-
- pOut_buf_cur[0] = (drfs_mz_uint8)counter;
- if (sym2 & 256)
- {
- pOut_buf_cur++;
- counter = sym2;
- break;
- }
- pOut_buf_cur[1] = (drfs_mz_uint8)sym2;
- pOut_buf_cur += 2;
- }
- }
- if ((counter &= 511) == 256) break;
-
- num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257];
- if (num_extra) { drfs_mz_uint extra_bits; DRFS_TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; }
-
- DRFS_TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]);
- num_extra = s_dist_extra[dist]; dist = s_dist_base[dist];
- if (num_extra) { drfs_mz_uint extra_bits; DRFS_TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; }
-
- dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start;
- if ((dist > dist_from_out_buf_start) && (decomp_flags & DRFS_TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
- {
- DRFS_TINFL_CR_RETURN_FOREVER(37, DRFS_TINFL_STATUS_FAILED);
- }
-
- pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask);
-
- if ((DRFS_MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end)
- {
- while (counter--)
- {
- while (pOut_buf_cur >= pOut_buf_end) { DRFS_TINFL_CR_RETURN(53, DRFS_TINFL_STATUS_HAS_MORE_OUTPUT); }
- *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask];
- }
- continue;
- }
-#if DRFS_MINIZ_USE_UNALIGNED_LOADS_AND_STORES
- else if ((counter >= 9) && (counter <= dist))
- {
- const drfs_mz_uint8 *pSrc_end = pSrc + (counter & ~7);
- do
- {
- ((drfs_drfs_mz_uint32 *)pOut_buf_cur)[0] = ((const drfs_drfs_mz_uint32 *)pSrc)[0];
- ((drfs_drfs_mz_uint32 *)pOut_buf_cur)[1] = ((const drfs_drfs_mz_uint32 *)pSrc)[1];
- pOut_buf_cur += 8;
- } while ((pSrc += 8) < pSrc_end);
- if ((counter &= 7) < 3)
- {
- if (counter)
- {
- pOut_buf_cur[0] = pSrc[0];
- if (counter > 1)
- pOut_buf_cur[1] = pSrc[1];
- pOut_buf_cur += counter;
- }
- continue;
- }
- }
-#endif
- do
- {
- pOut_buf_cur[0] = pSrc[0];
- pOut_buf_cur[1] = pSrc[1];
- pOut_buf_cur[2] = pSrc[2];
- pOut_buf_cur += 3; pSrc += 3;
- } while ((int)(counter -= 3) > 2);
- if ((int)counter > 0)
- {
- pOut_buf_cur[0] = pSrc[0];
- if ((int)counter > 1)
- pOut_buf_cur[1] = pSrc[1];
- pOut_buf_cur += counter;
- }
- }
- }
- } while (!(r->m_final & 1));
- if (decomp_flags & DRFS_TINFL_FLAG_PARSE_ZLIB_HEADER)
- {
- DRFS_TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { drfs_mz_uint s; if (num_bits) DRFS_TINFL_GET_BITS(41, s, 8); else DRFS_TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; }
- }
- DRFS_TINFL_CR_RETURN_FOREVER(34, DRFS_TINFL_STATUS_DONE);
- DRFS_TINFL_CR_FINISH
-
-common_exit:
- r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start;
- *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next;
- if ((decomp_flags & (DRFS_TINFL_FLAG_PARSE_ZLIB_HEADER | DRFS_TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
- {
- const drfs_mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size;
- drfs_drfs_mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552;
- while (buf_len)
- {
- for (i = 0; i + 7 < block_len; i += 8, ptr += 8)
- {
- s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1;
- s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1;
- }
- for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
- s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
- }
- r->m_check_adler32 = (s2 << 16) + s1; if ((status == DRFS_TINFL_STATUS_DONE) && (decomp_flags & DRFS_TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = DRFS_TINFL_STATUS_ADLER32_MISMATCH;
- }
- return status;
-}
-
-// Higher level helper functions.
-void *drfs_tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
-{
- drfs_tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0;
- *pOut_len = 0;
- drfs_tinfl_init(&decomp);
- for ( ; ; )
- {
- size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity;
- drfs_tinfl_status status = drfs_tinfl_decompress(&decomp, (const drfs_mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (drfs_mz_uint8*)pBuf, pBuf ? (drfs_mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size,
- (flags & ~DRFS_TINFL_FLAG_HAS_MORE_INPUT) | DRFS_TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
- if ((status < 0) || (status == DRFS_TINFL_STATUS_NEEDS_MORE_INPUT))
- {
- DRFS_MZ_FREE(pBuf); *pOut_len = 0; return NULL;
- }
- src_buf_ofs += src_buf_size;
- *pOut_len += dst_buf_size;
- if (status == DRFS_TINFL_STATUS_DONE) break;
- new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128;
- pNew_buf = DRFS_MZ_REALLOC(pBuf, new_out_buf_capacity);
- if (!pNew_buf)
- {
- DRFS_MZ_FREE(pBuf); *pOut_len = 0; return NULL;
- }
- pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity;
- }
- return pBuf;
-}
-
-size_t drfs_tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
-{
- drfs_tinfl_decompressor decomp; drfs_tinfl_status status; drfs_tinfl_init(&decomp);
- status = drfs_tinfl_decompress(&decomp, (const drfs_mz_uint8*)pSrc_buf, &src_buf_len, (drfs_mz_uint8*)pOut_buf, (drfs_mz_uint8*)pOut_buf, &out_buf_len, (flags & ~DRFS_TINFL_FLAG_HAS_MORE_INPUT) | DRFS_TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
- return (status != DRFS_TINFL_STATUS_DONE) ? DRFS_TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len;
-}
-
-int drfs_tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
-{
- int result = 0;
- drfs_tinfl_decompressor decomp;
- drfs_mz_uint8 *pDict = (drfs_mz_uint8*)DRFS_MZ_MALLOC(DRFS_TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0;
- if (!pDict)
- return DRFS_TINFL_STATUS_FAILED;
- drfs_tinfl_init(&decomp);
- for ( ; ; )
- {
- size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = DRFS_TINFL_LZ_DICT_SIZE - dict_ofs;
- drfs_tinfl_status status = drfs_tinfl_decompress(&decomp, (const drfs_mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size,
- (flags & ~(DRFS_TINFL_FLAG_HAS_MORE_INPUT | DRFS_TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)));
- in_buf_ofs += in_buf_size;
- if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user)))
- break;
- if (status != DRFS_TINFL_STATUS_HAS_MORE_OUTPUT)
- {
- result = (status == DRFS_TINFL_STATUS_DONE);
- break;
- }
- dict_ofs = (dict_ofs + dst_buf_size) & (DRFS_TINFL_LZ_DICT_SIZE - 1);
- }
- DRFS_MZ_FREE(pDict);
- *pIn_buf_size = in_buf_ofs;
- return result;
-}
-
-
-// ------------------- .ZIP archive reading
-
-#ifndef DRFS_MINIZ_NO_ARCHIVE_APIS
-
-#define DRFS_MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c))
-
-// Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff.
-enum
-{
- // ZIP archive identifiers and record sizes
- DRFS_MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, DRFS_MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, DRFS_MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50,
- DRFS_MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, DRFS_MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, DRFS_MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22,
- // Central directory header record offsets
- DRFS_MZ_ZIP_CDH_SIG_OFS = 0, DRFS_MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, DRFS_MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, DRFS_MZ_ZIP_CDH_BIT_FLAG_OFS = 8,
- DRFS_MZ_ZIP_CDH_METHOD_OFS = 10, DRFS_MZ_ZIP_CDH_FILE_TIME_OFS = 12, DRFS_MZ_ZIP_CDH_FILE_DATE_OFS = 14, DRFS_MZ_ZIP_CDH_CRC32_OFS = 16,
- DRFS_MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, DRFS_MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, DRFS_MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, DRFS_MZ_ZIP_CDH_EXTRA_LEN_OFS = 30,
- DRFS_MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, DRFS_MZ_ZIP_CDH_DISK_START_OFS = 34, DRFS_MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, DRFS_MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, DRFS_MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42,
- // Local directory header offsets
- DRFS_MZ_ZIP_LDH_SIG_OFS = 0, DRFS_MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, DRFS_MZ_ZIP_LDH_BIT_FLAG_OFS = 6, DRFS_MZ_ZIP_LDH_METHOD_OFS = 8, DRFS_MZ_ZIP_LDH_FILE_TIME_OFS = 10,
- DRFS_MZ_ZIP_LDH_FILE_DATE_OFS = 12, DRFS_MZ_ZIP_LDH_CRC32_OFS = 14, DRFS_MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, DRFS_MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22,
- DRFS_MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, DRFS_MZ_ZIP_LDH_EXTRA_LEN_OFS = 28,
- // End of central directory offsets
- DRFS_MZ_ZIP_ECDH_SIG_OFS = 0, DRFS_MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, DRFS_MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, DRFS_MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8,
- DRFS_MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, DRFS_MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, DRFS_MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, DRFS_MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20,
-};
-
-typedef struct
-{
- void *m_p;
- size_t m_size, m_capacity;
- drfs_mz_uint m_element_size;
-} drfs_mz_zip_array;
-
-struct drfs_mz_zip_internal_state_tag
-{
- drfs_mz_zip_array m_central_dir;
- drfs_mz_zip_array m_central_dir_offsets;
- drfs_mz_zip_array m_sorted_central_dir_offsets;
- void* *m_pFile;
- void *m_pMem;
- size_t m_mem_size;
- size_t m_mem_capacity;
-};
-
-#define DRFS_MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size
-#define DRFS_MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index]
-
-static DRFS_MZ_FORCEINLINE void drfs_mz_zip_array_clear(drfs_mz_zip_archive *pZip, drfs_mz_zip_array *pArray)
-{
- pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p);
- memset(pArray, 0, sizeof(drfs_mz_zip_array));
-}
-
-static drfs_mz_bool drfs_mz_zip_array_ensure_capacity(drfs_mz_zip_archive *pZip, drfs_mz_zip_array *pArray, size_t min_new_capacity, drfs_mz_uint growing)
-{
- void *pNew_p; size_t new_capacity = min_new_capacity; DRFS_MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return DRFS_MZ_TRUE;
- if (growing) { new_capacity = DRFS_MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; }
- if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return DRFS_MZ_FALSE;
- pArray->m_p = pNew_p; pArray->m_capacity = new_capacity;
- return DRFS_MZ_TRUE;
-}
-
-static DRFS_MZ_FORCEINLINE drfs_mz_bool drfs_mz_zip_array_reserve(drfs_mz_zip_archive *pZip, drfs_mz_zip_array *pArray, size_t new_capacity, drfs_mz_uint growing)
-{
- if (new_capacity > pArray->m_capacity) { if (!drfs_mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return DRFS_MZ_FALSE; }
- return DRFS_MZ_TRUE;
-}
-
-static DRFS_MZ_FORCEINLINE drfs_mz_bool drfs_mz_zip_array_resize(drfs_mz_zip_archive *pZip, drfs_mz_zip_array *pArray, size_t new_size, drfs_mz_uint growing)
-{
- if (new_size > pArray->m_capacity) { if (!drfs_mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return DRFS_MZ_FALSE; }
- pArray->m_size = new_size;
- return DRFS_MZ_TRUE;
-}
-
-static DRFS_MZ_FORCEINLINE drfs_mz_bool drfs_mz_zip_array_ensure_room(drfs_mz_zip_archive *pZip, drfs_mz_zip_array *pArray, size_t n)
-{
- return drfs_mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, DRFS_MZ_TRUE);
-}
-
-static DRFS_MZ_FORCEINLINE drfs_mz_bool drfs_mz_zip_array_push_back(drfs_mz_zip_archive *pZip, drfs_mz_zip_array *pArray, const void *pElements, size_t n)
-{
- size_t orig_size = pArray->m_size; if (!drfs_mz_zip_array_resize(pZip, pArray, orig_size + n, DRFS_MZ_TRUE)) return DRFS_MZ_FALSE;
- memcpy((drfs_mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size);
- return DRFS_MZ_TRUE;
-}
-
-#ifndef DRFS_MINIZ_NO_TIME
-static time_t drfs_mz_zip_dos_to_time_t(int dos_time, int dos_date)
-{
- struct tm tm;
- memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1;
- tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31;
- tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62;
- return mktime(&tm);
-}
-#endif
-
-static drfs_mz_bool drfs_mz_zip_reader_init_internal(drfs_mz_zip_archive *pZip, drfs_drfs_mz_uint32 flags)
-{
- (void)flags;
- if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != DRFS_MZ_ZIP_MODE_INVALID))
- return DRFS_MZ_FALSE;
-
- if (!pZip->m_pAlloc) pZip->m_pAlloc = drfs__def_alloc_func;
- if (!pZip->m_pFree) pZip->m_pFree = drfs__def_free_func;
- if (!pZip->m_pRealloc) pZip->m_pRealloc = drfs__def_realloc_func;
-
- pZip->m_zip_mode = DRFS_MZ_ZIP_MODE_READING;
- pZip->m_archive_size = 0;
- pZip->m_central_directory_file_ofs = 0;
- pZip->m_total_files = 0;
-
- if (NULL == (pZip->m_pState = (drfs_mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(drfs_mz_zip_internal_state))))
- return DRFS_MZ_FALSE;
- memset(pZip->m_pState, 0, sizeof(drfs_mz_zip_internal_state));
- DRFS_MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(drfs_mz_uint8));
- DRFS_MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(drfs_drfs_mz_uint32));
- DRFS_MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(drfs_drfs_mz_uint32));
- return DRFS_MZ_TRUE;
-}
-
-static DRFS_MZ_FORCEINLINE drfs_mz_bool drfs_mz_zip_reader_filename_less(const drfs_mz_zip_array *pCentral_dir_array, const drfs_mz_zip_array *pCentral_dir_offsets, drfs_mz_uint l_index, drfs_mz_uint r_index)
-{
- const drfs_mz_uint8 *pL = &DRFS_MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, drfs_mz_uint8, DRFS_MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, drfs_drfs_mz_uint32, l_index)), *pE;
- const drfs_mz_uint8 *pR = &DRFS_MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, drfs_mz_uint8, DRFS_MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, drfs_drfs_mz_uint32, r_index));
- drfs_mz_uint l_len = DRFS_MZ_READ_LE16(pL + DRFS_MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = DRFS_MZ_READ_LE16(pR + DRFS_MZ_ZIP_CDH_FILENAME_LEN_OFS);
- drfs_mz_uint8 l = 0, r = 0;
- pL += DRFS_MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += DRFS_MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
- pE = pL + DRFS_MZ_MIN(l_len, r_len);
- while (pL < pE)
- {
- if ((l = DRFS_MZ_TOLOWER(*pL)) != (r = DRFS_MZ_TOLOWER(*pR)))
- break;
- pL++; pR++;
- }
- return (pL == pE) ? (l_len < r_len) : (l < r);
-}
-
-#define DRFS_MZ_SWAP_UINT32(a, b) do { drfs_drfs_mz_uint32 t = a; a = b; b = t; } DRFS_MZ_MACRO_END
-
-// Heap sort of lowercased filenames, used to help accelerate plain central directory searches by drfs_mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.)
-static void drfs_mz_zip_reader_sort_central_dir_offsets_by_filename(drfs_mz_zip_archive *pZip)
-{
- drfs_mz_zip_internal_state *pState = pZip->m_pState;
- const drfs_mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
- const drfs_mz_zip_array *pCentral_dir = &pState->m_central_dir;
- drfs_drfs_mz_uint32 *pIndices = &DRFS_MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, drfs_drfs_mz_uint32, 0);
- const int size = pZip->m_total_files;
- int start = (size - 2) >> 1, end;
- while (start >= 0)
- {
- int child, root = start;
- for ( ; ; )
- {
- if ((child = (root << 1) + 1) >= size)
- break;
- child += (((child + 1) < size) && (drfs_mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])));
- if (!drfs_mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
- break;
- DRFS_MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child;
- }
- start--;
- }
-
- end = size - 1;
- while (end > 0)
- {
- int child, root = 0;
- DRFS_MZ_SWAP_UINT32(pIndices[end], pIndices[0]);
- for ( ; ; )
- {
- if ((child = (root << 1) + 1) >= end)
- break;
- child += (((child + 1) < end) && drfs_mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]));
- if (!drfs_mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
- break;
- DRFS_MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child;
- }
- end--;
- }
-}
-
-static drfs_mz_bool drfs_mz_zip_reader_read_central_dir(drfs_mz_zip_archive *pZip, drfs_drfs_mz_uint32 flags)
-{
- drfs_mz_uint cdir_size, num_this_disk, cdir_disk_index;
- drfs_mz_uint64 cdir_ofs;
- drfs_mz_int64 cur_file_ofs;
- const drfs_mz_uint8 *p;
- drfs_drfs_mz_uint32 buf_u32[4096 / sizeof(drfs_drfs_mz_uint32)]; drfs_mz_uint8 *pBuf = (drfs_mz_uint8 *)buf_u32;
- drfs_mz_bool sort_central_dir = ((flags & DRFS_MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0);
- // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there.
- if (pZip->m_archive_size < DRFS_MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
- return DRFS_MZ_FALSE;
- // Find the end of central directory record by scanning the file from the end towards the beginning.
- cur_file_ofs = DRFS_MZ_MAX((drfs_mz_int64)pZip->m_archive_size - (drfs_mz_int64)sizeof(buf_u32), 0);
- for ( ; ; )
- {
- int i, n = (int)DRFS_MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs);
- if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (drfs_mz_uint)n)
- return DRFS_MZ_FALSE;
- for (i = n - 4; i >= 0; --i)
- if (DRFS_MZ_READ_LE32(pBuf + i) == DRFS_MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG)
- break;
- if (i >= 0)
- {
- cur_file_ofs += i;
- break;
- }
- if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + DRFS_MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)))
- return DRFS_MZ_FALSE;
- cur_file_ofs = DRFS_MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0);
- }
- // Read and verify the end of central directory record.
- if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, DRFS_MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != DRFS_MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
- return DRFS_MZ_FALSE;
- if ((DRFS_MZ_READ_LE32(pBuf + DRFS_MZ_ZIP_ECDH_SIG_OFS) != DRFS_MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) ||
- ((pZip->m_total_files = DRFS_MZ_READ_LE16(pBuf + DRFS_MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != DRFS_MZ_READ_LE16(pBuf + DRFS_MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS)))
- return DRFS_MZ_FALSE;
-
- num_this_disk = DRFS_MZ_READ_LE16(pBuf + DRFS_MZ_ZIP_ECDH_NUM_THIS_DISK_OFS);
- cdir_disk_index = DRFS_MZ_READ_LE16(pBuf + DRFS_MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS);
- if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1)))
- return DRFS_MZ_FALSE;
-
- if ((cdir_size = DRFS_MZ_READ_LE32(pBuf + DRFS_MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * DRFS_MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
- return DRFS_MZ_FALSE;
-
- cdir_ofs = DRFS_MZ_READ_LE32(pBuf + DRFS_MZ_ZIP_ECDH_CDIR_OFS_OFS);
- if ((cdir_ofs + (drfs_mz_uint64)cdir_size) > pZip->m_archive_size)
- return DRFS_MZ_FALSE;
-
- pZip->m_central_directory_file_ofs = cdir_ofs;
-
- if (pZip->m_total_files)
- {
- drfs_mz_uint i, n;
-
- // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices.
- if ((!drfs_mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, DRFS_MZ_FALSE)) ||
- (!drfs_mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, DRFS_MZ_FALSE)))
- return DRFS_MZ_FALSE;
-
- if (sort_central_dir)
- {
- if (!drfs_mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, DRFS_MZ_FALSE))
- return DRFS_MZ_FALSE;
- }
-
- if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size)
- return DRFS_MZ_FALSE;
-
- // Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported).
- p = (const drfs_mz_uint8 *)pZip->m_pState->m_central_dir.m_p;
- for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i)
- {
- drfs_mz_uint total_header_size, comp_size, decomp_size, disk_index;
- if ((n < DRFS_MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (DRFS_MZ_READ_LE32(p) != DRFS_MZ_ZIP_CENTRAL_DIR_HEADER_SIG))
- return DRFS_MZ_FALSE;
- DRFS_MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, drfs_drfs_mz_uint32, i) = (drfs_drfs_mz_uint32)(p - (const drfs_mz_uint8 *)pZip->m_pState->m_central_dir.m_p);
- if (sort_central_dir)
- DRFS_MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, drfs_drfs_mz_uint32, i) = i;
- comp_size = DRFS_MZ_READ_LE32(p + DRFS_MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
- decomp_size = DRFS_MZ_READ_LE32(p + DRFS_MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
- if (((!DRFS_MZ_READ_LE32(p + DRFS_MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF))
- return DRFS_MZ_FALSE;
- disk_index = DRFS_MZ_READ_LE16(p + DRFS_MZ_ZIP_CDH_DISK_START_OFS);
- if ((disk_index != num_this_disk) && (disk_index != 1))
- return DRFS_MZ_FALSE;
- if (((drfs_mz_uint64)DRFS_MZ_READ_LE32(p + DRFS_MZ_ZIP_CDH_LOCAL_HEADER_OFS) + DRFS_MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size)
- return DRFS_MZ_FALSE;
- if ((total_header_size = DRFS_MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + DRFS_MZ_READ_LE16(p + DRFS_MZ_ZIP_CDH_FILENAME_LEN_OFS) + DRFS_MZ_READ_LE16(p + DRFS_MZ_ZIP_CDH_EXTRA_LEN_OFS) + DRFS_MZ_READ_LE16(p + DRFS_MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n)
- return DRFS_MZ_FALSE;
- n -= total_header_size; p += total_header_size;
- }
- }
-
- if (sort_central_dir)
- drfs_mz_zip_reader_sort_central_dir_offsets_by_filename(pZip);
-
- return DRFS_MZ_TRUE;
-}
-
-drfs_mz_bool drfs_mz_zip_reader_init(drfs_mz_zip_archive *pZip, drfs_mz_uint64 size, drfs_drfs_mz_uint32 flags)
-{
- if ((!pZip) || (!pZip->m_pRead))
- return DRFS_MZ_FALSE;
- if (!drfs_mz_zip_reader_init_internal(pZip, flags))
- return DRFS_MZ_FALSE;
- pZip->m_archive_size = size;
- if (!drfs_mz_zip_reader_read_central_dir(pZip, flags))
- {
- drfs_mz_zip_reader_end(pZip);
- return DRFS_MZ_FALSE;
- }
- return DRFS_MZ_TRUE;
-}
-
-drfs_mz_uint drfs_mz_zip_reader_get_num_files(drfs_mz_zip_archive *pZip)
-{
- return pZip ? pZip->m_total_files : 0;
-}
-
-static DRFS_MZ_FORCEINLINE const drfs_mz_uint8 *drfs_mz_zip_reader_get_cdh(drfs_mz_zip_archive *pZip, drfs_mz_uint file_index)
-{
- if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != DRFS_MZ_ZIP_MODE_READING))
- return NULL;
- return &DRFS_MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, drfs_mz_uint8, DRFS_MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, drfs_drfs_mz_uint32, file_index));
-}
-
-drfs_mz_bool drfs_mz_zip_reader_is_file_a_directory(drfs_mz_zip_archive *pZip, drfs_mz_uint file_index)
-{
- drfs_mz_uint filename_len, external_attr;
- const drfs_mz_uint8 *p = drfs_mz_zip_reader_get_cdh(pZip, file_index);
- if (!p)
- return DRFS_MZ_FALSE;
-
- // First see if the filename ends with a '/' character.
- filename_len = DRFS_MZ_READ_LE16(p + DRFS_MZ_ZIP_CDH_FILENAME_LEN_OFS);
- if (filename_len)
- {
- if (*(p + DRFS_MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/')
- return DRFS_MZ_TRUE;
- }
-
- // Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct.
- // Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field.
- // FIXME: Remove this check? Is it necessary - we already check the filename.
- external_attr = DRFS_MZ_READ_LE32(p + DRFS_MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
- if ((external_attr & 0x10) != 0)
- return DRFS_MZ_TRUE;
-
- return DRFS_MZ_FALSE;
-}
-
-drfs_mz_bool drfs_mz_zip_reader_file_stat(drfs_mz_zip_archive *pZip, drfs_mz_uint file_index, drfs_drfs_mz_zip_archive_file_stat *pStat)
-{
- drfs_mz_uint n;
- const drfs_mz_uint8 *p = drfs_mz_zip_reader_get_cdh(pZip, file_index);
- if ((!p) || (!pStat))
- return DRFS_MZ_FALSE;
-
- // Unpack the central directory record.
- pStat->m_file_index = file_index;
- pStat->m_central_dir_ofs = DRFS_MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, drfs_drfs_mz_uint32, file_index);
- pStat->m_version_made_by = DRFS_MZ_READ_LE16(p + DRFS_MZ_ZIP_CDH_VERSION_MADE_BY_OFS);
- pStat->m_version_needed = DRFS_MZ_READ_LE16(p + DRFS_MZ_ZIP_CDH_VERSION_NEEDED_OFS);
- pStat->m_bit_flag = DRFS_MZ_READ_LE16(p + DRFS_MZ_ZIP_CDH_BIT_FLAG_OFS);
- pStat->m_method = DRFS_MZ_READ_LE16(p + DRFS_MZ_ZIP_CDH_METHOD_OFS);
-#ifndef DRFS_MINIZ_NO_TIME
- pStat->m_time = drfs_mz_zip_dos_to_time_t(DRFS_MZ_READ_LE16(p + DRFS_MZ_ZIP_CDH_FILE_TIME_OFS), DRFS_MZ_READ_LE16(p + DRFS_MZ_ZIP_CDH_FILE_DATE_OFS));
-#endif
- pStat->m_crc32 = DRFS_MZ_READ_LE32(p + DRFS_MZ_ZIP_CDH_CRC32_OFS);
- pStat->m_comp_size = DRFS_MZ_READ_LE32(p + DRFS_MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
- pStat->m_uncomp_size = DRFS_MZ_READ_LE32(p + DRFS_MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
- pStat->m_internal_attr = DRFS_MZ_READ_LE16(p + DRFS_MZ_ZIP_CDH_INTERNAL_ATTR_OFS);
- pStat->m_external_attr = DRFS_MZ_READ_LE32(p + DRFS_MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
- pStat->m_local_header_ofs = DRFS_MZ_READ_LE32(p + DRFS_MZ_ZIP_CDH_LOCAL_HEADER_OFS);
-
- // Copy as much of the filename and comment as possible.
- n = DRFS_MZ_READ_LE16(p + DRFS_MZ_ZIP_CDH_FILENAME_LEN_OFS); n = DRFS_MZ_MIN(n, DRFS_MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1);
- memcpy(pStat->m_filename, p + DRFS_MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0';
-
- n = DRFS_MZ_READ_LE16(p + DRFS_MZ_ZIP_CDH_COMMENT_LEN_OFS); n = DRFS_MZ_MIN(n, DRFS_MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1);
- pStat->m_comment_size = n;
- memcpy(pStat->m_comment, p + DRFS_MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + DRFS_MZ_READ_LE16(p + DRFS_MZ_ZIP_CDH_FILENAME_LEN_OFS) + DRFS_MZ_READ_LE16(p + DRFS_MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0';
-
- return DRFS_MZ_TRUE;
-}
-
-drfs_mz_uint drfs_mz_zip_reader_get_filename(drfs_mz_zip_archive *pZip, drfs_mz_uint file_index, char *pFilename, drfs_mz_uint filename_buf_size)
-{
- drfs_mz_uint n;
- const drfs_mz_uint8 *p = drfs_mz_zip_reader_get_cdh(pZip, file_index);
- if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; }
- n = DRFS_MZ_READ_LE16(p + DRFS_MZ_ZIP_CDH_FILENAME_LEN_OFS);
- if (filename_buf_size)
- {
- n = DRFS_MZ_MIN(n, filename_buf_size - 1);
- memcpy(pFilename, p + DRFS_MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n);
- pFilename[n] = '\0';
- }
- return n + 1;
-}
-
-static DRFS_MZ_FORCEINLINE drfs_mz_bool drfs_mz_zip_reader_string_equal(const char *pA, const char *pB, drfs_mz_uint len, drfs_mz_uint flags)
-{
- drfs_mz_uint i;
- if (flags & DRFS_MZ_ZIP_FLAG_CASE_SENSITIVE)
- return 0 == memcmp(pA, pB, len);
- for (i = 0; i < len; ++i)
- if (DRFS_MZ_TOLOWER(pA[i]) != DRFS_MZ_TOLOWER(pB[i]))
- return DRFS_MZ_FALSE;
- return DRFS_MZ_TRUE;
-}
-
-static DRFS_MZ_FORCEINLINE int drfs_mz_zip_reader_filename_compare(const drfs_mz_zip_array *pCentral_dir_array, const drfs_mz_zip_array *pCentral_dir_offsets, drfs_mz_uint l_index, const char *pR, drfs_mz_uint r_len)
-{
- const drfs_mz_uint8 *pL = &DRFS_MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, drfs_mz_uint8, DRFS_MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, drfs_drfs_mz_uint32, l_index)), *pE;
- drfs_mz_uint l_len = DRFS_MZ_READ_LE16(pL + DRFS_MZ_ZIP_CDH_FILENAME_LEN_OFS);
- drfs_mz_uint8 l = 0, r = 0;
- pL += DRFS_MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
- pE = pL + DRFS_MZ_MIN(l_len, r_len);
- while (pL < pE)
- {
- if ((l = DRFS_MZ_TOLOWER(*pL)) != (r = DRFS_MZ_TOLOWER(*pR)))
- break;
- pL++; pR++;
- }
- return (pL == pE) ? (int)(l_len - r_len) : (l - r);
-}
-
-static int drfs_mz_zip_reader_locate_file_binary_search(drfs_mz_zip_archive *pZip, const char *pFilename)
-{
- drfs_mz_zip_internal_state *pState = pZip->m_pState;
- const drfs_mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
- const drfs_mz_zip_array *pCentral_dir = &pState->m_central_dir;
- drfs_drfs_mz_uint32 *pIndices = &DRFS_MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, drfs_drfs_mz_uint32, 0);
- const int size = pZip->m_total_files;
- const drfs_mz_uint filename_len = (drfs_mz_uint)strlen(pFilename);
- int l = 0, h = size - 1;
- while (l <= h)
- {
- int m = (l + h) >> 1, file_index = pIndices[m], comp = drfs_mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len);
- if (!comp)
- return file_index;
- else if (comp < 0)
- l = m + 1;
- else
- h = m - 1;
- }
- return -1;
-}
-
-int drfs_mz_zip_reader_locate_file(drfs_mz_zip_archive *pZip, const char *pName, const char *pComment, drfs_mz_uint flags)
-{
- drfs_mz_uint file_index; size_t name_len, comment_len;
- if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != DRFS_MZ_ZIP_MODE_READING))
- return -1;
- if (((flags & (DRFS_MZ_ZIP_FLAG_IGNORE_PATH | DRFS_MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size))
- return drfs_mz_zip_reader_locate_file_binary_search(pZip, pName);
- name_len = strlen(pName); if (name_len > 0xFFFF) return -1;
- comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1;
- for (file_index = 0; file_index < pZip->m_total_files; file_index++)
- {
- const drfs_mz_uint8 *pHeader = &DRFS_MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, drfs_mz_uint8, DRFS_MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, drfs_drfs_mz_uint32, file_index));
- drfs_mz_uint filename_len = DRFS_MZ_READ_LE16(pHeader + DRFS_MZ_ZIP_CDH_FILENAME_LEN_OFS);
- const char *pFilename = (const char *)pHeader + DRFS_MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
- if (filename_len < name_len)
- continue;
- if (comment_len)
- {
- drfs_mz_uint file_extra_len = DRFS_MZ_READ_LE16(pHeader + DRFS_MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = DRFS_MZ_READ_LE16(pHeader + DRFS_MZ_ZIP_CDH_COMMENT_LEN_OFS);
- const char *pFile_comment = pFilename + filename_len + file_extra_len;
- if ((file_comment_len != comment_len) || (!drfs_mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags)))
- continue;
- }
- if ((flags & DRFS_MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len))
- {
- int ofs = filename_len - 1;
- do
- {
- if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':'))
- break;
- } while (--ofs >= 0);
- ofs++;
- pFilename += ofs; filename_len -= ofs;
- }
- if ((filename_len == name_len) && (drfs_mz_zip_reader_string_equal(pName, pFilename, filename_len, flags)))
- return file_index;
- }
- return -1;
-}
-
-drfs_mz_bool drfs_mz_zip_reader_extract_to_mem_no_alloc(drfs_mz_zip_archive *pZip, drfs_mz_uint file_index, void *pBuf, size_t buf_size, drfs_mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
-{
- int status = DRFS_TINFL_STATUS_DONE;
- drfs_mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail;
- drfs_drfs_mz_zip_archive_file_stat file_stat;
- void *pRead_buf;
- drfs_drfs_mz_uint32 local_header_u32[(DRFS_MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(drfs_drfs_mz_uint32) - 1) / sizeof(drfs_drfs_mz_uint32)]; drfs_mz_uint8 *pLocal_header = (drfs_mz_uint8 *)local_header_u32;
- drfs_tinfl_decompressor inflator;
-
- if ((buf_size) && (!pBuf))
- return DRFS_MZ_FALSE;
-
- if (!drfs_mz_zip_reader_file_stat(pZip, file_index, &file_stat))
- return DRFS_MZ_FALSE;
-
- // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes)
- if (!file_stat.m_comp_size)
- return DRFS_MZ_TRUE;
-
- // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers).
- // I'm torn how to handle this case - should it fail instead?
- if (drfs_mz_zip_reader_is_file_a_directory(pZip, file_index))
- return DRFS_MZ_TRUE;
-
- // Encryption and patch files are not supported.
- if (file_stat.m_bit_flag & (1 | 32))
- return DRFS_MZ_FALSE;
-
- // This function only supports stored and deflate.
- if ((!(flags & DRFS_MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != DRFS_MZ_DEFLATED))
- return DRFS_MZ_FALSE;
-
- // Ensure supplied output buffer is large enough.
- needed_size = (flags & DRFS_MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size;
- if (buf_size < needed_size)
- return DRFS_MZ_FALSE;
-
- // Read and parse the local directory entry.
- cur_file_ofs = file_stat.m_local_header_ofs;
- if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, DRFS_MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != DRFS_MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
- return DRFS_MZ_FALSE;
- if (DRFS_MZ_READ_LE32(pLocal_header) != DRFS_MZ_ZIP_LOCAL_DIR_HEADER_SIG)
- return DRFS_MZ_FALSE;
-
- cur_file_ofs += DRFS_MZ_ZIP_LOCAL_DIR_HEADER_SIZE + DRFS_MZ_READ_LE16(pLocal_header + DRFS_MZ_ZIP_LDH_FILENAME_LEN_OFS) + DRFS_MZ_READ_LE16(pLocal_header + DRFS_MZ_ZIP_LDH_EXTRA_LEN_OFS);
- if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)
- return DRFS_MZ_FALSE;
-
- if ((flags & DRFS_MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
- {
- // The file is stored or the caller has requested the compressed data.
- if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size)
- return DRFS_MZ_FALSE;
- return ((flags & DRFS_MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (drfs_mz_crc32(DRFS_MZ_CRC32_INIT, (const drfs_mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32);
- }
-
- // Decompress the file either directly from memory or from a file input buffer.
- drfs_tinfl_init(&inflator);
-
- if (pZip->m_pState->m_pMem)
- {
- // Read directly from the archive in memory.
- pRead_buf = (drfs_mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;
- read_buf_size = read_buf_avail = file_stat.m_comp_size;
- comp_remaining = 0;
- }
- else if (pUser_read_buf)
- {
- // Use a user provided read buffer.
- if (!user_read_buf_size)
- return DRFS_MZ_FALSE;
- pRead_buf = (drfs_mz_uint8 *)pUser_read_buf;
- read_buf_size = user_read_buf_size;
- read_buf_avail = 0;
- comp_remaining = file_stat.m_comp_size;
- }
- else
- {
- // Temporarily allocate a read buffer.
- read_buf_size = DRFS_MZ_MIN(file_stat.m_comp_size, DRFS_MZ_ZIP_MAX_IO_BUF_SIZE);
-#ifdef _MSC_VER
- if (((0, sizeof(size_t) == sizeof(drfs_drfs_mz_uint32))) && (read_buf_size > 0x7FFFFFFF))
-#else
- if (((sizeof(size_t) == sizeof(drfs_drfs_mz_uint32))) && (read_buf_size > 0x7FFFFFFF))
-#endif
- return DRFS_MZ_FALSE;
- if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))
- return DRFS_MZ_FALSE;
- read_buf_avail = 0;
- comp_remaining = file_stat.m_comp_size;
- }
-
- do
- {
- size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs);
- if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))
- {
- read_buf_avail = DRFS_MZ_MIN(read_buf_size, comp_remaining);
- if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
- {
- status = DRFS_TINFL_STATUS_FAILED;
- break;
- }
- cur_file_ofs += read_buf_avail;
- comp_remaining -= read_buf_avail;
- read_buf_ofs = 0;
- }
- in_buf_size = (size_t)read_buf_avail;
- status = drfs_tinfl_decompress(&inflator, (drfs_mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (drfs_mz_uint8 *)pBuf, (drfs_mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, DRFS_TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? DRFS_TINFL_FLAG_HAS_MORE_INPUT : 0));
- read_buf_avail -= in_buf_size;
- read_buf_ofs += in_buf_size;
- out_buf_ofs += out_buf_size;
- } while (status == DRFS_TINFL_STATUS_NEEDS_MORE_INPUT);
-
- if (status == DRFS_TINFL_STATUS_DONE)
- {
- // Make sure the entire file was decompressed, and check its CRC.
- if ((out_buf_ofs != file_stat.m_uncomp_size) || (drfs_mz_crc32(DRFS_MZ_CRC32_INIT, (const drfs_mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32))
- status = DRFS_TINFL_STATUS_FAILED;
- }
-
- if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf))
- pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
-
- return status == DRFS_TINFL_STATUS_DONE;
-}
-
-drfs_mz_bool drfs_mz_zip_reader_extract_file_to_mem_no_alloc(drfs_mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, drfs_mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
-{
- int file_index = drfs_mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
- if (file_index < 0)
- return DRFS_MZ_FALSE;
- return drfs_mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size);
-}
-
-drfs_mz_bool drfs_mz_zip_reader_extract_to_mem(drfs_mz_zip_archive *pZip, drfs_mz_uint file_index, void *pBuf, size_t buf_size, drfs_mz_uint flags)
-{
- return drfs_mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0);
-}
-
-drfs_mz_bool drfs_mz_zip_reader_extract_file_to_mem(drfs_mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, drfs_mz_uint flags)
-{
- return drfs_mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0);
-}
-
-void *drfs_mz_zip_reader_extract_to_heap(drfs_mz_zip_archive *pZip, drfs_mz_uint file_index, size_t *pSize, drfs_mz_uint flags)
-{
- drfs_mz_uint64 comp_size, uncomp_size, alloc_size;
- const drfs_mz_uint8 *p = drfs_mz_zip_reader_get_cdh(pZip, file_index);
- void *pBuf;
-
- if (pSize)
- *pSize = 0;
- if (!p)
- return NULL;
-
- comp_size = DRFS_MZ_READ_LE32(p + DRFS_MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
- uncomp_size = DRFS_MZ_READ_LE32(p + DRFS_MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
-
- alloc_size = (flags & DRFS_MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size;
-#ifdef _MSC_VER
- if (((0, sizeof(size_t) == sizeof(drfs_drfs_mz_uint32))) && (alloc_size > 0x7FFFFFFF))
-#else
- if (((sizeof(size_t) == sizeof(drfs_drfs_mz_uint32))) && (alloc_size > 0x7FFFFFFF))
-#endif
- return NULL;
- if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size)))
- return NULL;
-
- if (!drfs_mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags))
- {
- pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
- return NULL;
- }
-
- if (pSize) *pSize = (size_t)alloc_size;
- return pBuf;
-}
-
-drfs_mz_bool drfs_mz_zip_reader_end(drfs_mz_zip_archive *pZip)
-{
- if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != DRFS_MZ_ZIP_MODE_READING))
- return DRFS_MZ_FALSE;
-
- if (pZip->m_pState)
- {
- drfs_mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL;
- drfs_mz_zip_array_clear(pZip, &pState->m_central_dir);
- drfs_mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
- drfs_mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);
-
- pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
- }
- pZip->m_zip_mode = DRFS_MZ_ZIP_MODE_INVALID;
-
- return DRFS_MZ_TRUE;
-}
-
-#endif // #ifndef DRFS_MINIZ_NO_ARCHIVE_APIS
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // DRFS_MINIZ_HEADER_FILE_ONLY
-
-#if defined(__GNUC__)
- #pragma GCC diagnostic pop
-#endif
-#if defined(_MSC_VER)
- #pragma warning(pop)
-#endif
-
-
-
-
-typedef struct
-{
- // The current index of the iterator. When this hits the file count, the iteration is finished.
- unsigned int index;
-
- // The directory being iterated.
- char directoryPath[DRFS_MAX_PATH];
-
-}drfs_iterator_zip;
-
-typedef struct
-{
- // The file index within the archive.
- drfs_mz_uint index;
-
- // A pointer to the buffer containing the entire uncompressed data of the file. Unfortunately this is the only way I'm aware of for
- // reading file data from miniz.c so we'll just stick with it for now. We use a pointer to an 8-bit type so we can easily calculate
- // offsets.
- drfs_mz_uint8* pData;
-
- // The size of the file in bytes so we can guard against overflowing reads.
- size_t sizeInBytes;
-
- // The current position of the file's read pointer.
- size_t readPointer;
-
-}drfs_openedfile_zip;
-
-static size_t drfs_drfs_mz_file_read_func(void *pOpaque, drfs_mz_uint64 file_ofs, void *pBuf, size_t n)
-{
- // The opaque type is a pointer to a drfs_file object which represents the file of the archive.
- drfs_file* pZipFile = (drfs_file*)pOpaque;
- assert(pZipFile != NULL);
-
- if (!drfs_lock(pZipFile)) {
- return 0;
- }
-
- drfs_seek_nolock(pZipFile, (dr_int64)file_ofs, drfs_origin_start);
-
- size_t bytesRead;
- drfs_result result = drfs_read_nolock(pZipFile, pBuf, (unsigned int)n, &bytesRead);
- if (result != drfs_success) {
- // Failed to read the file.
- bytesRead = 0;
- }
-
- drfs_unlock(pZipFile);
- return (size_t)bytesRead;
-}
-
-
-static dr_bool32 drfs_is_valid_extension__zip(const char* extension)
-{
- return drfs__stricmp(extension, "zip") == 0;
-}
-
-static drfs_result drfs_open_archive__zip(drfs_file* pArchiveFile, unsigned int accessMode, drfs_handle* pHandleOut)
-{
- assert(pArchiveFile != NULL);
- assert(pHandleOut != NULL);
- assert(drfs_tell(pArchiveFile) == 0);
-
- *pHandleOut = NULL;
-
- // Only support read-only mode at the moment.
- if ((accessMode & DRFS_WRITE) != 0) {
- return drfs_permission_denied;
- }
-
-
- drfs_mz_zip_archive* pZip = (drfs_mz_zip_archive*)malloc(sizeof(drfs_mz_zip_archive));
- if (pZip == NULL) {
- return drfs_out_of_memory;
- }
-
- memset(pZip, 0, sizeof(drfs_mz_zip_archive));
-
- pZip->m_pRead = drfs_drfs_mz_file_read_func;
- pZip->m_pIO_opaque = pArchiveFile;
- if (!drfs_mz_zip_reader_init(pZip, drfs_size(pArchiveFile), 0)) {
- free(pZip);
- return drfs_invalid_archive;
- }
-
- *pHandleOut = pZip;
- return drfs_success;
-}
-
-static void drfs_close_archive__zip(drfs_handle archive)
-{
- assert(archive != NULL);
-
- drfs_mz_zip_reader_end((drfs_mz_zip_archive*)archive);
- free(archive);
-}
-
-static drfs_result drfs_get_file_info__zip(drfs_handle archive, const char* relativePath, drfs_file_info* fi)
-{
- assert(archive != NULL);
-
- drfs_mz_zip_archive* pZip = (drfs_mz_zip_archive*)archive;
- int fileIndex = drfs_mz_zip_reader_locate_file(pZip, relativePath, NULL, DRFS_MZ_ZIP_FLAG_CASE_SENSITIVE);
- if (fileIndex == -1)
- {
- // We failed to locate the file, but there's a chance it could actually be a folder. Here's the problem - folders
- // can be named such that they include a trailing slash. We'll want to check for that. Another problem is that
- // sometimes the folders won't actually be included in the central directory at all which means we need to do a
- // manual check across every file in the archive.
- char relativePathWithSlash[DRFS_MAX_PATH];
- drfs__strcpy_s(relativePathWithSlash, sizeof(relativePathWithSlash), relativePath);
- drfs__strcat_s(relativePathWithSlash, sizeof(relativePathWithSlash), "/");
- fileIndex = drfs_mz_zip_reader_locate_file(pZip, relativePath, NULL, DRFS_MZ_ZIP_FLAG_CASE_SENSITIVE);
- if (fileIndex == -1)
- {
- // We still couldn't find the directory even with the trailing slash. There's a chace it's a folder that's
- // simply not included in the central directory. It's appears the "Send to -> Compressed (zipped) folder"
- // functionality in Windows does this.
- drfs_mz_uint numFiles = drfs_mz_zip_reader_get_num_files(pZip);
- for (drfs_mz_uint iFile = 0; iFile < numFiles; ++iFile)
- {
- char filePath[DRFS_MAX_PATH];
- if (drfs_mz_zip_reader_get_filename(pZip, iFile, filePath, DRFS_MAX_PATH) > 0)
- {
- if (drfs_drpath_is_child(filePath, relativePath))
- {
- // This file is within a folder with a path of relativePath which means we can imply that relativePath
- // is a folder.
- drfs__strcpy_s(fi->absolutePath, sizeof(fi->absolutePath), relativePath);
- fi->sizeInBytes = 0;
- fi->lastModifiedTime = 0;
- fi->attributes = DRFS_FILE_ATTRIBUTE_READONLY | DRFS_FILE_ATTRIBUTE_DIRECTORY;
-
- return drfs_success;
- }
- }
- }
-
- return drfs_does_not_exist;
- }
- }
-
- assert(fileIndex != -1);
-
- if (fi != NULL)
- {
- drfs_drfs_mz_zip_archive_file_stat zipStat;
- if (drfs_mz_zip_reader_file_stat(pZip, (drfs_mz_uint)fileIndex, &zipStat))
- {
- drfs__strcpy_s(fi->absolutePath, sizeof(fi->absolutePath), relativePath);
- fi->sizeInBytes = zipStat.m_uncomp_size;
- fi->lastModifiedTime = (dr_uint64)zipStat.m_time;
- fi->attributes = DRFS_FILE_ATTRIBUTE_READONLY;
- if (drfs_mz_zip_reader_is_file_a_directory(pZip, (drfs_mz_uint)fileIndex)) {
- fi->attributes |= DRFS_FILE_ATTRIBUTE_DIRECTORY;
- }
-
- return drfs_success;
- }
- }
-
- return drfs_success;
-}
-
-static drfs_handle drfs_begin_iteration__zip(drfs_handle archive, const char* relativePath)
-{
- assert(relativePath != NULL);
-
- drfs_mz_zip_archive* pZip = (drfs_mz_zip_archive*)archive;
- assert(pZip != NULL);
-
- int directoryFileIndex = -1;
- if (relativePath[0] == '\0') {
- directoryFileIndex = 0;
- } else {
- directoryFileIndex = drfs_mz_zip_reader_locate_file(pZip, relativePath, NULL, DRFS_MZ_ZIP_FLAG_CASE_SENSITIVE);
- }
-
- if (directoryFileIndex == -1)
- {
- // The same issue applies here as documented in drfs_get_file_info__zip().
- char relativePathWithSlash[DRFS_MAX_PATH];
- drfs__strcpy_s(relativePathWithSlash, sizeof(relativePathWithSlash), relativePath);
- drfs__strcat_s(relativePathWithSlash, sizeof(relativePathWithSlash), "/");
- directoryFileIndex = drfs_mz_zip_reader_locate_file(pZip, relativePath, NULL, DRFS_MZ_ZIP_FLAG_CASE_SENSITIVE);
- if (directoryFileIndex == -1)
- {
- // We still couldn't find the directory even with the trailing slash. There's a chace it's a folder that's
- // simply not included in the central directory. It's appears the "Send to -> Compressed (zipped) folder"
- // functionality in Windows does this.
- drfs_mz_uint numFiles = drfs_mz_zip_reader_get_num_files(pZip);
- for (drfs_mz_uint iFile = 0; iFile < numFiles; ++iFile)
- {
- char filePath[DRFS_MAX_PATH];
- if (drfs_mz_zip_reader_get_filename(pZip, iFile, filePath, DRFS_MAX_PATH) > 0)
- {
- if (drfs_drpath_is_child(filePath, relativePath))
- {
- // This file is within a folder with a path of relativePath which means we can imply that relativePath
- // is a folder.
- goto on_success;
- }
- }
- }
-
- return NULL;
- }
- }
-
-
-
-on_success:;
- drfs_iterator_zip* pZipIterator = (drfs_iterator_zip*)malloc(sizeof(drfs_iterator_zip));
- if (pZipIterator != NULL)
- {
- pZipIterator->index = 0;
- drfs__strcpy_s(pZipIterator->directoryPath, sizeof(pZipIterator->directoryPath), relativePath);
- }
-
- return pZipIterator;
-}
-
-static void drfs_end_iteration__zip(drfs_handle archive, drfs_handle iterator)
-{
- (void)archive;
- assert(archive != NULL);
- assert(iterator != NULL);
-
- free(iterator);
-}
-
-static dr_bool32 drfs_next_iteration__zip(drfs_handle archive, drfs_handle iterator, drfs_file_info* fi)
-{
- (void)archive;
- assert(archive != NULL);
- assert(iterator != NULL);
-
- drfs_iterator_zip* pZipIterator = (drfs_iterator_zip*)iterator;
- if (pZipIterator == NULL) {
- return DR_FALSE;
- }
-
- drfs_mz_zip_archive* pZip = (drfs_mz_zip_archive*)archive;
- while (pZipIterator->index < drfs_mz_zip_reader_get_num_files(pZip))
- {
- unsigned int iFile = pZipIterator->index++;
-
- char filePath[DRFS_MAX_PATH];
- if (drfs_mz_zip_reader_get_filename(pZip, iFile, filePath, DRFS_MAX_PATH) > 0)
- {
- if (drfs_drpath_is_child(filePath, pZipIterator->directoryPath))
- {
- if (fi != NULL)
- {
- drfs_drfs_mz_zip_archive_file_stat zipStat;
- if (drfs_mz_zip_reader_file_stat(pZip, iFile, &zipStat))
- {
- drfs__strcpy_s(fi->absolutePath, sizeof(fi->absolutePath), filePath);
- fi->sizeInBytes = zipStat.m_uncomp_size;
- fi->lastModifiedTime = (dr_uint64)zipStat.m_time;
- fi->attributes = DRFS_FILE_ATTRIBUTE_READONLY;
- if (drfs_mz_zip_reader_is_file_a_directory(pZip, iFile)) {
- fi->attributes |= DRFS_FILE_ATTRIBUTE_DIRECTORY;
- }
-
- // If we have a directory we need to ensure we don't have a trailing slash.
- if ((fi->attributes & DRFS_FILE_ATTRIBUTE_DIRECTORY) != 0) {
- size_t absolutePathLen = strlen(fi->absolutePath);
- if (absolutePathLen > 0 && (fi->absolutePath[absolutePathLen - 1] == '/' || fi->absolutePath[absolutePathLen - 1] == '\\')) {
- fi->absolutePath[absolutePathLen - 1] = '\0';
- }
- }
- }
- }
-
- return DR_TRUE;
- }
- }
- }
-
- return DR_FALSE;
-}
-
-static drfs_result drfs_open_file__zip(drfs_handle archive, const char* relativePath, unsigned int accessMode, drfs_handle* pHandleOut)
-{
- assert(archive != NULL);
- assert(pHandleOut != NULL);
- assert(relativePath != NULL);
-
- *pHandleOut = NULL;
-
- // Only supporting read-only for now.
- if ((accessMode & DRFS_WRITE) != 0) {
- return drfs_permission_denied;
- }
-
-
- drfs_mz_zip_archive* pZip = (drfs_mz_zip_archive*)archive;
- int fileIndex = drfs_mz_zip_reader_locate_file(pZip, relativePath, NULL, DRFS_MZ_ZIP_FLAG_CASE_SENSITIVE);
- if (fileIndex == -1) {
- return drfs_does_not_exist;
- }
-
- drfs_openedfile_zip* pOpenedFile = (drfs_openedfile_zip*)malloc(sizeof(*pOpenedFile));
- if (pOpenedFile == NULL) {
- return drfs_out_of_memory;
- }
-
- pOpenedFile->pData = (drfs_mz_uint8*)drfs_mz_zip_reader_extract_to_heap(pZip, (drfs_mz_uint)fileIndex, &pOpenedFile->sizeInBytes, 0);
- if (pOpenedFile->pData == NULL) {
- free(pOpenedFile);
- return drfs_unknown_error;
- }
-
- pOpenedFile->index = (drfs_mz_uint)fileIndex;
- pOpenedFile->readPointer = 0;
-
- *pHandleOut = pOpenedFile;
- return drfs_success;
-}
-
-static void drfs_close_file__zip(drfs_handle archive, drfs_handle file)
-{
- drfs_openedfile_zip* pOpenedFile = (drfs_openedfile_zip*)file;
- assert(pOpenedFile != NULL);
-
- drfs_mz_zip_archive* pZip = (drfs_mz_zip_archive*)archive;
- assert(pZip != NULL);
-
- pZip->m_pFree(pZip->m_pAlloc_opaque, pOpenedFile->pData);
- free(pOpenedFile);
-}
-
-static drfs_result drfs_read_file__zip(drfs_handle archive, drfs_handle file, void* pDataOut, size_t bytesToRead, size_t* pBytesReadOut)
-{
- (void)archive;
- assert(archive != NULL);
- assert(file != NULL);
- assert(pDataOut != NULL);
- assert(bytesToRead > 0);
-
- drfs_openedfile_zip* pOpenedFile = (drfs_openedfile_zip*)file;
- if (pOpenedFile == NULL) {
- return drfs_invalid_args;
- }
-
- size_t bytesAvailable = pOpenedFile->sizeInBytes - pOpenedFile->readPointer;
- if (bytesAvailable < bytesToRead) {
- bytesToRead = bytesAvailable;
- }
-
- if (bytesToRead == 0) {
- return drfs_at_end_of_file; // Nothing left to read.
- }
-
-
- memcpy(pDataOut, pOpenedFile->pData + pOpenedFile->readPointer, bytesToRead);
- pOpenedFile->readPointer += bytesToRead;
-
- if (pBytesReadOut) {
- *pBytesReadOut = bytesToRead;
- }
-
- return drfs_success;
-}
-
-static drfs_result drfs_write_file__zip(drfs_handle archive, drfs_handle file, const void* pData, size_t bytesToWrite, size_t* pBytesWrittenOut)
-{
- (void)archive;
- (void)file;
- (void)pData;
- (void)bytesToWrite;
-
- assert(archive != NULL);
- assert(file != NULL);
- assert(pData != NULL);
- assert(bytesToWrite > 0);
-
- // All files are read-only for now.
- if (pBytesWrittenOut) {
- *pBytesWrittenOut = 0;
- }
-
- return drfs_permission_denied;
-}
-
-static drfs_result drfs_seek_file__zip(drfs_handle archive, drfs_handle file, dr_int64 bytesToSeek, drfs_seek_origin origin)
-{
- (void)archive;
-
- assert(archive != NULL);
- assert(file != NULL);
-
- drfs_openedfile_zip* pOpenedFile = (drfs_openedfile_zip*)file;
- if (pOpenedFile == NULL) {
- return drfs_invalid_args;
- }
-
- dr_uint64 newPos = pOpenedFile->readPointer;
- if (origin == drfs_origin_current)
- {
- if ((dr_int64)newPos + bytesToSeek >= 0)
- {
- newPos = (dr_uint64)((dr_int64)newPos + bytesToSeek);
- }
- else
- {
- // Trying to seek to before the beginning of the file.
- return drfs_invalid_args;
- }
- }
- else if (origin == drfs_origin_start)
- {
- assert(bytesToSeek >= 0);
- newPos = (dr_uint64)bytesToSeek;
- }
- else if (origin == drfs_origin_end)
- {
- assert(bytesToSeek >= 0);
- if ((dr_uint64)bytesToSeek <= pOpenedFile->sizeInBytes)
- {
- newPos = pOpenedFile->sizeInBytes - (dr_uint64)bytesToSeek;
- }
- else
- {
- // Trying to seek to before the beginning of the file.
- return drfs_invalid_args;
- }
- }
- else
- {
- // Should never get here.
- return drfs_unknown_error;
- }
-
-
- if (newPos > pOpenedFile->sizeInBytes) {
- return drfs_invalid_args;
- }
-
- pOpenedFile->readPointer = (size_t)newPos;
- return drfs_success;
-}
-
-static dr_uint64 drfs_tell_file__zip(drfs_handle archive, drfs_handle file)
-{
- (void)archive;
-
- drfs_openedfile_zip* pOpenedFile = (drfs_openedfile_zip*)file;
- assert(pOpenedFile != NULL);
-
- return pOpenedFile->readPointer;
-}
-
-static dr_uint64 drfs_file_size__zip(drfs_handle archive, drfs_handle file)
-{
- (void)archive;
-
- drfs_openedfile_zip* pOpenedFile = (drfs_openedfile_zip*)file;
- assert(pOpenedFile != NULL);
-
- return pOpenedFile->sizeInBytes;
-}
-
-static void drfs_flush__zip(drfs_handle archive, drfs_handle file)
-{
- (void)archive;
- (void)file;
-
- assert(archive != NULL);
- assert(file != NULL);
-
- // All files are read-only for now.
-}
-
-
-static void drfs_register_zip_backend(drfs_context* pContext)
-{
- if (pContext == NULL) {
- return;
- }
-
- drfs_archive_callbacks callbacks;
- callbacks.is_valid_extension = drfs_is_valid_extension__zip;
- callbacks.open_archive = drfs_open_archive__zip;
- callbacks.close_archive = drfs_close_archive__zip;
- callbacks.get_file_info = drfs_get_file_info__zip;
- callbacks.begin_iteration = drfs_begin_iteration__zip;
- callbacks.end_iteration = drfs_end_iteration__zip;
- callbacks.next_iteration = drfs_next_iteration__zip;
- callbacks.delete_file = NULL;
- callbacks.move_file = NULL;
- callbacks.create_directory = NULL;
- callbacks.copy_file = NULL;
- callbacks.open_file = drfs_open_file__zip;
- callbacks.close_file = drfs_close_file__zip;
- callbacks.read_file = drfs_read_file__zip;
- callbacks.write_file = drfs_write_file__zip;
- callbacks.seek_file = drfs_seek_file__zip;
- callbacks.tell_file = drfs_tell_file__zip;
- callbacks.file_size = drfs_file_size__zip;
- callbacks.flush_file = drfs_flush__zip;
- drfs_register_archive_backend(pContext, callbacks);
-}
-#endif //DR_FS_NO_ZIP
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// Quake 2 PAK
-//
-///////////////////////////////////////////////////////////////////////////////
-#ifndef DR_FS_NO_PAK
-typedef struct
-{
- char path[64];
-
-}drfs_path_pak;
-
-typedef struct
-{
- // The file name.
- char name[56];
-
- // The position within the file of the first byte of the file.
- unsigned int offset;
-
- // The size of the file, in bytes.
- unsigned int sizeInBytes;
-
-}drfs_file_pak;
-
-typedef struct
-{
- // A pointer to the archive file for reading data.
- drfs_file* pArchiveFile;
-
-
- // The 4-byte identifiers: "PACK"
- char id[4];
-
- // The offset of the directory.
- unsigned int directoryOffset;
-
- // The size of the directory. This should a multiple of 64.
- unsigned int directoryLength;
-
-
- // The access mode.
- unsigned int accessMode;
-
- // A pointer to the buffer containing the file information. The number of items in this array is equal to directoryLength / 64.
- drfs_file_pak* pFiles;
-
-}drfs_archive_pak;
-
-
-typedef struct
-{
- // The current index of the iterator. When this hits childCount, the iteration is finished.
- unsigned int index;
-
- // The directory being iterated.
- char directoryPath[DRFS_MAX_PATH];
-
-
- // The number of directories that have previously been iterated.
- unsigned int processedDirCount;
-
- // The directories that were previously iterated.
- drfs_path_pak* pProcessedDirs;
-
-}drfs_iterator_pak;
-
-static dr_bool32 drfs_iterator_pak_append_processed_dir(drfs_iterator_pak* pIterator, const char* path)
-{
- if (pIterator != NULL && path != NULL)
- {
- drfs_path_pak* pOldBuffer = pIterator->pProcessedDirs;
- drfs_path_pak* pNewBuffer = (drfs_path_pak*)malloc(sizeof(drfs_path_pak) * (pIterator->processedDirCount + 1));
-
- if (pNewBuffer != 0)
- {
- for (unsigned int iDst = 0; iDst < pIterator->processedDirCount; ++iDst)
- {
- pNewBuffer[iDst] = pOldBuffer[iDst];
- }
-
- drfs__strcpy_s(pNewBuffer[pIterator->processedDirCount].path, 64, path);
-
-
- pIterator->pProcessedDirs = pNewBuffer;
- pIterator->processedDirCount += 1;
-
- drfs_free(pOldBuffer);
-
- return 1;
- }
- }
-
- return 0;
-}
-
-static dr_bool32 drfs_iterator_pak_has_dir_been_processed(drfs_iterator_pak* pIterator, const char* path)
-{
- for (unsigned int i = 0; i < pIterator->processedDirCount; ++i)
- {
- if (strcmp(path, pIterator->pProcessedDirs[i].path) == 0)
- {
- return 1;
- }
- }
-
- return 0;
-}
-
-
-typedef struct
-{
- // The offset of the first byte of the file's data.
- size_t offsetInArchive;
-
- // The size of the file in bytes so we can guard against overflowing reads.
- size_t sizeInBytes;
-
- // The current position of the file's read pointer.
- size_t readPointer;
-
-}drfs_openedfile_pak;
-
-
-
-static drfs_archive_pak* drfs_pak_create(drfs_file* pArchiveFile, unsigned int accessMode)
-{
- drfs_archive_pak* pak = (drfs_archive_pak*)malloc(sizeof(*pak));
- if (pak != NULL)
- {
- pak->pArchiveFile = pArchiveFile;
- pak->directoryOffset = 0;
- pak->directoryLength = 0;
- pak->accessMode = accessMode;
- pak->pFiles = NULL;
- }
-
- return pak;
-}
-
-static void drfs_pak_delete(drfs_archive_pak* pArchive)
-{
- free(pArchive->pFiles);
- free(pArchive);
-}
-
-
-
-
-static dr_bool32 drfs_is_valid_extension__pak(const char* extension)
-{
- return drfs__stricmp(extension, "pak") == 0;
-}
-
-
-static drfs_result drfs_open_archive__pak(drfs_file* pArchiveFile, unsigned int accessMode, drfs_handle* pHandleOut)
-{
- assert(pArchiveFile != NULL);
- assert(pHandleOut != NULL);
- assert(drfs_tell(pArchiveFile) == 0);
-
- *pHandleOut = NULL;
-
- if (!drfs_lock(pArchiveFile)) {
- return drfs_unknown_error;
- }
-
- drfs_result result = drfs_success;
- drfs_archive_pak* pak = drfs_pak_create(pArchiveFile, accessMode);
- if (pak != NULL)
- {
- // First 4 bytes should equal "PACK"
- if (drfs_read_nolock(pArchiveFile, pak->id, 4, NULL) == drfs_success)
- {
- if (pak->id[0] == 'P' && pak->id[1] == 'A' && pak->id[2] == 'C' && pak->id[3] == 'K')
- {
- if (drfs_read_nolock(pArchiveFile, &pak->directoryOffset, 4, NULL) == drfs_success)
- {
- if (drfs_read_nolock(pArchiveFile, &pak->directoryLength, 4, NULL) == drfs_success)
- {
- // We loaded the header just fine so now we want to allocate space for each file in the directory and load them. Note that
- // this does not load the file data itself, just information about the files like their name and size.
- if (pak->directoryLength % 64 == 0)
- {
- unsigned int fileCount = pak->directoryLength / 64;
- if (fileCount > 0)
- {
- assert((sizeof(drfs_file_pak) * fileCount) == pak->directoryLength);
-
- pak->pFiles = (drfs_file_pak*)malloc(pak->directoryLength);
- if (pak->pFiles != NULL)
- {
- // Seek to the directory listing before trying to read it.
- if (drfs_seek_nolock(pArchiveFile, pak->directoryOffset, drfs_origin_start) == drfs_success)
- {
- size_t bytesRead;
- if (drfs_read_nolock(pArchiveFile, pak->pFiles, pak->directoryLength, &bytesRead) == drfs_success && bytesRead == pak->directoryLength)
- {
- // All good!
- }
- else
- {
- // Failed to read the directory listing.
- drfs_pak_delete(pak);
- pak = NULL;
- result = drfs_invalid_archive;
- }
- }
- else
- {
- // Failed to seek to the directory listing.
- drfs_pak_delete(pak);
- pak = NULL;
- result = drfs_invalid_archive;
- }
- }
- else
- {
- // Failed to allocate memory for the file info buffer.
- drfs_pak_delete(pak);
- pak = NULL;
- result = drfs_out_of_memory;
- }
- }
- }
- else
- {
- // The directory length is not a multiple of 64 - something is wrong with the file.
- drfs_pak_delete(pak);
- pak = NULL;
- result = drfs_invalid_archive;
- }
- }
- else
- {
- // Failed to read the directory length.
- drfs_pak_delete(pak);
- pak = NULL;
- result = drfs_invalid_archive;
- }
- }
- else
- {
- // Failed to read the directory offset.
- drfs_pak_delete(pak);
- pak = NULL;
- result = drfs_invalid_archive;
- }
- }
- else
- {
- // Not a pak file.
- drfs_pak_delete(pak);
- pak = NULL;
- result = drfs_invalid_archive;
- }
- }
- else
- {
- // Failed to read the header.
- drfs_pak_delete(pak);
- pak = NULL;
- result = drfs_invalid_archive;
- }
- }
-
- drfs_unlock(pArchiveFile);
- *pHandleOut = pak;
- return result;
-}
-
-static void drfs_close_archive__pak(drfs_handle archive)
-{
- drfs_archive_pak* pPak = (drfs_archive_pak*)archive;
- assert(pPak != NULL);
-
- drfs_pak_delete(pPak);
-}
-
-static drfs_result drfs_get_file_info__pak(drfs_handle archive, const char* relativePath, drfs_file_info* fi)
-{
- // We can determine whether or not the path refers to a file or folder by checking it the path is parent of any
- // files in the archive. If so, it's a folder, otherwise it's a file (so long as it exists).
- drfs_archive_pak* pak = (drfs_archive_pak*)archive;
- assert(pak != NULL);
-
- unsigned int fileCount = pak->directoryLength / 64;
- for (unsigned int i = 0; i < fileCount; ++i)
- {
- drfs_file_pak* pFile = pak->pFiles + i;
- if (strcmp(pFile->name, relativePath) == 0)
- {
- // It's a file.
- drfs__strcpy_s(fi->absolutePath, sizeof(fi->absolutePath), relativePath);
- fi->sizeInBytes = (dr_uint64)pFile->sizeInBytes;
- fi->lastModifiedTime = 0;
- fi->attributes = DRFS_FILE_ATTRIBUTE_READONLY;
-
- return drfs_success;
- }
- else if (drfs_drpath_is_descendant(pFile->name, relativePath))
- {
- // It's a directory.
- drfs__strcpy_s(fi->absolutePath, sizeof(fi->absolutePath), relativePath);
- fi->sizeInBytes = 0;
- fi->lastModifiedTime = 0;
- fi->attributes = DRFS_FILE_ATTRIBUTE_READONLY | DRFS_FILE_ATTRIBUTE_DIRECTORY;
-
- return drfs_success;
- }
- }
-
- return drfs_does_not_exist;
-}
-
-static drfs_handle drfs_begin_iteration__pak(drfs_handle archive, const char* relativePath)
-{
- (void)archive;
- assert(relativePath != NULL);
-
- drfs_iterator_pak* pIterator = (drfs_iterator_pak*)malloc(sizeof(drfs_iterator_pak));
- if (pIterator != NULL)
- {
- pIterator->index = 0;
- drfs__strcpy_s(pIterator->directoryPath, sizeof(pIterator->directoryPath), relativePath);
- pIterator->processedDirCount = 0;
- pIterator->pProcessedDirs = NULL;
- }
-
- return pIterator;
-}
-
-static void drfs_end_iteration__pak(drfs_handle archive, drfs_handle iterator)
-{
- (void)archive;
-
- drfs_iterator_pak* pIterator = (drfs_iterator_pak*)iterator;
- assert(pIterator != NULL);
-
- free(pIterator);
-}
-
-static dr_bool32 drfs_next_iteration__pak(drfs_handle archive, drfs_handle iterator, drfs_file_info* fi)
-{
- drfs_iterator_pak* pIterator = (drfs_iterator_pak*)iterator;
- assert(pIterator != NULL);
-
- drfs_archive_pak* pak = (drfs_archive_pak*)archive;
- assert(pak != NULL);
-
- unsigned int fileCount = pak->directoryLength / 64;
- while (pIterator->index < fileCount)
- {
- unsigned int iFile = pIterator->index++;
-
- drfs_file_pak* pFile = pak->pFiles + iFile;
- if (drfs_drpath_is_child(pFile->name, pIterator->directoryPath))
- {
- // It's a file.
- drfs__strcpy_s(fi->absolutePath, DRFS_MAX_PATH, pFile->name);
- fi->sizeInBytes = (dr_uint64)pFile->sizeInBytes;
- fi->lastModifiedTime = 0;
- fi->attributes = DRFS_FILE_ATTRIBUTE_READONLY;
-
- return DR_TRUE;
- }
- else if (drfs_drpath_is_descendant(pFile->name, pIterator->directoryPath))
- {
- // It's a directory. This needs special handling because we don't want to iterate over the same directory multiple times.
- const char* childDirEnd = pFile->name + strlen(pIterator->directoryPath) + 1; // +1 for the slash.
- while (childDirEnd[0] != '\0' && childDirEnd[0] != '/' && childDirEnd[0] != '\\')
- {
- childDirEnd += 1;
- }
-
- char childDir[64];
- memcpy(childDir, pFile->name, childDirEnd - pFile->name);
- childDir[childDirEnd - pFile->name] = '\0';
-
- if (!drfs_iterator_pak_has_dir_been_processed(pIterator, childDir))
- {
- // It's a directory.
- drfs__strcpy_s(fi->absolutePath, DRFS_MAX_PATH, childDir);
- fi->sizeInBytes = 0;
- fi->lastModifiedTime = 0;
- fi->attributes = DRFS_FILE_ATTRIBUTE_READONLY | DRFS_FILE_ATTRIBUTE_DIRECTORY;
-
- drfs_iterator_pak_append_processed_dir(pIterator, childDir);
-
- return DR_TRUE;
- }
- }
- }
-
- return DR_FALSE;
-}
-
-static drfs_result drfs_open_file__pak(drfs_handle archive, const char* relativePath, unsigned int accessMode, drfs_handle* pHandleOut)
-{
- assert(relativePath != NULL);
- assert(pHandleOut != NULL);
-
- // Only supporting read-only for now.
- if ((accessMode & DRFS_WRITE) != 0) {
- return drfs_permission_denied;
- }
-
-
- drfs_archive_pak* pak = (drfs_archive_pak*)archive;
- assert(pak != NULL);
-
- for (unsigned int iFile = 0; iFile < (pak->directoryLength / 64); ++iFile)
- {
- if (strcmp(relativePath, pak->pFiles[iFile].name) == 0)
- {
- // We found the file.
- drfs_openedfile_pak* pOpenedFile = (drfs_openedfile_pak*)malloc(sizeof(*pOpenedFile));
- if (pOpenedFile != NULL)
- {
- pOpenedFile->offsetInArchive = pak->pFiles[iFile].offset;
- pOpenedFile->sizeInBytes = pak->pFiles[iFile].sizeInBytes;
- pOpenedFile->readPointer = 0;
-
- *pHandleOut = pOpenedFile;
- return drfs_success;
- }
- }
- }
-
-
- return drfs_does_not_exist;
-}
-
-static void drfs_close_file__pak(drfs_handle archive, drfs_handle file)
-{
- (void)archive;
-
- drfs_openedfile_pak* pOpenedFile = (drfs_openedfile_pak*)file;
- assert(pOpenedFile != NULL);
-
- free(pOpenedFile);
-}
-
-static drfs_result drfs_read_file__pak(drfs_handle archive, drfs_handle file, void* pDataOut, size_t bytesToRead, size_t* pBytesReadOut)
-{
- assert(pDataOut != NULL);
- assert(bytesToRead > 0);
-
- drfs_archive_pak* pak = (drfs_archive_pak*)archive;
- assert(pak != NULL);
-
- drfs_openedfile_pak* pOpenedFile = (drfs_openedfile_pak*)file;
- assert(pOpenedFile != NULL);
-
- // The read pointer should never go past the file size.
- assert(pOpenedFile->sizeInBytes >= pOpenedFile->readPointer);
-
-
- size_t bytesAvailable = pOpenedFile->sizeInBytes - pOpenedFile->readPointer;
- if (bytesAvailable < bytesToRead) {
- bytesToRead = bytesAvailable; // Safe cast, as per the check above.
- }
-
- if (!drfs_lock(pak->pArchiveFile)) {
- return drfs_unknown_error;
- }
-
- drfs_seek_nolock(pak->pArchiveFile, (dr_int64)(pOpenedFile->offsetInArchive + pOpenedFile->readPointer), drfs_origin_start);
- drfs_result result = drfs_read_nolock(pak->pArchiveFile, pDataOut, bytesToRead, pBytesReadOut);
- if (result == drfs_success) {
- pOpenedFile->readPointer += bytesToRead;
- }
-
- drfs_unlock(pak->pArchiveFile);
- return result;
-}
-
-static drfs_result drfs_write_file__pak(drfs_handle archive, drfs_handle file, const void* pData, size_t bytesToWrite, size_t* pBytesWrittenOut)
-{
- (void)archive;
- (void)file;
- (void)pData;
- (void)bytesToWrite;
-
- assert(archive != NULL);
- assert(file != NULL);
- assert(pData != NULL);
- assert(bytesToWrite > 0);
-
- // All files are read-only for now.
- if (pBytesWrittenOut) {
- *pBytesWrittenOut = 0;
- }
-
- return drfs_permission_denied;
-}
-
-static drfs_result drfs_seek_file__pak(drfs_handle archive, drfs_handle file, dr_int64 bytesToSeek, drfs_seek_origin origin)
-{
- (void)archive;
-
- drfs_openedfile_pak* pOpenedFile = (drfs_openedfile_pak*)file;
- assert(pOpenedFile != NULL);
-
- dr_uint64 newPos = pOpenedFile->readPointer;
- if (origin == drfs_origin_current) {
- if ((dr_int64)newPos + bytesToSeek >= 0) {
- newPos = (dr_uint64)((dr_int64)newPos + bytesToSeek);
- } else {
- return drfs_invalid_args; // Trying to seek to before the beginning of the file.
- }
- } else if (origin == drfs_origin_start) {
- assert(bytesToSeek >= 0);
- newPos = (dr_uint64)bytesToSeek;
- } else if (origin == drfs_origin_end) {
- assert(bytesToSeek >= 0);
- if ((dr_uint64)bytesToSeek <= pOpenedFile->sizeInBytes) {
- newPos = pOpenedFile->sizeInBytes - (dr_uint64)bytesToSeek;
- } else {
- return drfs_invalid_args; // Trying to seek to before the beginning of the file.
- }
- } else {
- return drfs_unknown_error; // Should never get here.
- }
-
-
- if (newPos > pOpenedFile->sizeInBytes) {
- return drfs_invalid_args;
- }
-
- pOpenedFile->readPointer = (size_t)newPos;
- return drfs_success;
-}
-
-static dr_uint64 drfs_tell_file__pak(drfs_handle archive, drfs_handle file)
-{
- (void)archive;
-
- drfs_openedfile_pak* pOpenedFile = (drfs_openedfile_pak*)file;
- assert(pOpenedFile != NULL);
-
- return pOpenedFile->readPointer;
-}
-
-static dr_uint64 drfs_file_size__pak(drfs_handle archive, drfs_handle file)
-{
- (void)archive;
-
- drfs_openedfile_pak* pOpenedFile = (drfs_openedfile_pak*)file;
- assert(pOpenedFile != NULL);
-
- return pOpenedFile->sizeInBytes;
-}
-
-static void drfs_flush__pak(drfs_handle archive, drfs_handle file)
-{
- (void)archive;
- (void)file;
-
- assert(archive != NULL);
- assert(file != NULL);
-
- // All files are read-only for now.
-}
-
-static void drfs_register_pak_backend(drfs_context* pContext)
-{
- if (pContext == NULL) {
- return;
- }
-
- drfs_archive_callbacks callbacks;
- callbacks.is_valid_extension = drfs_is_valid_extension__pak;
- callbacks.open_archive = drfs_open_archive__pak;
- callbacks.close_archive = drfs_close_archive__pak;
- callbacks.get_file_info = drfs_get_file_info__pak;
- callbacks.begin_iteration = drfs_begin_iteration__pak;
- callbacks.end_iteration = drfs_end_iteration__pak;
- callbacks.next_iteration = drfs_next_iteration__pak;
- callbacks.delete_file = NULL;
- callbacks.move_file = NULL;
- callbacks.create_directory = NULL;
- callbacks.copy_file = NULL;
- callbacks.open_file = drfs_open_file__pak;
- callbacks.close_file = drfs_close_file__pak;
- callbacks.read_file = drfs_read_file__pak;
- callbacks.write_file = drfs_write_file__pak;
- callbacks.seek_file = drfs_seek_file__pak;
- callbacks.tell_file = drfs_tell_file__pak;
- callbacks.file_size = drfs_file_size__pak;
- callbacks.flush_file = drfs_flush__pak;
- drfs_register_archive_backend(pContext, callbacks);
-}
-#endif //DR_FS_NO_PAK
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// Wavefront MTL
-//
-///////////////////////////////////////////////////////////////////////////////
-#ifndef DR_FS_NO_MTL
-typedef struct
-{
- // The byte offset within the archive
- dr_uint64 offset;
-
- // The size of the file, in bytes.
- dr_uint64 sizeInBytes;
-
- // The name of the material. The specification says this can be any length, but we're going to clamp it to 255 + null terminator which should be fine.
- char name[256];
-
-}drfs_file_mtl;
-
-typedef struct
-{
- // A pointer to the archive's file so we can read data.
- drfs_file* pArchiveFile;
-
- // The access mode.
- unsigned int accessMode;
-
- // The buffer containing the list of files.
- drfs_file_mtl* pFiles;
-
- // The number of files in the archive.
- unsigned int fileCount;
-
-}drfs_archive_mtl;
-
-typedef struct
-{
- // The current index of the iterator. When this hits the file count, the iteration is finished.
- unsigned int index;
-
-}drfs_iterator_mtl;
-
-typedef struct
-{
- // The offset within the archive file the first byte of the file is located.
- dr_uint64 offsetInArchive;
-
- // The size of the file in bytes so we can guard against overflowing reads.
- dr_uint64 sizeInBytes;
-
- // The current position of the file's read pointer.
- dr_uint64 readPointer;
-
-}drfs_openedfile_mtl;
-
-
-static drfs_archive_mtl* drfs_mtl_create(drfs_file* pArchiveFile, unsigned int accessMode)
-{
- drfs_archive_mtl* mtl = (drfs_archive_mtl*)malloc(sizeof(drfs_archive_mtl));
- if (mtl != NULL)
- {
- mtl->pArchiveFile = pArchiveFile;
- mtl->accessMode = accessMode;
- mtl->pFiles = NULL;
- mtl->fileCount = 0;
- }
-
- return mtl;
-}
-
-static void drfs_mtl_delete(drfs_archive_mtl* pArchive)
-{
- free(pArchive->pFiles);
- free(pArchive);
-}
-
-static void drfs_mtl_addfile(drfs_archive_mtl* pArchive, drfs_file_mtl* pFile)
-{
- if (pArchive != NULL && pFile != NULL)
- {
- drfs_file_mtl* pOldBuffer = pArchive->pFiles;
- drfs_file_mtl* pNewBuffer = (drfs_file_mtl*)malloc(sizeof(drfs_file_mtl) * (pArchive->fileCount + 1));
-
- if (pNewBuffer != 0)
- {
- for (unsigned int iDst = 0; iDst < pArchive->fileCount; ++iDst) {
- pNewBuffer[iDst] = pOldBuffer[iDst];
- }
-
- pNewBuffer[pArchive->fileCount] = *pFile;
-
- pArchive->pFiles = pNewBuffer;
- pArchive->fileCount += 1;
-
- free(pOldBuffer);
- }
- }
-}
-
-
-typedef struct
-{
- dr_uint64 archiveSizeInBytes;
- dr_uint64 bytesRemaining;
- drfs_file* pFile;
- char* chunkPointer;
- char* chunkEnd;
- char chunk[4096];
- unsigned int chunkSize;
-
-}drfs_openarchive_mtl_state;
-
-static dr_bool32 drfs_mtl_loadnextchunk(drfs_openarchive_mtl_state* pState)
-{
- assert(pState != NULL);
-
- if (pState->bytesRemaining > 0)
- {
- pState->chunkSize = (pState->bytesRemaining > 4096) ? 4096 : (unsigned int)pState->bytesRemaining;
- assert(pState->chunkSize);
-
- if (drfs_read_nolock(pState->pFile, pState->chunk, pState->chunkSize, NULL) == drfs_success)
- {
- pState->bytesRemaining -= pState->chunkSize;
- pState->chunkPointer = pState->chunk;
- pState->chunkEnd = pState->chunk + pState->chunkSize;
-
- return DR_TRUE;
- }
- else
- {
- // An error occured while reading. Just reset everything to make it look like an error occured.
- pState->bytesRemaining = 0;
- pState->chunkSize = 0;
- pState->chunkPointer = pState->chunk;
- pState->chunkEnd = pState->chunkPointer;
- }
- }
-
- return DR_FALSE;
-}
-
-static dr_bool32 drfs_mtl_loadnewmtl(drfs_openarchive_mtl_state* pState)
-{
- assert(pState != NULL);
-
- const char newmtl[7] = "newmtl";
- for (unsigned int i = 0; i < 6; ++i)
- {
- // Check if we need a new chunk.
- if (pState->chunkPointer >= pState->chunkEnd)
- {
- if (!drfs_mtl_loadnextchunk(pState))
- {
- return DR_FALSE;
- }
- }
-
-
- if (pState->chunkPointer[0] != newmtl[i])
- {
- return DR_FALSE;
- }
-
- pState->chunkPointer += 1;
- }
-
- // At this point the first 6 characters equal "newmtl".
- return DR_TRUE;
-}
-
-static dr_bool32 drfs_mtl_skipline(drfs_openarchive_mtl_state* pState)
-{
- assert(pState != NULL);
-
- // Keep looping until we find a new line character.
- while (pState->chunkPointer < pState->chunkEnd)
- {
- if (pState->chunkPointer[0] == '\n')
- {
- // Found the new line. Now move forward by one to get past the new line character.
- pState->chunkPointer += 1;
- if (pState->chunkPointer >= pState->chunkEnd)
- {
- return drfs_mtl_loadnextchunk(pState);
- }
-
- return DR_TRUE;
- }
-
- pState->chunkPointer += 1;
- }
-
- // If we get here it means we got past the end of the chunk. We just read the next chunk and call this recursively.
- if (drfs_mtl_loadnextchunk(pState))
- {
- return drfs_mtl_skipline(pState);
- }
-
- return DR_FALSE;
-}
-
-static dr_bool32 drfs_mtl_skipwhitespace(drfs_openarchive_mtl_state* pState)
-{
- assert(pState != NULL);
-
- while (pState->chunkPointer < pState->chunkEnd)
- {
- const char c = pState->chunkPointer[0];
- if (c != ' ' && c != '\t' && c != '\r' && c != '\n')
- {
- return DR_TRUE;
- }
-
- pState->chunkPointer += 1;
- }
-
- if (drfs_mtl_loadnextchunk(pState))
- {
- return drfs_mtl_skipwhitespace(pState);
- }
-
- return DR_FALSE;
-}
-
-static dr_bool32 drfs_mtl_loadmtlname(drfs_openarchive_mtl_state* pState, void* dst, unsigned int dstSizeInBytes)
-{
- assert(pState != NULL);
-
- // We loop over character by character until we find a whitespace, "#" character or the end of the file.
- char* dst8 = (char*)dst;
- while (dstSizeInBytes > 0 && pState->chunkPointer < pState->chunkEnd)
- {
- const char c = pState->chunkPointer[0];
- if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '#')
- {
- // We've found the end of the name. Null terminate and return.
- *dst8 = '\0';
- return DR_TRUE;
- }
- else
- {
- *dst8++ = c;
- dstSizeInBytes -= 1;
- pState->chunkPointer += 1;
- }
- }
-
- // At this point we either ran out of space in the destination buffer or the chunk.
- if (dstSizeInBytes > 0)
- {
- // We got to the end of the chunk, so we need to load the next chunk and call this recursively.
- assert(pState->chunkPointer == pState->chunkEnd);
-
- if (drfs_mtl_loadnextchunk(pState))
- {
- return drfs_mtl_loadmtlname(pState, dst8, dstSizeInBytes);
- }
- else
- {
- // We reached the end of the file, but the name may be valid.
- return DR_TRUE;
- }
- }
- else
- {
- // We ran out of room in the buffer.
- return DR_FALSE;
- }
-}
-
-
-static dr_bool32 drfs_is_valid_extension__mtl(const char* extension)
-{
- return drfs__stricmp(extension, "mtl") == 0;
-}
-
-static drfs_result drfs_open_archive__mtl(drfs_file* pArchiveFile, unsigned int accessMode, drfs_handle* pHandleOut)
-{
- assert(pArchiveFile != NULL);
- assert(pHandleOut != NULL);
- assert(drfs_tell(pArchiveFile) == 0);
-
- *pHandleOut = NULL;
-
- if (!drfs_lock(pArchiveFile)) {
- return drfs_unknown_error;
- }
-
- drfs_archive_mtl* mtl = drfs_mtl_create(pArchiveFile, accessMode);
- if (mtl == NULL) {
- return drfs_invalid_archive;
- }
-
- drfs_result result = drfs_success;
-
- // We create a state object that is used to help us with chunk management.
- drfs_openarchive_mtl_state state;
- state.pFile = pArchiveFile;
- state.archiveSizeInBytes = drfs_size_nolock(pArchiveFile);
- state.bytesRemaining = state.archiveSizeInBytes;
- state.chunkSize = 0;
- state.chunkPointer = state.chunk;
- state.chunkEnd = state.chunk;
- if (drfs_mtl_loadnextchunk(&state))
- {
- while (state.bytesRemaining > 0 || state.chunkPointer < state.chunkEnd)
- {
- ptrdiff_t bytesRemainingInChunk = state.chunkEnd - state.chunkPointer;
- assert(bytesRemainingInChunk > 0);
-
- dr_uint64 newmtlOffset = state.archiveSizeInBytes - state.bytesRemaining - ((dr_uint64)bytesRemainingInChunk);
-
- if (drfs_mtl_loadnewmtl(&state))
- {
- if (state.chunkPointer[0] == ' ' || state.chunkPointer[0] == '\t')
- {
- // We found a new material. We need to iterate until we hit the first whitespace, "#", or the end of the file.
- if (drfs_mtl_skipwhitespace(&state))
- {
- drfs_file_mtl file;
- if (drfs_mtl_loadmtlname(&state, file.name, 256))
- {
- // Everything worked out. We now need to create the file and add it to our list. At this point we won't know the size. We determine
- // the size in a post-processing step later.
- file.offset = newmtlOffset;
- drfs_mtl_addfile(mtl, &file);
- }
- }
- }
- }
-
- // Move to the next line.
- drfs_mtl_skipline(&state);
- }
-
-
- // The files will have been read at this point, but we need to do a post-processing step to retrieve the size of each file.
- for (unsigned int iFile = 0; iFile < mtl->fileCount; ++iFile)
- {
- if (iFile < mtl->fileCount - 1)
- {
- // It's not the last item. The size of this item is the offset of the next file minus the offset of this file.
- mtl->pFiles[iFile].sizeInBytes = mtl->pFiles[iFile + 1].offset - mtl->pFiles[iFile].offset;
- }
- else
- {
- // It's the last item. The size of this item is the size of the archive file minus the file's offset.
- mtl->pFiles[iFile].sizeInBytes = state.archiveSizeInBytes - mtl->pFiles[iFile].offset;
- }
- }
- }
- else
- {
- drfs_mtl_delete(mtl);
- mtl = NULL;
- result = drfs_invalid_archive;
- }
-
- drfs_unlock(pArchiveFile);
- *pHandleOut = mtl;
- return result;
-}
-
-static void drfs_close_archive__mtl(drfs_handle archive)
-{
- drfs_archive_mtl* mtl = (drfs_archive_mtl*)archive;
- assert(mtl != NULL);
-
- drfs_mtl_delete(mtl);
-}
-
-static drfs_result drfs_get_file_info__mtl(drfs_handle archive, const char* relativePath, drfs_file_info* fi)
-{
- drfs_archive_mtl* mtl = (drfs_archive_mtl*)archive;
- assert(mtl != NULL);
-
- for (unsigned int iFile = 0; iFile < mtl->fileCount; ++iFile)
- {
- if (strcmp(relativePath, mtl->pFiles[iFile].name) == 0)
- {
- // We found the file.
- if (fi != NULL)
- {
- drfs__strcpy_s(fi->absolutePath, sizeof(fi->absolutePath), relativePath);
- fi->sizeInBytes = mtl->pFiles[iFile].sizeInBytes;
- fi->lastModifiedTime = 0;
- fi->attributes = DRFS_FILE_ATTRIBUTE_READONLY;
- }
-
- return drfs_success;
- }
- }
-
- return drfs_does_not_exist;
-}
-
-static drfs_handle drfs_begin_iteration__mtl(drfs_handle archive, const char* relativePath)
-{
- assert(relativePath != NULL);
-
- drfs_archive_mtl* mtl = (drfs_archive_mtl*)archive;
- assert(mtl != NULL);
-
- if (mtl->fileCount > 0)
- {
- if (relativePath[0] == '\0' || (relativePath[0] == '/' && relativePath[1] == '\0')) // This is a flat archive, so no sub-folders.
- {
- drfs_iterator_mtl* pIterator = (drfs_iterator_mtl*)malloc(sizeof(*pIterator));
- if (pIterator != NULL)
- {
- pIterator->index = 0;
- return pIterator;
- }
- }
- }
-
- return NULL;
-}
-
-static void drfs_end_iteration__mtl(drfs_handle archive, drfs_handle iterator)
-{
- (void)archive;
-
- drfs_iterator_mtl* pIterator = (drfs_iterator_mtl*)iterator;
- free(pIterator);
-}
-
-static dr_bool32 drfs_next_iteration__mtl(drfs_handle archive, drfs_handle iterator, drfs_file_info* fi)
-{
- drfs_archive_mtl* mtl = (drfs_archive_mtl*)archive;
- assert(mtl != NULL);
-
- drfs_iterator_mtl* pIterator = (drfs_iterator_mtl*)iterator;
- assert(pIterator != NULL);
-
- if (pIterator->index < mtl->fileCount)
- {
- if (fi != NULL)
- {
- drfs__strcpy_s(fi->absolutePath, DRFS_MAX_PATH, mtl->pFiles[pIterator->index].name);
- fi->sizeInBytes = mtl->pFiles[pIterator->index].sizeInBytes;
- fi->lastModifiedTime = 0;
- fi->attributes = DRFS_FILE_ATTRIBUTE_READONLY;
- }
-
- pIterator->index += 1;
- return DR_TRUE;
- }
-
- return DR_FALSE;
-}
-
-static drfs_result drfs_open_file__mtl(drfs_handle archive, const char* relativePath, unsigned int accessMode, drfs_handle* pHandleOut)
-{
- assert(relativePath != NULL);
- assert(pHandleOut != NULL);
-
- *pHandleOut = NULL;
-
- // Only supporting read-only for now.
- if ((accessMode & DRFS_WRITE) != 0) {
- return drfs_permission_denied;
- }
-
- drfs_archive_mtl* mtl = (drfs_archive_mtl*)archive;
- assert(mtl != NULL);
-
- for (unsigned int iFile = 0; iFile < mtl->fileCount; ++iFile)
- {
- if (strcmp(relativePath, mtl->pFiles[iFile].name) == 0)
- {
- // We found the file.
- drfs_openedfile_mtl* pOpenedFile = (drfs_openedfile_mtl*)malloc(sizeof(drfs_openedfile_mtl));
- if (pOpenedFile != NULL)
- {
- pOpenedFile->offsetInArchive = mtl->pFiles[iFile].offset;
- pOpenedFile->sizeInBytes = mtl->pFiles[iFile].sizeInBytes;
- pOpenedFile->readPointer = 0;
-
- *pHandleOut = pOpenedFile;
- return drfs_success;
- }
- }
- }
-
- return drfs_does_not_exist;
-}
-
-static void drfs_close_file__mtl(drfs_handle archive, drfs_handle file)
-{
- (void)archive;
-
- drfs_openedfile_mtl* pOpenedFile = (drfs_openedfile_mtl*)file;
- assert(pOpenedFile != NULL);
-
- free(pOpenedFile);
-}
-
-static drfs_result drfs_read_file__mtl(drfs_handle archive, drfs_handle file, void* pDataOut, size_t bytesToRead, size_t* pBytesReadOut)
-{
- assert(pDataOut != NULL);
- assert(bytesToRead > 0);
-
- drfs_archive_mtl* mtl = (drfs_archive_mtl*)archive;
- assert(mtl != NULL);
-
- drfs_openedfile_mtl* pOpenedFile = (drfs_openedfile_mtl*)file;
- assert(pOpenedFile != NULL);
-
- // The read pointer should never go past the file size.
- assert(pOpenedFile->sizeInBytes >= pOpenedFile->readPointer);
-
- dr_uint64 bytesAvailable = pOpenedFile->sizeInBytes - pOpenedFile->readPointer;
- if (bytesAvailable < bytesToRead) {
- bytesToRead = (size_t)bytesAvailable; // Safe cast, as per the check above.
- }
-
- if (!drfs_lock(mtl->pArchiveFile)) {
- return drfs_unknown_error;
- }
-
- drfs_seek_nolock(mtl->pArchiveFile, (dr_int64)(pOpenedFile->offsetInArchive + pOpenedFile->readPointer), drfs_origin_start);
- drfs_result result = drfs_read_nolock(mtl->pArchiveFile, pDataOut, bytesToRead, pBytesReadOut);
- if (result == drfs_success) {
- pOpenedFile->readPointer += bytesToRead;
- }
-
- return result;
-}
-
-static drfs_result drfs_write_file__mtl(drfs_handle archive, drfs_handle file, const void* pData, size_t bytesToWrite, size_t* pBytesWrittenOut)
-{
- (void)archive;
- (void)file;
- (void)pData;
- (void)bytesToWrite;
-
- assert(archive != NULL);
- assert(file != NULL);
- assert(pData != NULL);
- assert(bytesToWrite > 0);
-
- // All files are read-only for now.
- if (pBytesWrittenOut) {
- *pBytesWrittenOut = 0;
- }
-
- return drfs_success;
-}
-
-static drfs_result drfs_seek_file__mtl(drfs_handle archive, drfs_handle file, dr_int64 bytesToSeek, drfs_seek_origin origin)
-{
- (void)archive;
-
- drfs_openedfile_mtl* pOpenedFile = (drfs_openedfile_mtl*)file;
- assert(pOpenedFile != NULL);
-
- dr_uint64 newPos = pOpenedFile->readPointer;
- if (origin == drfs_origin_current) {
- if ((dr_int64)newPos + bytesToSeek >= 0) {
- newPos = (dr_uint64)((dr_int64)newPos + bytesToSeek);
- } else {
- return drfs_invalid_args; // Trying to seek to before the beginning of the file.
- }
- } else if (origin == drfs_origin_start) {
- assert(bytesToSeek >= 0);
- newPos = (dr_uint64)bytesToSeek;
- } else if (origin == drfs_origin_end) {
- assert(bytesToSeek >= 0);
- if ((dr_uint64)bytesToSeek <= pOpenedFile->sizeInBytes) {
- newPos = pOpenedFile->sizeInBytes - (dr_uint64)bytesToSeek;
- } else {
- return drfs_invalid_args; // Trying to seek to before the beginning of the file.
- }
- } else {
- return drfs_unknown_error; // Should never get here.
- }
-
-
- if (newPos > pOpenedFile->sizeInBytes) {
- return drfs_invalid_args;
- }
-
- pOpenedFile->readPointer = newPos;
- return drfs_success;
-}
-
-static dr_uint64 drfs_tell_file__mtl(drfs_handle archive, drfs_handle file)
-{
- (void)archive;
-
- drfs_openedfile_mtl* pOpenedFile = (drfs_openedfile_mtl*)file;
- assert(pOpenedFile != NULL);
-
- return pOpenedFile->readPointer;
-}
-
-static dr_uint64 drfs_file_size__mtl(drfs_handle archive, drfs_handle file)
-{
- (void)archive;
-
- drfs_openedfile_mtl* pOpenedFile = (drfs_openedfile_mtl*)file;
- assert(pOpenedFile != NULL);
-
- return pOpenedFile->sizeInBytes;
-}
-
-static void drfs_flush__mtl(drfs_handle archive, drfs_handle file)
-{
- (void)archive;
- (void)file;
-
- assert(archive != NULL);
- assert(file != NULL);
-
- // All files are read-only for now.
-}
-
-
-static void drfs_register_mtl_backend(drfs_context* pContext)
-{
- if (pContext == NULL) {
- return;
- }
-
- drfs_archive_callbacks callbacks;
- callbacks.is_valid_extension = drfs_is_valid_extension__mtl;
- callbacks.open_archive = drfs_open_archive__mtl;
- callbacks.close_archive = drfs_close_archive__mtl;
- callbacks.get_file_info = drfs_get_file_info__mtl;
- callbacks.begin_iteration = drfs_begin_iteration__mtl;
- callbacks.end_iteration = drfs_end_iteration__mtl;
- callbacks.next_iteration = drfs_next_iteration__mtl;
- callbacks.delete_file = NULL;
- callbacks.move_file = NULL;
- callbacks.create_directory = NULL;
- callbacks.copy_file = NULL;
- callbacks.open_file = drfs_open_file__mtl;
- callbacks.close_file = drfs_close_file__mtl;
- callbacks.read_file = drfs_read_file__mtl;
- callbacks.write_file = drfs_write_file__mtl;
- callbacks.seek_file = drfs_seek_file__mtl;
- callbacks.tell_file = drfs_tell_file__mtl;
- callbacks.file_size = drfs_file_size__mtl;
- callbacks.flush_file = drfs_flush__mtl;
- drfs_register_archive_backend(pContext, callbacks);
-}
-#endif //DR_FS_NO_MTL
-
-
-
-#endif //DR_FS_IMPLEMENTATION
-
-/*
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-For more information, please refer to
-*/
diff --git a/source/engine/thirdparty/dr_libs/old/dr_fsw.h b/source/engine/thirdparty/dr_libs/old/dr_fsw.h
deleted file mode 100644
index 830e7fc..0000000
--- a/source/engine/thirdparty/dr_libs/old/dr_fsw.h
+++ /dev/null
@@ -1,1625 +0,0 @@
-// Public Domain. See "unlicense" statement at the end of this file.
-
-// ABOUT
-//
-// dr_fsw is a simple library for watching for changes to the file system. This is not a full-featured library
-// and is only intended for basic use cases.
-//
-// Limitations:
-// - Only Windows is supported at the moment.
-//
-//
-//
-// USAGE
-//
-// This is a single-file library. To use it, do something like the following in one .c file.
-// #define DR_FSW_IMPLEMENTATION
-// #include "dr_fsw.h"
-//
-// You can then #include this file in other parts of the program as you would with any other header file.
-//
-// Example:
-// // Startup.
-// drfsw_context* context = drfsw_create_context();
-// if (context == NULL)
-// {
-// // There was an error creating the context...
-// }
-//
-// drfsw_add_directory(context, "C:/My/Folder");
-// drfsw_add_directory(context, "C:/My/Other/Folder");
-//
-// ...
-//
-// // Meanwhile, in another thread...
-// void MyOtherThreadEntryProc()
-// {
-// drfsw_event e;
-// while (isMyApplicationStillAlive && drfsw_next_event(context, &e))
-// {
-// switch (e.type)
-// {
-// case drfsw_event_type_created: OnFileCreated(e.absolutePath); break;
-// case drfsw_event_type_deleted: OnFileDeleted(e.absolutePath); break;
-// case drfsw_event_type_renamed: OnFileRenamed(e.absolutePath, e.absolutePathNew); break;
-// case drfsw_event_type_updated: OnFileUpdated(e.absolutePath); break;
-// default: break;
-// }
-// }
-// }
-//
-// ...
-//
-// // Shutdown
-// drfsw_context* contextOld = context;
-// context = NULL;
-// drfsw_delete_context(contextOld);
-//
-// Directories are watched recursively, so try to avoid using this on high level directories
-// like "C:\". Also avoid watching a directory that is a descendant of another directory that's
-// already being watched.
-//
-// It does not matter whether or not paths are specified with forward or back slashes; the
-// library will normalize all of that internally depending on the platform. Note, however,
-// that events always report their paths with forward slashes.
-//
-// You don't need to wait for events on a separate thread, however drfsw_next_event() is
-// a blocking call, so it's usually best to do so. Alternatively, you can use
-// drfsw_peek_event() which is the same, except non-blocking. Avoid using both
-// drfsw_next_event() and drfsw_peek_event() at the same time because both will remove
-// the event from the internal queue so it likely won't work the way you expect.
-//
-// The shutdown sequence is a bit strange, but since another thread is accessing that pointer,
-// you should set any shared pointers to null before calling drfsw_delete_context(). That way,
-// the next call to drfsw_next_event() will pass in a null pointer which will cause it to
-// immediately return with zero and thus break the loop and terminate the thread. It is up to
-// the application to ensure the pointer passed to drfsw_next_event() is valid. Deleting a context
-// will cause a waiting call to drfsw_next_event() to return 0, however there is a chance that the
-// context is deleted before drfsw_next_event() has entered into it's wait state. It's up to the
-// application to make sure the context remains valid.
-//
-//
-//
-// QUICK NOTES
-// - Files that are not on the machine's local file system will not be detected (such as files on a network drive).
-// - In some cases, renaming files won't be detected. Instead it may be implemented as a delete/create pair.
-//
-// - Win32: Every directory that is watched becomes "in use" by the operating system. It is still possible
-// to modify the files and folders inside the watched directory, however.
-// - Win32: There is a known issue with the ReadDirectoryChangesW() watch technique (which is used internally)
-// where some events won't get processed if a large number of files change in a short period of time.
-
-#ifndef dr_fsw_h
-#define dr_fsw_h
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// The maximum length of a path in bytes, including the null terminator. If a path exceeds this amount, it will be set to an empty
-// string. When this is changed the source file will need to be recompiled. Most of the time leaving this at 256 is fine, but it's
-// not a problem to increase the size if you are encountering issues. Note that increasing this value will increase memory usage
-// on both the heap and the stack.
-#ifndef DRFSW_MAX_PATH
-//#define DRFSW_MAX_PATH 256U
-#define DRFSW_MAX_PATH 1024U
-//#define DRFSW_MAX_PATH 4096U
-#endif
-
-// The maximum size of the event queue before it overflows.
-#define DRFSW_EVENT_QUEUE_SIZE 1024U
-
-
-// The different event types.
-typedef enum
-{
- drfsw_event_type_created,
- drfsw_event_type_deleted,
- drfsw_event_type_renamed,
- drfsw_event_type_updated
-
-} drfsw_event_type;
-
-
-// Structure containing information about an event.
-typedef struct
-{
- // The type of the event: created, deleted, renamed or updated.
- drfsw_event_type type;
-
- // The absolute path of the file. For renamed events, this is the old name.
- char absolutePath[DRFSW_MAX_PATH];
-
- // The new file name. This is only used for renamed events. For other event types, this will be an empty string.
- char absolutePathNew[DRFSW_MAX_PATH];
-
- // The absolute base path. For renamed events, this is the old base path.
- char absoluteBasePath[DRFSW_MAX_PATH];
-
- // The absolute base path for the new file name. This is only used for renamed events. For other event types, this will be an empty string.
- char absoluteBasePathNew[DRFSW_MAX_PATH];
-
-} drfsw_event;
-
-
-typedef void* drfsw_context;
-
-
-// Creates a file system watcher.
-//
-// This will create a background thread that will do the actual checking.
-drfsw_context* drfsw_create_context(void);
-
-// Deletes the given file system watcher.
-//
-// This will not return until the thread watching for changes has returned.
-//
-// You do not need to remove the watched directories beforehand - this function will make sure everything is cleaned up properly.
-void drfsw_delete_context(drfsw_context* pContext);
-
-
-// Adds a directory to watch. This will watch for files and folders recursively.
-int drfsw_add_directory(drfsw_context* pContext, const char* absolutePath);
-
-// Removes a watched directory.
-void drfsw_remove_directory(drfsw_context* pContext, const char* absolutePath);
-
-// Helper for removing every watched directory.
-void drfsw_remove_all_directories(drfsw_context* pContext);
-
-// Determines whether or not the given directory is being watched.
-int drfsw_is_watching_directory(drfsw_context* pContext, const char* absolutePath);
-
-
-// Waits for an event from the file system.
-//
-// This is a blocking function. Call drfsw_peek_event() to do a non-blocking call. If an error occurs, or the context is deleted, 0
-// will be returned and the memory pointed to by pEventOut will be undefined.
-//
-// This can be called from any thread, however it should not be called from multiple threads simultaneously.
-//
-// Use caution when using this combined with drfsw_peek_event(). In almost all cases you should use just one or the other at any
-// given time.
-//
-// It is up to the application to ensure the context is still valid before calling this function.
-//
-// Example Usage:
-//
-// void MyFSWatcher() {
-// drfsw_event e;
-// while (isMyContextStillAlive && drfsw_next_event(context, e)) {
-// // Do something with the event...
-// }
-// }
-int drfsw_next_event(drfsw_context* pContext, drfsw_event* pEventOut);
-
-// Checks to see if there is a pending event, and if so, returns non-zero and fills the given structure with the event details. This
-// removes the event from the queue.
-//
-// This can be called from any thread, however it should not be called from multiple threads simultaneously.
-//
-// It is up to the application to ensure the context is still valid before calling this function.
-int drfsw_peek_event(drfsw_context* pContext, drfsw_event* pEventOut);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif //dr_fsw_h
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// IMPLEMENTATION
-//
-///////////////////////////////////////////////////////////////////////////////
-#ifdef DR_FSW_IMPLEMENTATION
-
-// NOTES:
-//
-// Win32 and ReadDirectoryChangesW
-//
-// Here is how watching for changes via the ReadDirectoryChangesW() works:
-// 1) You create a handle to the directory with CreateFile()
-// 2) You pass this handle to ReadDirectoryChangesW(), including a pointer to a function that is called when changes to the directory are made.
-// 3) From the aforementioned callback, ReadDirectoryChangesW() needs to be called again
-//
-// There are, however, a lot of details that need to be handled correctly in order for this to work
-//
-// First of all, the callback passed to ReadDirectoryChangesW() will not be called unless the calling thread is in an alertable state. A thread
-// is put into an alertable state with WaitForMultipleObjectsEx() (the Ex version is important since it has an extra parameter that lets you
-// put the thread into an alertable state). Using this blocks the thread which means you need to create a worker thread in the background.
-
-#if defined(__clang__)
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wcast-align"
-#endif
-
-#ifndef DRFSW_PRIVATE
-#define DRFSW_PRIVATE static
-#endif
-
-// The number of FILE_NOTIFY_INFORMATION structures in the buffer that's passed to ReadDirectoryChangesW()
-#define WIN32_RDC_FNI_COUNT DRFSW_EVENT_QUEUE_SIZE
-
-#include
-
-#if defined(_WIN32)
-#include
-
-DRFSW_PRIVATE void* drfsw_malloc(size_t sizeInBytes)
-{
- return HeapAlloc(GetProcessHeap(), 0, sizeInBytes);
-}
-
-DRFSW_PRIVATE void drfsw_free(void* p)
-{
- HeapFree(GetProcessHeap(), 0, p);
-}
-
-DRFSW_PRIVATE void drfsw_memcpy(void* dst, const void* src, size_t sizeInBytes)
-{
- CopyMemory(dst, src, sizeInBytes);
-}
-
-DRFSW_PRIVATE void drfsw_zeromemory(void* dst, size_t sizeInBytes)
-{
- ZeroMemory(dst, sizeInBytes);
-}
-#else
-#include
-#include
-
-DRFSW_PRIVATE void* drfsw_malloc(size_t sizeInBytes)
-{
- return malloc(sizeInBytes);
-}
-
-DRFSW_PRIVATE void drfsw_free(void* p)
-{
- free(p);
-}
-
-DRFSW_PRIVATE void drfsw_memcpy(void* dst, const void* src, size_t sizeInBytes)
-{
- memcpy(dst, src, sizeInBytes);
-}
-
-DRFSW_PRIVATE void drfsw_zeromemory(void* dst, size_t sizeInBytes)
-{
- memset(dst, 0, sizeInBytes);
-}
-#endif
-
-
-DRFSW_PRIVATE int drfsw_strcpy(char* dst, unsigned int dstSizeInBytes, const char* src)
-{
-#if defined(_MSC_VER)
- return strcpy_s(dst, dstSizeInBytes, src);
-#else
- if (dst == 0) {
- return EINVAL;
- }
- if (dstSizeInBytes == 0) {
- return ERANGE;
- }
- if (src == 0) {
- dst[0] = '\0';
- return EINVAL;
- }
-
- char* iDst = dst;
- const char* iSrc = src;
- size_t remainingSizeInBytes = dstSizeInBytes;
- while (remainingSizeInBytes > 0 && iSrc[0] != '\0')
- {
- iDst[0] = iSrc[0];
-
- iDst += 1;
- iSrc += 1;
- remainingSizeInBytes -= 1;
- }
-
- if (remainingSizeInBytes > 0) {
- iDst[0] = '\0';
- } else {
- dst[0] = '\0';
- return ERANGE;
- }
-
- return 0;
-#endif
-}
-
-
-DRFSW_PRIVATE int drfsw_event_init(drfsw_event* pEvent, drfsw_event_type type, const char* absolutePath, const char* absolutePathNew, const char* absoluteBasePath, const char* absoluteBasePathNew)
-{
- if (pEvent != NULL)
- {
- pEvent->type = type;
-
- if (absolutePath != NULL) {
- drfsw_strcpy(pEvent->absolutePath, DRFSW_MAX_PATH, absolutePath);
- } else {
- drfsw_zeromemory(pEvent->absolutePath, DRFSW_MAX_PATH);
- }
-
- if (absolutePathNew != NULL) {
- drfsw_strcpy(pEvent->absolutePathNew, DRFSW_MAX_PATH, absolutePathNew);
- } else {
- drfsw_zeromemory(pEvent->absolutePathNew, DRFSW_MAX_PATH);
- }
-
-
- if (absoluteBasePath != NULL) {
- drfsw_strcpy(pEvent->absoluteBasePath, DRFSW_MAX_PATH, absoluteBasePath);
- } else {
- drfsw_zeromemory(pEvent->absoluteBasePath, DRFSW_MAX_PATH);
- }
-
- if (absoluteBasePathNew != NULL) {
- drfsw_strcpy(pEvent->absoluteBasePathNew, DRFSW_MAX_PATH, absoluteBasePathNew);
- } else {
- drfsw_zeromemory(pEvent->absoluteBasePathNew, DRFSW_MAX_PATH);
- }
-
- return 1;
- }
-
- return 0;
-}
-
-
-typedef struct
-{
- // The buffer containing the events in the queue.
- drfsw_event* pBuffer;
-
- // The size of the buffer, in drfsw_event's.
- unsigned int bufferSize;
-
- // The number of items in the queue.
- unsigned int count;
-
- // The index of the first item in the queue.
- unsigned int indexFirst;
-
-#if defined(_WIN32)
- // The semaphore for blocking in drfsw_next_event().
- HANDLE hSemaphore;
-
- // The mutex for synchronizing access to the buffer. This is needed because drfsw_next_event() will need to read the buffer while
- // another thread is filling it with events. In addition, it will help to keep drfsw_next_event() and drfsw_peek_event() playing
- // nicely with each other.
- HANDLE hLock;
-#endif
-
-} drfsw_event_queue;
-
-DRFSW_PRIVATE int drfsw_event_queue_init(drfsw_event_queue* pQueue)
-{
- if (pQueue != NULL)
- {
- pQueue->pBuffer = NULL;
- pQueue->bufferSize = 0;
- pQueue->indexFirst = 0;
- pQueue->count = 0;
-
-#if defined(_WIN32)
- pQueue->hSemaphore = CreateSemaphoreW(NULL, 0, DRFSW_EVENT_QUEUE_SIZE, NULL);
- if (pQueue->hSemaphore == NULL)
- {
- drfsw_free(pQueue->pBuffer);
- return 0;
- }
-
- pQueue->hLock = CreateEventW(NULL, FALSE, TRUE, NULL);
- if (pQueue->hLock == NULL)
- {
- CloseHandle(pQueue->hSemaphore);
- drfsw_free(pQueue->pBuffer);
- return 0;
- }
-#endif
-
- return 1;
- }
-
- return 0;
-}
-
-DRFSW_PRIVATE void drfsw_event_queue_uninit(drfsw_event_queue* pQueue)
-{
- if (pQueue != NULL)
- {
- drfsw_free(pQueue->pBuffer);
- pQueue->pBuffer = NULL;
-
- pQueue->bufferSize = 0;
- pQueue->indexFirst = 0;
- pQueue->count = 0;
-
-#if defined(_WIN32)
- CloseHandle(pQueue->hSemaphore);
- pQueue->hSemaphore = NULL;
-
- CloseHandle(pQueue->hLock);
- pQueue->hLock = NULL;
-#endif
- }
-}
-
-DRFSW_PRIVATE unsigned int drfsw_event_queue_getcount(drfsw_event_queue* pQueue)
-{
- if (pQueue != NULL)
- {
- return pQueue->count;
- }
-
- return 0;
-}
-
-DRFSW_PRIVATE void drfsw_event_queue_inflate(drfsw_event_queue* pQueue)
-{
- if (pQueue != NULL)
- {
- unsigned int newBufferSize = pQueue->bufferSize + 1;
- if (pQueue->bufferSize > 0)
- {
- newBufferSize = pQueue->bufferSize*2;
- }
-
- drfsw_event* pOldBuffer = pQueue->pBuffer;
- drfsw_event* pNewBuffer = (drfsw_event*)drfsw_malloc(newBufferSize * sizeof(drfsw_event));
-
- for (unsigned int iDst = 0; iDst < pQueue->count; ++iDst)
- {
- unsigned int iSrc = (pQueue->indexFirst + iDst) % pQueue->bufferSize;
- drfsw_memcpy(pNewBuffer + iDst, pOldBuffer + iSrc, sizeof(drfsw_event));
- }
-
-
- pQueue->bufferSize = newBufferSize;
- pQueue->pBuffer = pNewBuffer;
- pQueue->indexFirst = 0;
-
- drfsw_free(pOldBuffer);
- }
-}
-
-DRFSW_PRIVATE int drfsw_event_queue_pushback(drfsw_event_queue* pQueue, drfsw_event* pEvent)
-{
- if (pQueue != NULL)
- {
- if (pEvent != NULL)
- {
- unsigned int count = drfsw_event_queue_getcount(pQueue);
- if (count == DRFSW_EVENT_QUEUE_SIZE)
- {
- // We've hit the limit.
- return 0;
- }
-
- if (count == pQueue->bufferSize)
- {
- drfsw_event_queue_inflate(pQueue);
- assert(count < pQueue->bufferSize);
- }
-
-
- // Insert the value.
- unsigned int iDst = (pQueue->indexFirst + pQueue->count) % pQueue->bufferSize;
- drfsw_memcpy(pQueue->pBuffer + iDst, pEvent, sizeof(drfsw_event));
-
-
- // Increment the counter.
- pQueue->count += 1;
-
-
- return 1;
- }
- }
-
- return 0;
-}
-
-DRFSW_PRIVATE int drfsw_event_queue_pop(drfsw_event_queue* pQueue, drfsw_event* pEventOut)
-{
- if (pQueue != NULL && pQueue->count > 0)
- {
- if (pEventOut != NULL)
- {
- drfsw_memcpy(pEventOut, pQueue->pBuffer + pQueue->indexFirst, sizeof(drfsw_event));
- pQueue->indexFirst = (pQueue->indexFirst + 1) % pQueue->bufferSize;
- }
-
- pQueue->count -= 1;
-
-
- return 1;
- }
-
- return 0;
-}
-
-
-
-// A simple function for appending a relative path to an absolute path. This does not resolve "." and ".." components.
-DRFSW_PRIVATE int drfsw_make_absolute_path(const char* absolutePart, const char* relativePart, char absolutePathOut[DRFSW_MAX_PATH])
-{
- size_t absolutePartLength = strlen(absolutePart);
- size_t relativePartLength = strlen(relativePart);
-
- if (absolutePartLength > 0)
- {
- if (absolutePart[absolutePartLength - 1] == '/')
- {
- absolutePartLength -= 1;
- }
-
- if (absolutePartLength > DRFSW_MAX_PATH)
- {
- absolutePartLength = DRFSW_MAX_PATH - 1;
- }
- }
-
- if (absolutePartLength + relativePartLength + 1 > DRFSW_MAX_PATH)
- {
- relativePartLength = DRFSW_MAX_PATH - 1 - absolutePartLength - 1; // -1 for the null terminate and -1 for the slash.
- }
-
-
- // Absolute part.
- memcpy(absolutePathOut, absolutePart, absolutePartLength);
-
- // Slash.
- absolutePathOut[absolutePartLength] = '/';
-
- // Relative part.
- memcpy(absolutePathOut + absolutePartLength + 1, relativePart, relativePartLength);
-
- // Null terminator.
- absolutePathOut[absolutePartLength + 1 + relativePartLength] = '\0';
-
-
- return 1;
-}
-
-// Replaces the back slashes with forward slashes in the given string. This operates on the string in place.
-DRFSW_PRIVATE int drfsw_to_forward_slashes(char* path)
-{
- if (path != NULL)
- {
- unsigned int counter = 0;
- while (*path++ != '\0' && counter++ < DRFSW_MAX_PATH)
- {
- if (*path == '\\')
- {
- *path = '/';
- }
- }
-
- return 1;
- }
-
- return 0;
-}
-
-
-typedef struct
-{
- // A pointer to the buffer containing pointers to the objects.
- void** buffer;
-
- // The size of the buffer, in pointers.
- unsigned int bufferSize;
-
- // The number of pointers in the list.
- unsigned int count;
-
-} drfsw_list;
-
-DRFSW_PRIVATE int drfsw_list_init(drfsw_list* pList)
-{
- if (pList != NULL)
- {
- pList->buffer = NULL;
- pList->bufferSize = 0;
- pList->count = 0;
-
- return 1;
- }
-
- return 0;
-}
-
-DRFSW_PRIVATE void drfsw_list_uninit(drfsw_list* pList)
-{
- if (pList != NULL)
- {
- drfsw_free(pList->buffer);
- }
-}
-
-DRFSW_PRIVATE void drfsw_list_inflate(drfsw_list* pList)
-{
- if (pList != NULL)
- {
- unsigned int newBufferSize = pList->bufferSize + 1;
- if (pList->bufferSize > 0)
- {
- newBufferSize = pList->bufferSize*2;
- }
-
- void** pOldBuffer = pList->buffer;
- void** pNewBuffer = (void**)drfsw_malloc(newBufferSize*sizeof(void*));
-
- // Move everything over to the new buffer.
- for (unsigned int i = 0; i < pList->count; ++i)
- {
- pNewBuffer[i] = pOldBuffer[i];
- }
-
-
- pList->bufferSize = newBufferSize;
- pList->buffer = pNewBuffer;
- }
-}
-
-DRFSW_PRIVATE void drfsw_list_pushback(drfsw_list* pList, void* pItem)
-{
- if (pList != NULL)
- {
- if (pList->count == pList->bufferSize)
- {
- drfsw_list_inflate(pList);
- assert(pList->count < pList->bufferSize);
-
- pList->buffer[pList->count] = pItem;
- pList->count += 1;
- }
- }
-}
-
-DRFSW_PRIVATE void drfsw_list_removebyindex(drfsw_list* pList, unsigned int index)
-{
- if (pList != NULL)
- {
- assert(index < pList->count);
- assert(pList->count > 0);
-
- // Just move everything down one slot.
- for (unsigned int i = index; index < pList->count - 1; ++i)
- {
- pList->buffer[i] = pList->buffer[i + 1];
- }
-
- pList->count -= 1;
- }
-}
-
-
-
-#if defined(_WIN32)
-#define DRFSW_MAX_PATH_W (DRFSW_MAX_PATH / sizeof(wchar_t))
-
-///////////////////////////////////////////////
-// ReadDirectoryChangesW
-
-static const int WIN32_RDC_PENDING_WATCH = (1 << 0);
-static const int WIN32_RDC_PENDING_DELETE = (1 << 1);
-
-
-// Replaces the forward slashes to back slashes for use with Win32. This operates on the string in place.
-DRFSW_PRIVATE int drfsw_to_back_slashes_wchar(wchar_t* path)
-{
- if (path != NULL)
- {
- unsigned int counter = 0;
- while (*path++ != L'\0' && counter++ < DRFSW_MAX_PATH_W)
- {
- if (*path == L'/')
- {
- *path = L'\\';
- }
- }
-
- return 1;
- }
-
- return 0;
-}
-
-// Converts a UTF-8 string to wchar_t for use with Win32. Free the returned pointer with drfsw_free().
-DRFSW_PRIVATE int drfsw_utf8_to_wchar(const char* str, wchar_t wstrOut[DRFSW_MAX_PATH_W])
-{
- int wcharsWritten = MultiByteToWideChar(CP_UTF8, 0, str, -1, wstrOut, DRFSW_MAX_PATH_W);
- if (wcharsWritten > 0)
- {
- assert((unsigned int)wcharsWritten <= DRFSW_MAX_PATH_W);
- return 1;
- }
-
- return 0;
-}
-
-DRFSW_PRIVATE int drfsw_wchar_to_utf8(const wchar_t* wstr, int wstrCC, char pathOut[DRFSW_MAX_PATH])
-{
- int bytesWritten = WideCharToMultiByte(CP_UTF8, 0, wstr, wstrCC, pathOut, DRFSW_MAX_PATH - 1, NULL, NULL);
- if (bytesWritten > 0)
- {
- assert((unsigned int)bytesWritten < DRFSW_MAX_PATH);
- pathOut[bytesWritten] = '\0';
-
- return 1;
- }
-
- return 0;
-}
-
-// Converts a UTF-8 path to wchar_t and converts the slashes to backslashes for use with Win32. Free the returned pointer with drfsw_free().
-DRFSW_PRIVATE int drfsw_to_win32_path_wchar(const char* path, wchar_t wpathOut[DRFSW_MAX_PATH_W])
-{
- if (drfsw_utf8_to_wchar(path, wpathOut))
- {
- return drfsw_to_back_slashes_wchar(wpathOut);
- }
-
- return 0;
-}
-
-// Converts a wchar_t Win32 path to a char unix style path (forward slashes instead of back).
-DRFSW_PRIVATE int drfsw_from_win32_path(const wchar_t* wpath, int wpathCC, char pathOut[DRFSW_MAX_PATH])
-{
- if (drfsw_wchar_to_utf8(wpath, wpathCC, pathOut))
- {
- return drfsw_to_forward_slashes(pathOut);
- }
-
- return 0;
-}
-
-
-DRFSW_PRIVATE VOID CALLBACK drfsw_win32_completionroutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped);
-DRFSW_PRIVATE VOID CALLBACK drfsw_win32_schedulewatchAPC(ULONG_PTR dwParam);
-DRFSW_PRIVATE VOID CALLBACK drfsw_win32_cancelioAPC(ULONG_PTR dwParam);
-
-typedef struct
-{
- // Thelist containing pointers to the watched directory objects. This is not thread safe.
- drfsw_list list;
-
- // The lock for synchronizing access to the list.
- HANDLE hLock;
-
-} drfsw_directory_list_win32;
-
-// Structure representing the watcher context for the Win32 RDC method.
-typedef struct
-{
- // The list of watched directories.
- drfsw_directory_list_win32 watchedDirectories;
-
- // The event queue.
- drfsw_event_queue eventQueue;
-
-
- // A handle to the watcher thread.
- HANDLE hThread;
-
- // The event that will become signaled when the watcher thread needs to be terminated.
- HANDLE hTerminateEvent;
-
- // The semaphore which is used when deleting a watched folder. This starts off at 0, and the maximum count is 1. When a watched
- // directory is removed, the calling thread will wait on this semaphore while the worker thread does the deletion.
- HANDLE hDeleteDirSemaphore;
-
- // Whether or not the watch thread needs to be terminated.
- BOOL terminateThread;
-
-} drfsw_context_win32;
-
-DRFSW_PRIVATE drfsw_context* drfsw_create_context_win32(void);
-DRFSW_PRIVATE void drfsw_delete_context_win32(drfsw_context_win32* pContext);
-DRFSW_PRIVATE int drfsw_add_directory_win32(drfsw_context_win32* pContext, const char* absolutePath);
-DRFSW_PRIVATE void drfsw_remove_directory_win32(drfsw_context_win32* pContext, const char* absolutePath);
-DRFSW_PRIVATE void drfsw_remove_all_directories_win32(drfsw_context_win32* pContext);
-DRFSW_PRIVATE int drfsw_is_watching_directory_win32(drfsw_context_win32* pContext, const char* absolutePath);
-DRFSW_PRIVATE int drfsw_next_event_win32(drfsw_context_win32* pContext, drfsw_event* pEventOut);
-DRFSW_PRIVATE int drfsw_peek_event_win32(drfsw_context_win32* pContext, drfsw_event* pEventOut);
-DRFSW_PRIVATE void drfsw_postevent_win32(drfsw_context_win32* pContext, drfsw_event* pEvent);
-
-
-// Structure representing a directory that's being watched with the Win32 RDC method.
-typedef struct
-{
- // A pointer to the context that owns this directory.
- drfsw_context_win32* pContext;
-
- // The absolute path of the directory being watched.
- char absolutePath[DRFSW_MAX_PATH];
-
- // The handle representing the directory. This is created with CreateFile() which means the directory itself will become locked
- // because the operating system see's it as "in use". It is possible to modify the files and folder inside the directory, though.
- HANDLE hDirectory;
-
- // This is required for for ReadDirectoryChangesW().
- OVERLAPPED overlapped;
-
- // A pointer to the buffer containing the notification objects that is passed to the notification callback specified with
- // ReadDirectoryChangesW(). This must be aligned to a DWORD boundary, but drfsw_malloc() will do that for us, so that should not
- // be an issue.
- FILE_NOTIFY_INFORMATION* pFNIBuffer1;
- FILE_NOTIFY_INFORMATION* pFNIBuffer2;
-
- // The size of the file notification information buffer, in bytes.
- DWORD fniBufferSizeInBytes;
-
- // Flags describing the state of the directory.
- int flags;
-
-} drfsw_directory_win32;
-
-DRFSW_PRIVATE int drfsw_directory_win32_beginwatch(drfsw_directory_win32* pDirectory);
-
-
-DRFSW_PRIVATE void drfsw_directory_win32_uninit(drfsw_directory_win32* pDirectory)
-{
- if (pDirectory != NULL)
- {
- if (pDirectory->hDirectory != NULL)
- {
- CloseHandle(pDirectory->hDirectory);
- pDirectory->hDirectory = NULL;
- }
-
- drfsw_free(pDirectory->pFNIBuffer1);
- pDirectory->pFNIBuffer1 = NULL;
-
- drfsw_free(pDirectory->pFNIBuffer2);
- pDirectory->pFNIBuffer2 = NULL;
- }
-}
-
-DRFSW_PRIVATE int drfsw_directory_win32_init(drfsw_directory_win32* pDirectory, drfsw_context_win32* pContext, const char* absolutePath)
-{
- if (pDirectory != NULL)
- {
- pDirectory->pContext = pContext;
- drfsw_zeromemory(pDirectory->absolutePath, DRFSW_MAX_PATH);
- pDirectory->hDirectory = NULL;
- pDirectory->pFNIBuffer1 = NULL;
- pDirectory->pFNIBuffer2 = NULL;
- pDirectory->fniBufferSizeInBytes = 0;
- pDirectory->flags = 0;
-
- size_t length = strlen(absolutePath);
- if (length > 0)
- {
- memcpy(pDirectory->absolutePath, absolutePath, length);
- pDirectory->absolutePath[length] = '\0';
-
- wchar_t absolutePathWithBackSlashes[DRFSW_MAX_PATH_W];
- if (drfsw_to_win32_path_wchar(absolutePath, absolutePathWithBackSlashes))
- {
- pDirectory->hDirectory = CreateFileW(
- absolutePathWithBackSlashes,
- FILE_LIST_DIRECTORY,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- NULL,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
- NULL);
- if (pDirectory->hDirectory != INVALID_HANDLE_VALUE)
- {
- // From MSDN:
- //
- // Using a completion routine. To receive notification through a completion routine, do not associate the directory with a
- // completion port. Specify a completion routine in lpCompletionRoutine. This routine is called whenever the operation has
- // been completed or canceled while the thread is in an alertable wait state. The hEvent member of the OVERLAPPED structure
- // is not used by the system, so you can use it yourself.
- ZeroMemory(&pDirectory->overlapped, sizeof(pDirectory->overlapped));
- pDirectory->overlapped.hEvent = pDirectory;
-
- pDirectory->fniBufferSizeInBytes = WIN32_RDC_FNI_COUNT * sizeof(FILE_NOTIFY_INFORMATION);
- pDirectory->pFNIBuffer1 = (FILE_NOTIFY_INFORMATION*)drfsw_malloc(pDirectory->fniBufferSizeInBytes);
- pDirectory->pFNIBuffer2 = (FILE_NOTIFY_INFORMATION*)drfsw_malloc(pDirectory->fniBufferSizeInBytes);
- if (pDirectory->pFNIBuffer1 != NULL && pDirectory->pFNIBuffer2 != NULL)
- {
- // At this point the directory is initialized, however it is not yet being watched. The watch needs to be triggered from
- // the worker thread. To do this, we need to signal hPendingWatchEvent, however that needs to be done after the context
- // has added the directory to it's internal list.
- return 1;
- }
- else
- {
- drfsw_directory_win32_uninit(pDirectory);
- }
- }
- else
- {
- drfsw_directory_win32_uninit(pDirectory);
- }
- }
- else
- {
- drfsw_directory_win32_uninit(pDirectory);
- }
- }
- }
-
- return 0;
-}
-
-DRFSW_PRIVATE int drfsw_directory_win32_schedulewatch(drfsw_directory_win32* pDirectory)
-{
- if (pDirectory != NULL)
- {
- pDirectory->flags |= WIN32_RDC_PENDING_WATCH;
- QueueUserAPC(drfsw_win32_schedulewatchAPC, pDirectory->pContext->hThread, (ULONG_PTR)pDirectory);
-
- return 1;
- }
-
- return 0;
-}
-
-DRFSW_PRIVATE int drfsw_directory_win32_scheduledelete(drfsw_directory_win32* pDirectory)
-{
- if (pDirectory != NULL)
- {
- pDirectory->flags |= WIN32_RDC_PENDING_DELETE;
- QueueUserAPC(drfsw_win32_cancelioAPC, pDirectory->pContext->hThread, (ULONG_PTR)pDirectory);
-
- return 1;
- }
-
- return 0;
-}
-
-DRFSW_PRIVATE int drfsw_directory_win32_beginwatch(drfsw_directory_win32* pDirectory)
-{
- // This function should only be called from the worker thread.
-
- if (pDirectory != NULL)
- {
- DWORD dwNotifyFilter = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_CREATION;
- DWORD dwBytes = 0;
- if (ReadDirectoryChangesW(pDirectory->hDirectory, pDirectory->pFNIBuffer1, pDirectory->fniBufferSizeInBytes, TRUE, dwNotifyFilter, &dwBytes, &pDirectory->overlapped, drfsw_win32_completionroutine))
- {
- pDirectory->flags &= ~WIN32_RDC_PENDING_WATCH;
- return 1;
- }
- }
-
- return 0;
-}
-
-
-
-
-DRFSW_PRIVATE int drfsw_directory_list_win32_init(drfsw_directory_list_win32* pDirectories)
-{
- if (pDirectories != NULL)
- {
- drfsw_list_init(&pDirectories->list);
-
- pDirectories->hLock = CreateEvent(NULL, FALSE, TRUE, NULL);
- if (pDirectories->hLock != NULL)
- {
- return 1;
- }
- }
-
- return 0;
-}
-
-DRFSW_PRIVATE void drfsw_directory_list_win32_uninit(drfsw_directory_list_win32* pDirectories)
-{
- if (pDirectories != NULL)
- {
- drfsw_list_uninit(&pDirectories->list);
-
- CloseHandle(pDirectories->hLock);
- pDirectories->hLock = NULL;
- }
-}
-
-
-
-
-
-DRFSW_PRIVATE VOID CALLBACK drfsw_win32_completionroutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
-{
- drfsw_directory_win32* pDirectory = (drfsw_directory_win32*)lpOverlapped->hEvent;
- if (pDirectory != NULL)
- {
- if (dwErrorCode == ERROR_OPERATION_ABORTED)
- {
- // When we get here, CancelIo() was called on the directory. We treat this as a signal that the context has requested that the directory
- // be deleted. At this point the directory has been removed from the context's internal list and we just need to uninitialize and free
- // the directory object.
- drfsw_context_win32* pContext = pDirectory->pContext;
- drfsw_directory_win32_uninit(pDirectory);
- drfsw_free(pDirectory);
-
- ReleaseSemaphore(pContext->hDeleteDirSemaphore, 1, NULL);
- return;
- }
-
- assert(dwNumberOfBytesTransfered >= sizeof(FILE_NOTIFY_INFORMATION));
-
- // At this point we're not actually watching the directory - there is a chance that as we're executing this section there
- // are changes to the file system whose events will go undetected. We need to call ReadDirectoryChangesW() again as soon as
- // possible. This routine is always called from the worker thread, and only while it's in an alertable state. Therefore it
- // is safe for us to use a simple front/back buffer type system to make it as quick as possible to resume watching operations.
- FILE_NOTIFY_INFORMATION* temp = pDirectory->pFNIBuffer1;
- pDirectory->pFNIBuffer1 = pDirectory->pFNIBuffer2;
- pDirectory->pFNIBuffer2 = temp;
-
-
- // Begin watching again (call ReadDirectoryChangesW() again) as soon as possible. At this point we are not currently watching
- // for changes to the directory, so we need to start that before posting events. To start watching we need to send a signal to
- // the worker thread which will do the actual call to ReadDirectoryChangesW().
- drfsw_directory_win32_schedulewatch(pDirectory);
-
-
- // Now we loop through all of our notifications and post the event to the context for later processing by drfsw_next_event()
- // and drfsw_peek_event().
- char absolutePathOld[DRFSW_MAX_PATH];
- char absoluteBasePathOld[DRFSW_MAX_PATH];
- drfsw_context_win32* pContext = pDirectory->pContext; // Just for convenience.
-
-
- FILE_NOTIFY_INFORMATION* pFNI = pDirectory->pFNIBuffer2;
- for (;;)
- {
- char relativePath[DRFSW_MAX_PATH];
- if (drfsw_from_win32_path(pFNI->FileName, pFNI->FileNameLength / sizeof(wchar_t), relativePath))
- {
- char absolutePath[DRFSW_MAX_PATH];
- if (drfsw_make_absolute_path(pDirectory->absolutePath, relativePath, absolutePath))
- {
- switch (pFNI->Action)
- {
- case FILE_ACTION_ADDED:
- {
- drfsw_event e;
- if (drfsw_event_init(&e, drfsw_event_type_created, absolutePath, NULL, pDirectory->absolutePath, NULL))
- {
- drfsw_postevent_win32(pContext, &e);
- }
-
- break;
- }
-
- case FILE_ACTION_REMOVED:
- {
- drfsw_event e;
- if (drfsw_event_init(&e, drfsw_event_type_deleted, absolutePath, NULL, pDirectory->absolutePath, NULL))
- {
- drfsw_postevent_win32(pContext, &e);
- }
-
- break;
- }
-
- case FILE_ACTION_RENAMED_OLD_NAME:
- {
- drfsw_strcpy(absolutePathOld, sizeof(absolutePathOld), absolutePath);
- drfsw_strcpy(absoluteBasePathOld, sizeof(absoluteBasePathOld), pDirectory->absolutePath);
-
- break;
- }
- case FILE_ACTION_RENAMED_NEW_NAME:
- {
- drfsw_event e;
- if (drfsw_event_init(&e, drfsw_event_type_renamed, absolutePathOld, absolutePath, absoluteBasePathOld, pDirectory->absolutePath))
- {
- drfsw_postevent_win32(pContext, &e);
- }
-
- break;
- }
-
- case FILE_ACTION_MODIFIED:
- {
- drfsw_event e;
- if (drfsw_event_init(&e, drfsw_event_type_updated, absolutePath, NULL, pDirectory->absolutePath, NULL))
- {
- drfsw_postevent_win32(pContext, &e);
- }
-
- break;
- }
-
- default: break;
- }
- }
- }
-
-
-
- if (pFNI->NextEntryOffset == 0)
- {
- break;
- }
-
- pFNI = (FILE_NOTIFY_INFORMATION*)(((char*)pFNI) + pFNI->NextEntryOffset);
- }
- }
-}
-
-DRFSW_PRIVATE VOID CALLBACK drfsw_win32_schedulewatchAPC(ULONG_PTR dwParam)
-{
- drfsw_directory_win32* pDirectory = (drfsw_directory_win32*)dwParam;
- if (pDirectory != NULL)
- {
- if ((pDirectory->flags & WIN32_RDC_PENDING_WATCH) != 0)
- {
- drfsw_directory_win32_beginwatch(pDirectory);
- }
- }
-}
-
-DRFSW_PRIVATE VOID CALLBACK drfsw_win32_cancelioAPC(ULONG_PTR dwParam)
-{
- drfsw_directory_win32* pDirectory = (drfsw_directory_win32*)dwParam;
- if (pDirectory != NULL)
- {
- if ((pDirectory->flags & WIN32_RDC_PENDING_DELETE) != 0)
- {
- // We don't free the directory structure from here. Instead we just call CancelIo(). This will trigger
- // the ERROR_OPERATION_ABORTED error in the notification callback which is where the real delete will
- // occur. That is also where the synchronization lock is released that the thread that called
- // drfsw_delete_directory() is waiting on.
- CancelIo(pDirectory->hDirectory);
-
- // The directory needs to be removed from the context's list. The directory object will be freed in the
- // notification callback in response to ERROR_OPERATION_ABORTED which will be triggered by the previous
- // call to CancelIo().
- for (unsigned int i = 0; i < pDirectory->pContext->watchedDirectories.list.count; ++i)
- {
- if (pDirectory == pDirectory->pContext->watchedDirectories.list.buffer[i])
- {
- drfsw_list_removebyindex(&pDirectory->pContext->watchedDirectories.list, i);
- break;
- }
- }
- }
- }
-}
-
-
-
-
-DRFSW_PRIVATE DWORD WINAPI _WatcherThreadProc_RDC(drfsw_context_win32 *pContextRDC)
-{
- while (!pContextRDC->terminateThread)
- {
- // Important that we use the Ex version here because we need to put the thread into an alertable state (last argument). If the thread is not put into
- // an alertable state, ReadDirectoryChangesW() won't ever call the notification event.
- DWORD rc = WaitForSingleObjectEx(pContextRDC->hTerminateEvent, INFINITE, TRUE);
- switch (rc)
- {
- case WAIT_OBJECT_0 + 0:
- {
- // The context has signaled that it needs to be deleted.
- pContextRDC->terminateThread = TRUE;
- break;
- }
-
- case WAIT_IO_COMPLETION:
- default:
- {
- // Nothing to do.
- break;
- }
- }
- }
-
- return 0;
-}
-
-DRFSW_PRIVATE drfsw_context* drfsw_create_context_win32()
-{
- drfsw_context_win32* pContext = (drfsw_context_win32*)drfsw_malloc(sizeof(drfsw_context_win32));
- if (pContext != NULL)
- {
- if (drfsw_directory_list_win32_init(&pContext->watchedDirectories))
- {
- if (drfsw_event_queue_init(&pContext->eventQueue))
- {
- pContext->hTerminateEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- pContext->hDeleteDirSemaphore = CreateSemaphoreW(NULL, 0, 1, NULL);
- pContext->terminateThread = FALSE;
-
- if (pContext->hTerminateEvent != NULL)
- {
- pContext->hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)_WatcherThreadProc_RDC, pContext, 0, NULL);
- if (pContext->hThread != NULL)
- {
- // Everything went well.
- }
- else
- {
- CloseHandle(pContext->hTerminateEvent);
-
- drfsw_free(pContext);
- pContext = NULL;
- }
- }
- else
- {
- drfsw_free(pContext);
- pContext = NULL;
- }
- }
- }
- }
-
- return (drfsw_context*)pContext;
-}
-
-DRFSW_PRIVATE void drfsw_delete_context_win32(drfsw_context_win32* pContext)
-{
- if (pContext != NULL)
- {
- // Every watched directory needs to be removed.
- drfsw_remove_all_directories_win32(pContext);
-
-
- // Signal the close event, and wait for the thread to finish.
- SignalObjectAndWait(pContext->hTerminateEvent, pContext->hThread, INFINITE, FALSE);
-
- // The thread has finished, so close the handle.
- CloseHandle(pContext->hThread);
- pContext->hThread = NULL;
-
-
- // We need to wait for the event queue to finish up before deleting the context for real. If we don't do this nextevent() may try
- // to access the context and then crash.
- WaitForSingleObject(pContext->eventQueue.hLock, INFINITE);
- drfsw_event_queue_uninit(&pContext->eventQueue); // <-- This will close pContext->eventQueue.hLock so no need to call SetEvent().
-
-
- // The worker thread events need to be closed.
- CloseHandle(pContext->hTerminateEvent);
- pContext->hTerminateEvent = NULL;
-
-
- // The semaphore we use for deleting directories.
- CloseHandle(pContext->hDeleteDirSemaphore);
- pContext->hDeleteDirSemaphore = NULL;
-
-
- drfsw_directory_list_win32_uninit(&pContext->watchedDirectories);
-
- // Free the memory.
- drfsw_free(pContext);
- }
-}
-
-DRFSW_PRIVATE drfsw_directory_win32* drfsw_find_directory_win32(drfsw_context_win32* pContext, const char* absolutePath, unsigned int* pIndexOut)
-{
- assert(pContext != NULL);
-
- for (unsigned int iDirectory = 0; iDirectory < pContext->watchedDirectories.list.count; ++iDirectory)
- {
- drfsw_directory_win32* pDirectory = (drfsw_directory_win32*)pContext->watchedDirectories.list.buffer[iDirectory];
- if (pDirectory != NULL)
- {
- if (strcmp(absolutePath, pDirectory->absolutePath) == 0)
- {
- if (pIndexOut != NULL)
- {
- *pIndexOut = iDirectory;
- }
-
- return pDirectory;
- }
- }
- }
-
- return NULL;
-}
-
-DRFSW_PRIVATE int drfsw_add_directory_win32(drfsw_context_win32* pContext, const char* absolutePath)
-{
- if (pContext != NULL)
- {
- drfsw_directory_win32* pDirectory = (drfsw_directory_win32*)drfsw_malloc(sizeof(drfsw_directory_win32));
- if (pDirectory != NULL)
- {
- if (!drfsw_is_watching_directory_win32(pContext, absolutePath))
- {
- if (drfsw_directory_win32_init(pDirectory, pContext, absolutePath))
- {
- // At this point the directory has been initialized, but it's not yet being watched. To do this, we need to call ReadDirectoryChangesW() from
- // the worker thread which means we need to signal an event which the worker thread will be waiting on. When the worker thread receives the
- // signal, it will iterate over each directory in the context and check for the ones that are pending watching. Therefore, before signaling
- // the event, we need to make sure the directory is added to the context's list.
- WaitForSingleObject(pContext->watchedDirectories.hLock, INFINITE);
- {
- drfsw_list_pushback(&pContext->watchedDirectories.list, pDirectory);
- }
- SetEvent(pContext->watchedDirectories.hLock);
-
- // The directory is now in the list and we can send the signal.
- drfsw_directory_win32_schedulewatch(pDirectory);
-
-
- return 1;
- }
- else
- {
- drfsw_free(pDirectory);
- pDirectory = NULL;
- }
- }
- else
- {
- drfsw_free(pDirectory);
- pDirectory = NULL;
- }
- }
- }
-
-
-
- // An error occured if we got here.
- return 0;
-}
-
-DRFSW_PRIVATE void drfsw_remove_directory_win32_no_lock(drfsw_context_win32* pContext, const char* absolutePath)
-{
- assert(pContext != NULL);
-
- unsigned int index;
- drfsw_directory_win32* pDirectory = drfsw_find_directory_win32(pContext, absolutePath, &index);
- if (pDirectory != NULL)
- {
- // When removing a directory, we need to call CancelIo() on the file handle we created for the directory. The problem is that
- // this needs to be called on the worker thread in order for watcher notification callback function to get the correct error
- // code. To do this we need to signal an event which the worker thread is waiting on. The worker thread will then call CancelIo()
- // which in turn will trigger the correct error code in the notification callback. The notification callback is where the
- // the object will be deleted for real and will release the synchronization lock that this function is waiting on below.
- drfsw_directory_win32_scheduledelete(pDirectory);
-
- // Now we need to wait for the relevant event to become signaled. This will become signaled when the worker thread has finished
- // deleting the file handle and whatnot from it's end.
- WaitForSingleObject(pContext->hDeleteDirSemaphore, INFINITE);
- }
-}
-
-DRFSW_PRIVATE void drfsw_remove_directory_win32(drfsw_context_win32* pContext, const char* absolutePath)
-{
- if (pContext != NULL)
- {
- WaitForSingleObject(pContext->watchedDirectories.hLock, INFINITE);
- {
- drfsw_remove_directory_win32_no_lock(pContext, absolutePath);
- }
- SetEvent(pContext->watchedDirectories.hLock);
- }
-}
-
-DRFSW_PRIVATE void drfsw_remove_all_directories_win32(drfsw_context_win32* pContext)
-{
- if (pContext != NULL)
- {
- WaitForSingleObject(pContext->watchedDirectories.hLock, INFINITE);
- {
- for (unsigned int i = pContext->watchedDirectories.list.count; i > 0; --i)
- {
- drfsw_remove_directory_win32_no_lock(pContext, ((drfsw_directory_win32*)pContext->watchedDirectories.list.buffer[i - 1])->absolutePath);
- }
- }
- SetEvent(pContext->watchedDirectories.hLock);
- }
-}
-
-DRFSW_PRIVATE int drfsw_is_watching_directory_win32(drfsw_context_win32* pContext, const char* absolutePath)
-{
- if (pContext != NULL)
- {
- return drfsw_find_directory_win32(pContext, absolutePath, NULL) != NULL;
- }
-
- return 0;
-}
-
-
-DRFSW_PRIVATE int drfsw_next_event_win32(drfsw_context_win32* pContext, drfsw_event* pEvent)
-{
- int result = 0;
- if (pContext != NULL && !pContext->terminateThread)
- {
- // Wait for either the semaphore or the thread to terminate.
- HANDLE hEvents[2];
- hEvents[0] = pContext->hThread;
- hEvents[1] = pContext->eventQueue.hSemaphore;
-
- DWORD rc = WaitForMultipleObjects(sizeof(hEvents) / sizeof(hEvents[0]), hEvents, FALSE, INFINITE);
- switch (rc)
- {
- case WAIT_OBJECT_0 + 0:
- {
- // The thread has been terminated.
- result = 0;
- break;
- }
-
- case WAIT_OBJECT_0 + 1:
- {
- // We're past the semaphore block, so now we can copy of the event. We need to lock the queue before doing this in case another thread wants
- // to try pushing another event onto the queue.
- if (!pContext->terminateThread)
- {
- DWORD eventLockResult = WaitForSingleObject(pContext->eventQueue.hLock, INFINITE);
- if (eventLockResult == WAIT_OBJECT_0)
- {
- drfsw_event_queue_pop(&pContext->eventQueue, pEvent);
- }
- SetEvent(pContext->eventQueue.hLock);
-
- if (eventLockResult == WAIT_OBJECT_0)
- {
- result = 1;
- }
- else
- {
- // The lock returned early for some reason which means there must have been some kind of error or the context has been destroyed.
- result = 0;
- }
- }
-
- break;
- }
-
- default: break;
- }
- }
-
- return result;
-}
-
-DRFSW_PRIVATE int drfsw_peek_event_win32(drfsw_context_win32* pContext, drfsw_event* pEvent)
-{
- int result = 0;
- if (pContext != NULL)
- {
- DWORD eventLockResult = WaitForSingleObject(pContext->eventQueue.hLock, INFINITE);
- {
- // Make sure we decrement our semaphore counter. Don't block here.
- WaitForSingleObject(pContext->eventQueue.hSemaphore, 0);
-
- // Now we just copy over the data by popping it from the queue.
- if (eventLockResult == WAIT_OBJECT_0)
- {
- if (drfsw_event_queue_getcount(&pContext->eventQueue) > 0)
- {
- drfsw_event_queue_pop(&pContext->eventQueue, pEvent);
- result = 1;
- }
- else
- {
- result = 0;
- }
- }
- else
- {
- // Waiting on the event queue lock failed for some reason. It could mean that the context has been deleted.
- result = 0;
- }
- }
- SetEvent(pContext->eventQueue.hLock);
- }
-
- return result;
-}
-
-DRFSW_PRIVATE void drfsw_postevent_win32(drfsw_context_win32* pContext, drfsw_event* pEvent)
-{
- if (pContext != NULL && pEvent != NULL)
- {
- // Add the event to the queue.
- WaitForSingleObject(pContext->eventQueue.hLock, INFINITE);
- {
- drfsw_event_queue_pushback(&pContext->eventQueue, pEvent);
- }
- SetEvent(pContext->eventQueue.hLock);
-
-
- // Release the semaphore so that drfsw_next_event() can handle it.
- ReleaseSemaphore(pContext->eventQueue.hSemaphore, 1, NULL);
- }
-}
-
-
-
-////////////////////////////////////
-// Public API for Win32
-
-drfsw_context* drfsw_create_context()
-{
- return drfsw_create_context_win32();
-}
-
-void drfsw_delete_context(drfsw_context* pContext)
-{
- drfsw_delete_context_win32((drfsw_context_win32*)pContext);
-}
-
-
-int drfsw_add_directory(drfsw_context* pContext, const char* absolutePath)
-{
- return drfsw_add_directory_win32((drfsw_context_win32*)pContext, absolutePath);
-}
-
-void drfsw_remove_directory(drfsw_context* pContext, const char* absolutePath)
-{
- drfsw_remove_directory_win32((drfsw_context_win32*)pContext, absolutePath);
-}
-
-void drfsw_remove_all_directories(drfsw_context* pContext)
-{
- drfsw_remove_all_directories_win32((drfsw_context_win32*)pContext);
-}
-
-int drfsw_is_watching_directory(drfsw_context* pContext, const char* absolutePath)
-{
- return drfsw_is_watching_directory_win32((drfsw_context_win32*)pContext, absolutePath);
-}
-
-
-int drfsw_next_event(drfsw_context* pContext, drfsw_event* pEventOut)
-{
- return drfsw_next_event_win32((drfsw_context_win32*)pContext, pEventOut);
-}
-
-int drfsw_peek_event(drfsw_context* pContext, drfsw_event* pEventOut)
-{
- return drfsw_peek_event_win32((drfsw_context_win32*)pContext, pEventOut);
-}
-
-
-#endif // Win32
-
-
-#if defined(__clang__)
- #pragma GCC diagnostic pop
-#endif
-
-#endif //DR_FSW_IMPLEMENTATION
-
-
-
-
-/*
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-For more information, please refer to
-*/
diff --git a/source/engine/thirdparty/dr_libs/old/dr_gui.h b/source/engine/thirdparty/dr_libs/old/dr_gui.h
deleted file mode 100644
index 7283223..0000000
--- a/source/engine/thirdparty/dr_libs/old/dr_gui.h
+++ /dev/null
@@ -1,17167 +0,0 @@
-// Public Domain. See "unlicense" statement at the end of this file.
-
-// QUICK NOTES
-//
-// General
-// - dr_gui is a low-level GUI system that works on generic objects referred to as "elements".
-// - An element is the most basic unit in dr_gui. It contains basic information about it's layout and hierarchy.
-// - Elements can be used as the building blocks for more complex controls such as list boxes and scrollbars.
-// - The layout of elements use floats instead of integers. The rationale for this is that it makes it easier to do certain
-// layout arithmetic. For example, if you want to evenly distribute 3 elements across a fixed area, the integer based
-// arithmetic can cause rounding errors which cause the elements to not sit flush against the area. By using float-based
-// arithmetic we can avoid that particular issue.
-//
-// Hierarchy
-// - An element can have a parent and any number of children. If an element does not have a parent, it is referred to as the
-// top-level element.
-// - When an element is deleted, it's children will be deleted as well.
-// - Top-level elements do not have siblings.
-//
-// Event Handling
-// - The application must notify dr_gui of application-generated events such as key strokes and mouse movements. These are
-// referred to as inbound events. An event that is generated by dr_gui are referred to as outbound events.
-// - Inbound events are used to generate outbound events. For example, a mouse-move inbound event will generate an outbound
-// mouse-move event, and perhaps a mouse leave/enter pair.
-// - Outbound events are posted and handled immediately. A call to drgui_post_inbound_event() will not return until all of
-// the outbound events it generates have been handled.
-// - Inbound events are not thread safe, however an application is free to post an inbound event from any thread so long as
-// it does it's own synchronization.
-// - Inbound events will typically specify the relevant top-level element and let dr_gui do the relevant processing required
-// to generate the appropriate outbound events. For example, the mouse-move event will be specified with respect to the top-
-// level element, but dr_gui will determine the exact child element that the mouse moved on and thus should receive the
-// relevant outbound mouse-move event.
-// - There are some special events that are handled differently to normal events. The best example is the paint events. The
-// paint event is only called from drgui_draw().
-// - Key press/release events are only ever posted to the element that has the keyboard capture/focus which is set with
-// drgui_capture_keyboard(). Thus, when posting an inbound key event, a top-level element is not required when posting
-// those events. The relevant context is still required, however.
-//
-// Global Outbound Event Handling
-// - At times dr_gui will need to notify the host application in order for certain functionality to work properly. For example.
-// when the mouse is captured it won't work 100% correct unless the host application has a chance to capture the mouse against
-// the container window. Because dr_gui has no notion of a window system it relies on the host application to handle this
-// properly.
-// - A global outbound event handler should be implemented for each of the following events:
-// - on_dirty: Called when a region of an element is marked as dirty and needs to be redrawn. The application will want to
-// invalidate the container window to trigger an operating system redraw. Set this with drgui_set_global_on_dirty().
-// - on_capture_mouse: Called when the mouse is captured and gives the application the opportunity to capture the mouse against
-// the container window at the operating system level. Set with drgui_set_global_on_capture_mouse().
-// - on_release_mouse: Called when the mouse is released. The opposite of on_capture_mouse.
-// - on_capture_keyboard: Called when an element is given the keyboard focus and gives the application the opportunity to
-// apply the keyboard focus to the container window. Set with drgui_set_global_on_capture_keyboard().
-// - on_release_keyboard: Called when an element loses the keyboard focus. The opposite of on_capture_keyboard.
-// - on_change_cursor: Called when the current cursor needs to be changed as a result of the mouse moving over a new element.
-//
-// Layout
-// - An element's data structure does not store it's relative position but instead stores it's absolute position. The rationale
-// for this is that storing it as relative complicates absolute positioning calculations because it would need to do a recursive
-// traversal of the element's ancestors.
-// - Child elements can be scaled by setting an element's inner scale. The inner scale does not scale the element itself - only
-// it's children.
-// - When an element is drawn, everything is scaled by it's inner scale. For example, if the inner scale is 2x and a 100x100 quad
-// is drawn, the quad will be scaled to 200x200. An exception to this rule is fonts, which are never scaled. This 0s because
-// text is always drawn based on the size of the font.
-// - Applications should only need to work on unscaled coordinates. That is, an application should never need to worry about
-// manual scaling, except for fonts. When positioning and sizing child elements, they should be done based on unscaled
-// coordinates.
-// - Use the inner scale system for DPI awareness.
-// - The inner scale is applied recursively. That is, if a top level element has it's inner scale set to 2x and one of it's
-// children has an inner scale of 2x, the actual inner scale of the child element will be 4x.
-//
-//
-// Drawing/Painting
-// - Drawing is one of the more complex parts of the GUI because it can be a bit unintuitive regarding exactly when an element
-// is drawn and when a drawing function is allowed to be called.
-// - To draw an element, call drgui_draw(). This takes a pointer to the element to draw and the rectangle region that should
-// be redrawn. Any children that fall inside the specified rectangle region will be redrawn as well. You do not want to call
-// drgui_draw() on a parent element and then again on it's children because dr_gui will do that automatically.
-// - drgui_draw() does not draw anything directly, but rather calls painting callback routines which is where the actual
-// drawing takes place.
-// - Sometimes an application will need to be told when a region of an element is dirty and needs redrawing. An example is
-// event-driven, non real-time applications such as normal desktop applications. To mark an element as dirty, you call the
-// drgui_dirty() function which takes the element that is dirty, and the rectangle region that needs to be redrawn. This
-// does not redraw the element immediately, but instead posts an on_dirty event for the application. Marking regions as dirty
-// is not strictly required, but you should prefer it for event-driven applications that require painting operations to be
-// performed at specific times (such as inside Win32's WM_PAINT messages).
-// - Some operations will cause a region of an element to become dirty - such as when it is resized. dr_gui will
-// automatically mark the relevant regions as dirty which in turn will cause a paint message to be posted. If this is not
-// required, it can be disabled with drgui_disable_auto_dirty(). You may want to disable automatic dirtying if you are
-// running a real-time application like a game which would redraw the entire GUI every frame anyway and thus not require
-// handling of the paint message.
-// - Real-time application guidelines (games, etc.):
-// - drgui_disable_auto_dirty()
-// - drgui_draw(pTopLevelElement, 0, 0, viewportWidth, viewportHeight) at the end of every frame after your main loop.
-//
-
-
-//
-// OPTIONS
-//
-// #define DRGUI_NO_DR_2D
-// Disable dr_2d integration. Disabling dr_2d will require you to implement your own drawing callbacks.
-//
-// #define DRGUI_NO_TEXT_EDITING
-// Disables the text box control and text engine.
-
-
-//
-// EXAMPLES
-//
-// Basic Drawing:
-//
-// drgui_draw(pTopLevelElement, 0, 0, drgui_get_width(pTopLevelElement), drgui_get_height(pTopLevelElement));
-//
-// -------------------------
-//
-// Event-Driven Drawing (Win32):
-//
-// void my_global_on_dirty_win32(drgui_element* pElement, drgui_rect relativeRect) {
-// drgui_rect absoluteRect = relativeRect;
-// drgui_make_rect_absolute(pElement, &absoluteRect);
-//
-// RECT rect;
-// rect.left = absoluteRect.left;
-// rect.top = absoluteRect.top;
-// rect.right = absoluteRect.right;
-// rect.height = absoluteRect.bottom;
-// InvalidateRect((HWND)drgui_get_user_data(drgui_find_top_level_element(pElement)), &rect, FALSE);
-// }
-//
-// ...
-//
-// LRESULT CALLBACK MyWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
-// ...
-// drgui_element* pTopLevelElement = (drgui_element*)GetWindowLongPtr(hWnd, 0);
-// if (pTopLevelElement != NULL) {
-// switch (msg) {
-// ...
-// case WM_PAINT:
-// {
-// RECT rect;
-// if (GetUpdateRect(hWnd, &rect, FALSE)) {
-// drgui_draw(pTopLevelElement, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
-// }
-//
-// break;
-// }
-// ...
-// }
-// }
-// ...
-// }
-//
-
-
-
-#ifndef dr_gui_h
-#define dr_gui_h
-
-#ifndef DRGUI_NO_DR_2D
-// If you're using easy_draw integration, set the path below to the relative location of dr_2d. By default, the
-// following structure is assumed:
-//
-// - dr_libs
-// - dr_2d.h
-// - dr_gui.h
-#include "dr_2d.h"
-#endif
-
-#include
-
-#ifndef DRGUI_MAX_FONT_FAMILY_LENGTH
-#define DRGUI_MAX_FONT_FAMILY_LENGTH 128
-#endif
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct drgui_context drgui_context;
-typedef struct drgui_element drgui_element;
-typedef struct drgui_color drgui_color;
-typedef struct drgui_rect drgui_rect;
-typedef struct drgui_painting_callbacks drgui_painting_callbacks;
-typedef struct drgui_font drgui_font;
-typedef struct drgui_image drgui_image;
-typedef struct drgui_font_metrics drgui_font_metrics;
-typedef struct drgui_glyph_metrics drgui_glyph_metrics;
-
-typedef unsigned char drgui_byte;
-typedef unsigned int drgui_key;
-
-typedef void* drgui_resource;
-
-/// Common system cursors.
-typedef enum
-{
- drgui_cursor_none,
- drgui_cursor_default,
- drgui_cursor_arrow = drgui_cursor_default,
- drgui_cursor_text,
- drgui_cursor_cross,
- drgui_cursor_size_ns, // North/South resize arrows.
- drgui_cursor_size_we, // West/East resize arrows.
- drgui_cursor_size_nesw, // North/East, South/West resize arrows.
- drgui_cursor_size_nwse // North/West, South/East resize arrows.
-} drgui_cursor_type;
-
-/// Font weights.
-typedef enum
-{
- drgui_font_weight_medium,
- drgui_font_weight_thin,
- drgui_font_weight_extra_light,
- drgui_font_weight_light,
- drgui_font_weight_semi_light,
- drgui_font_weight_book,
- drgui_font_weight_semi_bold,
- drgui_font_weight_bold,
- drgui_font_weight_extra_bold,
- drgui_font_weight_heavy,
- drgui_font_weight_extra_heavy,
-
- drgui_font_weight_normal = drgui_font_weight_medium,
- drgui_font_weight_default = drgui_font_weight_medium
-
-} drgui_font_weight;
-
-/// Font slants.
-typedef enum
-{
- drgui_font_slant_none,
- drgui_font_slant_italic,
- drgui_font_slant_oblique
-
-} drgui_font_slant;
-
-/// Image formats.
-typedef enum
-{
- drgui_image_format_rgba8,
- drgui_image_format_bgra8,
- drgui_image_format_argb8,
-} drgui_image_format;
-
-
-/// Font metrics.
-struct drgui_font_metrics
-{
- int ascent;
- int descent;
- int lineHeight;
- int spaceWidth;
-};
-
-/// Glyph metrics.
-struct drgui_glyph_metrics
-{
- int width;
- int height;
- int originX;
- int originY;
- int advanceX;
- int advanceY;
-};
-
-
-/// Structure representing an RGBA color. Color components are specified in the range of 0 - 255.
-struct drgui_color
-{
- drgui_byte r;
- drgui_byte g;
- drgui_byte b;
- drgui_byte a;
-};
-
-/// Structure representing a rectangle.
-struct drgui_rect
-{
- float left;
- float top;
- float right;
- float bottom;
-};
-
-
-#define DRGUI_IMAGE_DRAW_BACKGROUND (1 << 0)
-#define DRGUI_IMAGE_HINT_NO_ALPHA (1 << 1)
-#define DRGUI_IMAGE_DRAW_BOUNDS (1 << 2)
-#define DRGUI_IMAGE_CLIP_BOUNDS (1 << 3) //< Clips the image to the bounds
-#define DRGUI_IMAGE_ALIGN_CENTER (1 << 4)
-
-#define DRGUI_READ (1 << 0)
-#define DRGUI_WRITE (1 << 1)
-
-#define DRGUI_FONT_NO_CLEARTYPE (1 << 0)
-
-typedef struct
-{
- /// The destination position on the x axis. This is ignored if the DR2D_IMAGE_ALIGN_CENTER option is set.
- float dstX;
-
- /// The destination position on the y axis. This is ignored if the DR2D_IMAGE_ALIGN_CENTER option is set.
- float dstY;
-
- /// The destination width.
- float dstWidth;
-
- /// The destination height.
- float dstHeight;
-
-
- /// The source offset on the x axis.
- float srcX;
-
- /// The source offset on the y axis.
- float srcY;
-
- /// The source width.
- float srcWidth;
-
- /// The source height.
- float srcHeight;
-
-
- /// The position of the destination's bounds on the x axis.
- float dstBoundsX;
-
- /// The position of the destination's bounds on the y axis.
- float dstBoundsY;
-
- /// The width of the destination's bounds.
- float dstBoundsWidth;
-
- /// The height of the destination's bounds.
- float dstBoundsHeight;
-
-
- /// The foreground tint color. This is not applied to the background color, and the alpha component is ignored.
- drgui_color foregroundTint;
-
- /// The background color. Only used if the DR2D_IMAGE_DRAW_BACKGROUND option is set.
- drgui_color backgroundColor;
-
- /// The bounds color. This color is used for the region of the bounds that sit on the outside of the destination rectangle. This will
- /// usually be set to the same value as backgroundColor, but it could also be used to draw a border around the image.
- drgui_color boundsColor;
-
-
- /// Flags for controlling how the image should be drawn.
- unsigned int options;
-
-} drgui_draw_image_args;
-
-
-typedef void (* drgui_callback)();
-
-typedef void (* drgui_on_move_proc) (drgui_element* pElement, float newRelativePosX, float newRelativePosY);
-typedef void (* drgui_on_size_proc) (drgui_element* pElement, float newWidth, float newHeight);
-typedef void (* drgui_on_mouse_enter_proc) (drgui_element* pElement);
-typedef void (* drgui_on_mouse_leave_proc) (drgui_element* pElement);
-typedef void (* drgui_on_mouse_move_proc) (drgui_element* pElement, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-typedef void (* drgui_on_mouse_button_down_proc) (drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-typedef void (* drgui_on_mouse_button_up_proc) (drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-typedef void (* drgui_on_mouse_button_dblclick_proc)(drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-typedef void (* drgui_on_mouse_wheel_proc) (drgui_element* pElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-typedef void (* drgui_on_key_down_proc) (drgui_element* pElement, drgui_key key, int stateFlags);
-typedef void (* drgui_on_key_up_proc) (drgui_element* pElement, drgui_key key, int stateFlags);
-typedef void (* drgui_on_printable_key_down_proc) (drgui_element* pElement, unsigned int character, int stateFlags);
-typedef void (* drgui_on_paint_proc) (drgui_element* pElement, drgui_rect relativeRect, void* pPaintData);
-typedef void (* drgui_on_dirty_proc) (drgui_element* pElement, drgui_rect relativeRect);
-typedef bool (* drgui_on_hittest_proc) (drgui_element* pElement, float relativePosX, float relativePosY);
-typedef void (* drgui_on_capture_mouse_proc) (drgui_element* pElement);
-typedef void (* drgui_on_release_mouse_proc) (drgui_element* pElement);
-typedef void (* drgui_on_capture_keyboard_proc) (drgui_element* pElement, drgui_element* pPrevCapturedElement);
-typedef void (* drgui_on_release_keyboard_proc) (drgui_element* pElement, drgui_element* pNewCapturedElement);
-typedef void (* drgui_on_change_cursor_proc) (drgui_element* pElement, drgui_cursor_type cursor);
-typedef void (* drgui_on_delete_element_proc) (drgui_element* pElement);
-typedef void (* drgui_on_log) (drgui_context* pContext, const char* message);
-
-typedef void (* drgui_draw_begin_proc) (void* pPaintData);
-typedef void (* drgui_draw_end_proc) (void* pPaintData);
-typedef void (* drgui_set_clip_proc) (drgui_rect relativeRect, void* pPaintData);
-typedef void (* drgui_get_clip_proc) (drgui_rect* pRectOut, void* pPaintData);
-typedef void (* drgui_draw_line_proc) (float startX, float startY, float endX, float endY, float width, drgui_color color, void* pPaintData);
-typedef void (* drgui_draw_rect_proc) (drgui_rect relativeRect, drgui_color color, void* pPaintData);
-typedef void (* drgui_draw_rect_outline_proc) (drgui_rect relativeRect, drgui_color color, float outlineWidth, void* pPaintData);
-typedef void (* drgui_draw_rect_with_outline_proc) (drgui_rect relativeRect, drgui_color color, float outlineWidth, drgui_color outlineColor, void* pPaintData);
-typedef void (* drgui_draw_round_rect_proc) (drgui_rect relativeRect, drgui_color color, float radius, void* pPaintData);
-typedef void (* drgui_draw_round_rect_outline_proc) (drgui_rect relativeRect, drgui_color color, float radius, float outlineWidth, void* pPaintData);
-typedef void (* drgui_draw_round_rect_with_outline_proc) (drgui_rect relativeRect, drgui_color color, float radius, float outlineWidth, drgui_color outlineColor, void* pPaintData);
-typedef void (* drgui_draw_text_proc) (drgui_resource font, const char* text, int textLengthInBytes, float posX, float posY, drgui_color color, drgui_color backgroundColor, void* pPaintData);
-typedef void (* drgui_draw_image_proc) (drgui_resource image, drgui_draw_image_args* pArgs, void* pPaintData);
-
-typedef drgui_resource (* drgui_create_font_proc) (void* pPaintingContext, const char* family, unsigned int size, drgui_font_weight weight, drgui_font_slant slant, float rotation, unsigned int flags);
-typedef void (* drgui_delete_font_proc) (drgui_resource font);
-typedef unsigned int (* drgui_get_font_size_proc) (drgui_resource font);
-typedef bool (* drgui_get_font_metrics_proc) (drgui_resource font, drgui_font_metrics* pMetricsOut);
-typedef bool (* drgui_get_glyph_metrics_proc) (drgui_resource font, unsigned int utf32, drgui_glyph_metrics* pMetricsOut);
-typedef bool (* drgui_measure_string_proc) (drgui_resource font, const char* text, size_t textSizeInBytes, float* pWidthOut, float* pHeightOut);
-typedef bool (* drgui_get_text_cursor_position_from_point_proc)(drgui_resource font, const char* text, size_t textSizeInBytes, float maxWidth, float inputPosX, float* pTextCursorPosXOut, size_t* pCharacterIndexOut);
-typedef bool (* drgui_get_text_cursor_position_from_char_proc) (drgui_resource font, const char* text, size_t characterIndex, float* pTextCursorPosXOut);
-
-typedef drgui_resource (* drgui_create_image_proc) (void* pPaintingContext, unsigned int width, unsigned int height, drgui_image_format format, unsigned int stride, const void* pImageData);
-typedef void (* drgui_delete_image_proc) (drgui_resource image);
-typedef drgui_image_format (* drgui_get_optimal_image_format_proc)(void* pPaintingContext);
-typedef void (* drgui_get_image_size_proc) (drgui_resource image, unsigned int* pWidthOut, unsigned int* pHeightOut);
-typedef void* (* drgui_map_image_data_proc) (drgui_resource image, unsigned int accessFlags);
-typedef void (* drgui_unmap_image_data_proc) (drgui_resource image);
-
-typedef bool (* drgui_visible_iteration_proc)(drgui_element* pElement, drgui_rect *pRelativeRect, void* pUserData);
-
-
-// Key state flags.
-#define DRGUI_MOUSE_BUTTON_LEFT_DOWN (1 << 0)
-#define DRGUI_MOUSE_BUTTON_RIGHT_DOWN (1 << 1)
-#define DRGUI_MOUSE_BUTTON_MIDDLE_DOWN (1 << 2)
-#define DRGUI_MOUSE_BUTTON_4_DOWN (1 << 3)
-#define DRGUI_MOUSE_BUTTON_5_DOWN (1 << 4)
-#define DRGUI_KEY_STATE_SHIFT_DOWN (1 << 5) // Whether or not a shift key is down at the time the input event is handled.
-#define DRGUI_KEY_STATE_CTRL_DOWN (1 << 6) // Whether or not a ctrl key is down at the time the input event is handled.
-#define DRGUI_KEY_STATE_ALT_DOWN (1 << 7) // Whether or not an alt key is down at the time the input event is handled.
-#define DRGUI_KEY_STATE_AUTO_REPEATED (1 << 31) // Whether or not the key press is generated due to auto-repeating. Only used with key down events.
-
-// Common mouse buttons.
-#define DRGUI_MOUSE_BUTTON_LEFT 1
-#define DRGUI_MOUSE_BUTTON_RIGHT 2
-#define DRGUI_MOUSE_BUTTON_MIDDLE 3
-
-// Common key codes.
-#define DRGUI_BACKSPACE 0xff08
-#define DRGUI_SHIFT 0xff10
-#define DRGUI_ESCAPE 0xff1b
-#define DRGUI_PAGE_UP 0xff55
-#define DRGUI_PAGE_DOWN 0xff56
-#define DRGUI_END 0xff57
-#define DRGUI_HOME 0xff50
-#define DRGUI_ARROW_LEFT 0x8fb
-#define DRGUI_ARROW_UP 0x8fc
-#define DRGUI_ARROW_RIGHT 0x8fd
-#define DRGUI_ARROW_DOWN 0x8fe
-#define DRGUI_DELETE 0xffff
-#define DRGUI_F1 0xffbe
-#define DRGUI_F2 0xffbf
-#define DRGUI_F3 0xffc0
-#define DRGUI_F4 0xffc1
-#define DRGUI_F5 0xffc2
-#define DRGUI_F6 0xffc3
-#define DRGUI_F7 0xffc4
-#define DRGUI_F8 0xffc5
-#define DRGUI_F9 0xffc6
-#define DRGUI_F10 0xffc7
-#define DRGUI_F11 0xffc8
-#define DRGUI_F12 0xffc9
-
-static size_t drgui_strcpy(char* dst, size_t dstSize, const char* src)
-{
- if (strcpy_s(dst, dstSize, src) == 0) {
- return strlen(dst);
- }
-
- return 0;
-}
-
-static inline size_t drgui_key_to_string(drgui_key key, char* strOut, size_t strOutSize)
-{
- if (strOut == NULL || strOutSize == 0) {
- return 0;
- }
-
- if (strOutSize == 1) {
- strOut[0] = '\0';
- return 0;
- }
-
-
- switch (key)
- {
- case DRGUI_BACKSPACE: return drgui_strcpy(strOut, strOutSize, "Backspace");
- case DRGUI_SHIFT: return drgui_strcpy(strOut, strOutSize, "Shift");
- case DRGUI_ESCAPE: return drgui_strcpy(strOut, strOutSize, "Escape");
- case DRGUI_PAGE_UP: return drgui_strcpy(strOut, strOutSize, "Page Up");
- case DRGUI_PAGE_DOWN: return drgui_strcpy(strOut, strOutSize, "Page Down");
- case DRGUI_END: return drgui_strcpy(strOut, strOutSize, "End");
- case DRGUI_HOME: return drgui_strcpy(strOut, strOutSize, "Home");
- case DRGUI_ARROW_LEFT: return drgui_strcpy(strOut, strOutSize, "Arrow Left");
- case DRGUI_ARROW_UP: return drgui_strcpy(strOut, strOutSize, "Arrow Up");
- case DRGUI_ARROW_RIGHT: return drgui_strcpy(strOut, strOutSize, "Arrow Right");
- case DRGUI_ARROW_DOWN: return drgui_strcpy(strOut, strOutSize, "Arrow Down");
- case DRGUI_DELETE: return drgui_strcpy(strOut, strOutSize, "Delete");
- case DRGUI_F1: return drgui_strcpy(strOut, strOutSize, "F1");
- case DRGUI_F2: return drgui_strcpy(strOut, strOutSize, "F2");
- case DRGUI_F3: return drgui_strcpy(strOut, strOutSize, "F3");
- case DRGUI_F4: return drgui_strcpy(strOut, strOutSize, "F4");
- case DRGUI_F5: return drgui_strcpy(strOut, strOutSize, "F5");
- case DRGUI_F6: return drgui_strcpy(strOut, strOutSize, "F6");
- case DRGUI_F7: return drgui_strcpy(strOut, strOutSize, "F7");
- case DRGUI_F8: return drgui_strcpy(strOut, strOutSize, "F8");
- case DRGUI_F9: return drgui_strcpy(strOut, strOutSize, "F9");
- case DRGUI_F10: return drgui_strcpy(strOut, strOutSize, "F10");
- case DRGUI_F11: return drgui_strcpy(strOut, strOutSize, "F11");
- case DRGUI_F12: return drgui_strcpy(strOut, strOutSize, "F12");
- }
-
- if (key >= 32 && key <= 126) {
- strOut[0] = (char)key;
- strOut[1] = '\0';
- return 1;
- }
-
- // TODO: Non-ascii characters.
- return 0;
-}
-
-static inline drgui_key drgui_key_parse(const char* str)
-{
- if (str == NULL || str[0] == '\0') {
- return 0;
- }
-
- if (_stricmp(str, "backspace") == 0) return DRGUI_BACKSPACE;
- if (_stricmp(str, "shift") == 0) return DRGUI_SHIFT;
- if (_stricmp(str, "escape") == 0) return DRGUI_ESCAPE;
- if (_stricmp(str, "page up") == 0 || _stricmp(str, "pageup") == 0) return DRGUI_PAGE_UP;
- if (_stricmp(str, "page down") == 0 || _stricmp(str, "pagedown") == 0) return DRGUI_PAGE_DOWN;
- if (_stricmp(str, "end") == 0) return DRGUI_END;
- if (_stricmp(str, "home") == 0) return DRGUI_HOME;
- if (_stricmp(str, "arrow left") == 0 || _stricmp(str, "arrowleft") == 0) return DRGUI_ARROW_LEFT;
- if (_stricmp(str, "arrow up") == 0 || _stricmp(str, "arrowup") == 0) return DRGUI_ARROW_UP;
- if (_stricmp(str, "arrow right") == 0 || _stricmp(str, "arrowright") == 0) return DRGUI_ARROW_RIGHT;
- if (_stricmp(str, "arrow down") == 0 || _stricmp(str, "arrowdown") == 0) return DRGUI_ARROW_DOWN;
- if (_stricmp(str, "delete") == 0) return DRGUI_BACKSPACE;
-
- if (str[0] == 'F' || str[0] == 'f') {
- if (str[1] == '1') {
- if (str[2] == '\0') {
- return DRGUI_F1;
- } else {
- if (str[2] == '0' && str[2] == '\0') return DRGUI_F10;
- if (str[2] == '1' && str[2] == '\0') return DRGUI_F11;
- if (str[2] == '2' && str[2] == '\0') return DRGUI_F12;
- }
- }
- if (str[1] == '2' && str[2] == '\0') return DRGUI_F2;
- if (str[1] == '3' && str[2] == '\0') return DRGUI_F3;
- if (str[1] == '4' && str[2] == '\0') return DRGUI_F4;
- if (str[1] == '5' && str[2] == '\0') return DRGUI_F5;
- if (str[1] == '6' && str[2] == '\0') return DRGUI_F6;
- if (str[1] == '7' && str[2] == '\0') return DRGUI_F7;
- if (str[1] == '8' && str[2] == '\0') return DRGUI_F8;
- if (str[1] == '9' && str[2] == '\0') return DRGUI_F9;
- }
-
-
- // ASCII characters.
- if (str[0] >= 32 && str[0] <= 126 && str[1] == '\0') {
- return str[0];
- }
-
- if (_stricmp(str, "tab") == 0) {
- return '\t';
- }
-
-
- // TODO: Non-ascii characters.
- return 0;
-}
-
-
-/// Structure containing callbacks for painting routines.
-struct drgui_painting_callbacks
-{
- drgui_draw_begin_proc drawBegin;
- drgui_draw_end_proc drawEnd;
-
- drgui_set_clip_proc setClip;
- drgui_get_clip_proc getClip;
-
- drgui_draw_line_proc drawLine;
- drgui_draw_rect_proc drawRect;
- drgui_draw_rect_outline_proc drawRectOutline;
- drgui_draw_rect_with_outline_proc drawRectWithOutline;
- drgui_draw_round_rect_proc drawRoundRect;
- drgui_draw_round_rect_outline_proc drawRoundRectOutline;
- drgui_draw_round_rect_with_outline_proc drawRoundRectWithOutline;
- drgui_draw_text_proc drawText;
- drgui_draw_image_proc drawImage;
-
- drgui_create_font_proc createFont;
- drgui_delete_font_proc deleteFont;
- drgui_get_font_size_proc getFontSize;
- drgui_get_font_metrics_proc getFontMetrics;
- drgui_get_glyph_metrics_proc getGlyphMetrics;
- drgui_measure_string_proc measureString;
- drgui_get_text_cursor_position_from_point_proc getTextCursorPositionFromPoint;
- drgui_get_text_cursor_position_from_char_proc getTextCursorPositionFromChar;
-
- drgui_create_image_proc createImage;
- drgui_delete_image_proc deleteImage;
- drgui_get_optimal_image_format_proc getOptimalImageFormat;
- drgui_get_image_size_proc getImageSize;
- drgui_map_image_data_proc mapImageData;
- drgui_unmap_image_data_proc unmapImageData;
-};
-
-struct drgui_image
-{
- /// A pointer to the context that owns this image.
- drgui_context* pContext;
-
- /// The resource handle that is passed around to the callback functions.
- drgui_resource hResource;
-};
-
-struct drgui_font
-{
- /// A pointer to the context that owns this font.
- drgui_context* pContext;
-
- /// The font family.
- char family[DRGUI_MAX_FONT_FAMILY_LENGTH];
-
- /// The base size of the font. This is set to the value that was used to create the font in the first place.
- unsigned int size;
-
- /// The font's weight.
- drgui_font_weight weight;
-
- /// The fon't slant.
- drgui_font_slant slant;
-
- /// The fon't rotation.
- float rotation;
-
- /// The font's flags. Can be a combination of the following:
- /// DRGUI_FONT_NO_CLEARTYPE
- unsigned int flags;
-
- /// The internal font. This is created by the rendering backend.
- drgui_resource internalFont;
-};
-
-
-struct drgui_element
-{
- /// A pointer to the context that owns this element. This should never be null for valid elements.
- drgui_context* pContext;
-
-
- /// A pointer to the parent element. This can be null in which case this element is the parent.
- drgui_element* pParent;
-
- /// A pointer to the first child element.
- drgui_element* pFirstChild;
-
- /// A pointer to the last child element.
- drgui_element* pLastChild;
-
- /// A pointer to the next sibling element.
- drgui_element* pNextSibling;
-
- /// A pointer ot the previous sibing element.
- drgui_element* pPrevSibling;
-
-
- /// A pointer to the next dead element. When an element is deleted during an event handler it is not deleted straight away but
- /// rather at the end of the current batch of event processing. Dead elements are stored in a linked list, with this pointer
- /// acting as the link between items. This will be null if the element is the last in the list, or is not marked as dead. Note
- /// that this should not be used to check if the element is marked as dead - use the IS_ELEMENT_DEAD flag instead.
- drgui_element* pNextDeadElement;
-
-
- /// The type of the element, as a string. This is only every used by the host application, and is intended to be used as way
- /// to selectively perform certain operations on specific types of GUI elements.
- char type[64];
-
-
- /// The absolute position of the element on the x axis. A position of 0 is the left side of the surface it is attached to.
- float absolutePosX;
-
- /// The absolute position of the element on the y axis. A position of 0 is the top of the surface it is attached to.
- float absolutePosY;
-
- /// The width of the element.
- float width;
-
- /// The height of the element.
- float height;
-
-
- /// The cursor. Defaults to drge_cursor_default.
- drgui_cursor_type cursor;
-
- /// Boolean flags.
- unsigned int flags;
-
- // The region of the element that's dirty.
- drgui_rect dirtyRect;
-
-
- /// The function to call when the element's relative position moves.
- drgui_on_move_proc onMove;
-
- /// The function to call when the element's size changes.
- drgui_on_size_proc onSize;
-
- /// The function to call when the mouse enters the given element.
- drgui_on_mouse_enter_proc onMouseEnter;
-
- /// The function to call when the mouse leaves the given element.
- drgui_on_mouse_leave_proc onMouseLeave;
-
- /// The function to call when the mouse is moved while over the element.
- drgui_on_mouse_move_proc onMouseMove;
-
- /// The function to call when a mouse buttonis pressed while over the element.
- drgui_on_mouse_button_down_proc onMouseButtonDown;
-
- /// The function to call when a mouse button is released while over the element.
- drgui_on_mouse_button_up_proc onMouseButtonUp;
-
- /// The function to call when a mouse button is double-clicked while over the element.
- drgui_on_mouse_button_dblclick_proc onMouseButtonDblClick;
-
- /// The function to call when the mouse wheel it turned while over the element.
- drgui_on_mouse_wheel_proc onMouseWheel;
-
- /// The function to call when a key on the keyboard is pressed or auto-repeated.
- drgui_on_key_down_proc onKeyDown;
-
- /// The function to call when a key on the keyboard is released.
- drgui_on_key_up_proc onKeyUp;
-
- /// The function to call when a printable character is pressed or auto-repeated. This would be used for text editing.
- drgui_on_printable_key_down_proc onPrintableKeyDown;
-
- /// The function to call when the paint event is received.
- drgui_on_paint_proc onPaint;
-
- /// The function to call when the element is marked as dirty.
- drgui_on_dirty_proc onDirty;
-
- /// The function to call when a hit test needs to be performed.
- drgui_on_hittest_proc onHitTest;
-
- /// The event handler to call when an element receives the mouse focus.
- drgui_on_capture_mouse_proc onCaptureMouse;
-
- /// The event handler to call when an element loses the mouse focus.
- drgui_on_release_mouse_proc onReleaseMouse;
-
- /// The event handler to call when an element receives the keyboard focus.
- drgui_on_capture_keyboard_proc onCaptureKeyboard;
-
- /// The event handler to call when an element loses the keyboard focus.
- drgui_on_release_keyboard_proc onReleaseKeyboard;
-
-
- /// The size of the extra data.
- size_t extraDataSize;
-
- /// A pointer to the extra data.
- drgui_byte pExtraData[1];
-};
-
-struct drgui_context
-{
- /// The paiting context.
- void* pPaintingContext;
-
- /// The painting callbacks.
- drgui_painting_callbacks paintingCallbacks;
-
-
- /// The inbound event counter. This is incremented with drgui_begin_inbound_event() and decremented with
- /// drgui_end_inbound_event(). We use this to determine whether or not an inbound event is being processed.
- int inboundEventCounter;
-
- /// The outbound event counter that we use as the "lock" for outbound events. All outbound events are posted from
- /// inbound events, and all inbound events are already synchronized so we don't need to use a mutex. This is mainly
- /// used as a way to check for erroneous outbound event generation.
- int outboundEventLockCounter;
-
- /// A pointer to the first element that has been marked as dead. Elements marked as dead are stored as a linked list.
- drgui_element* pFirstDeadElement;
-
- /// A pointer to the element that is sitting directly under the mouse. This is updated on every inbound mouse move event
- /// and is used for determining when a mouse enter/leave event needs to be posted.
- drgui_element* pElementUnderMouse;
-
- /// A pointer to the element with the mouse capture.
- drgui_element* pElementWithMouseCapture;
-
- /// A pointer to the element with the keyboard focus.
- drgui_element* pElementWithKeyboardCapture;
-
- /// A pointer to the element that wants the keyboard focus. If for some reason an element isn't able to immediately
- /// capture the keyboard (such as while in the middle of a release_keyboard event handler) this will be set to that
- /// particular element. This will then be used to capture the keyboard at a later time when it is able.
- drgui_element* pElementWantingKeyboardCapture;
-
- /// The current cursor.
- drgui_cursor_type currentCursor;
-
- /// Boolean flags.
- unsigned int flags;
-
-
- /// The global event callback to call when an element is marked as dirty.
- drgui_on_dirty_proc onGlobalDirty;
-
- /// The global event handler to call when an element captures the mouse.
- drgui_on_capture_mouse_proc onGlobalCaptureMouse;
-
- /// The global event handler to call when an element releases the mouse.
- drgui_on_release_mouse_proc onGlobalReleaseMouse;
-
- /// The global event handler to call when an element captures the keyboard.
- drgui_on_capture_keyboard_proc onGlobalCaptureKeyboard;
-
- /// The global event handler to call when an element releases the keyboard.
- drgui_on_release_keyboard_proc onGlobalReleaseKeyboard;
-
- /// The global event handler to call when the system cursor needs to change.
- drgui_on_change_cursor_proc onChangeCursor;
-
- /// The function to call when an element is deleted.
- drgui_on_delete_element_proc onDeleteElement;
-
-
- /// The function to call when a log message is posted.
- drgui_on_log onLog;
-
-
-
- /// A pointer to the top level element that was passed in from the last inbound mouse move event.
- drgui_element* pLastMouseMoveTopLevelElement;
-
- /// The position of the mouse that was passed in from the last inbound mouse move event.
- float lastMouseMovePosX;
- float lastMouseMovePosY;
-
-
- // A pointer to the list of dirty elements.
- drgui_element** ppDirtyElements;
-
- // The size of the buffer containing the dirty elements.
- size_t dirtyElementBufferSize;
-
- // The number of dirty top-level elements.
- size_t dirtyElementCount;
-
- /// The counter to use when determining whether or not an on_dirty event needs to be posted. This is incremented with
- /// drgui_begin_auto_dirty() and decremented with drgui_end_auto_dirty(). When the counter is decremented and hits
- /// zero, the on_dirty event will be posted.
- unsigned int dirtyCounter;
-};
-
-
-
-/////////////////////////////////////////////////////////////////
-//
-// CORE API
-//
-/////////////////////////////////////////////////////////////////
-
-/// Creates a context.
-drgui_context* drgui_create_context();
-
-/// Deletes a context and everything that it created.
-void drgui_delete_context(drgui_context* pContext);
-
-
-
-/////////////////////////////////////////////////////////////////
-// Events
-
-/// Posts a mouse leave inbound event.
-///
-/// @remarks
-/// The intention behind this event is to allow the application to let dr_gui know that the mouse have left the window. Since dr_gui does
-/// not have any notion of a window it must rely on the host application to notify it.
-void drgui_post_inbound_event_mouse_leave(drgui_element* pTopLevelElement);
-
-/// Posts a mouse move inbound event.
-void drgui_post_inbound_event_mouse_move(drgui_element* pTopLevelElement, int mousePosX, int mousePosY, int stateFlags);
-
-/// Posts a mouse button down inbound event.
-void drgui_post_inbound_event_mouse_button_down(drgui_element* pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags);
-
-/// Posts a mouse button up inbound event.
-void drgui_post_inbound_event_mouse_button_up(drgui_element* pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags);
-
-/// Posts a mouse button double-clicked inbound event.
-void drgui_post_inbound_event_mouse_button_dblclick(drgui_element* pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags);
-
-/// Posts a mouse wheel inbound event.
-void drgui_post_inbound_event_mouse_wheel(drgui_element* pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags);
-
-/// Posts a key down inbound event.
-void drgui_post_inbound_event_key_down(drgui_context* pContext, drgui_key key, int stateFlags);
-
-/// Posts a key up inbound event.
-void drgui_post_inbound_event_key_up(drgui_context* pContext, drgui_key key, int stateFlags);
-
-/// Posts a printable key down inbound event.
-///
-/// @remarks
-/// The \c character argument should be a UTF-32 code point.
-void drgui_post_inbound_event_printable_key_down(drgui_context* pContext, unsigned int character, int stateFlags);
-
-
-/// Registers the global on_dirty event callback.
-///
-/// @remarks
-/// This is called whenever a region of an element is marked as dirty and allows an application to mark the region of the
-/// container window as dirty to trigger an operating system level repaint of the window.
-void drgui_set_global_on_dirty(drgui_context* pContext, drgui_on_dirty_proc onDirty);
-
-/// Registers the global on_capture_mouse event callback.
-///
-/// @remarks
-/// This is called whenever an element receives an the mouse capture and allows an application to do operating system level
-/// mouse captures against the container window or whatnot.
-/// @par
-/// The advantage of using a global event callback is that it can be set once at the context level rather than many times
-/// at the element level.
-void drgui_set_global_on_capture_mouse(drgui_context* pContext, drgui_on_capture_mouse_proc onCaptureMouse);
-
-/// Registers the global on_release_mouse event callback.
-///
-/// @remarks
-/// This is called whenever an element loses an the mouse capture and allows an application to do operating system level
-/// mouse releases against the container window or whatnot.
-/// @par
-/// The advantage of using a global event callback is that it can be set once at the context level rather than many times
-/// at the element level.
-void drgui_set_global_on_release_mouse(drgui_context* pContext, drgui_on_release_mouse_proc onReleaseMouse);
-
-/// Registers the global on_capture_keyboard event callback.
-///
-/// @remarks
-/// This is called whenever an element receives an the keyboard capture and allows an application to do an operating system level
-/// keyboard focus against the container window or whatnot.
-/// @par
-/// The advantage of using a global event callback is that it can be set once at the context level rather than many times
-/// at the element level.
-void drgui_set_global_on_capture_keyboard(drgui_context* pContext, drgui_on_capture_keyboard_proc onCaptureKeyboard);
-
-/// Registers the global on_release_keyboard event callback.
-///
-/// @remarks
-/// This is called whenever an element loses an the keyboard capture and allows an application to do an operating system level
-/// keyboard release against the container window or whatnot.
-/// @par
-/// The advantage of using a global event callback is that it can be set once at the context level rather than many times
-/// at the element level.
-void drgui_set_global_on_release_keyboard(drgui_context* pContext, drgui_on_capture_keyboard_proc onReleaseKeyboard);
-
-/// Sets the global on_change_cursor event callback.
-///
-/// @remarks
-/// This is called whenever the operating system needs to change the cursor.
-void drgui_set_global_on_change_cursor(drgui_context* pContext, drgui_on_change_cursor_proc onChangeCursor);
-
-/// Sets the function to call when an element is deleted.
-void drgui_set_on_delete_element(drgui_context* pContext, drgui_on_delete_element_proc onDeleteElement);
-
-
-/// Registers the callback to call when a log message is posted.
-void drgui_set_on_log(drgui_context* pContext, drgui_on_log onLog);
-
-
-
-
-/////////////////////////////////////////////////////////////////
-// Elements
-
-/// Creates an element.
-drgui_element* drgui_create_element(drgui_context* pContext, drgui_element* pParent, size_t extraDataSize, const void* pExtraData);
-
-/// Deletes and element.
-void drgui_delete_element(drgui_element* pElement);
-
-
-/// Retrieves the size of the extra data of the given element, in bytes.
-size_t drgui_get_extra_data_size(drgui_element* pElement);
-
-/// Retrieves a pointer to the extra data of the given element.
-void* drgui_get_extra_data(drgui_element* pElement);
-
-
-/// Sets the type of the element.
-///
-/// The type name cannot be more than 63 characters in length.
-bool drgui_set_type(drgui_element* pElement, const char* type);
-
-/// Retrieves the type fo the element.
-const char* drgui_get_type(drgui_element* pElement);
-
-/// Determines whether or not the given element is of the given type.
-bool drgui_is_of_type(drgui_element* pElement, const char* type);
-
-
-/// Hides the given element.
-void drgui_hide(drgui_element *pElement);
-
-/// Shows the given element.
-void drgui_show(drgui_element* pElement);
-
-/// Determines whether or not the element is marked as visible.
-///
-/// @remarks
-/// This is a direct accessor for the internal visible flag of the element and is not recursive. Thus, if this element is
-/// marked as visible, but it's parent is invisible, it will still return true. Use drgui_is_visible_recursive() to do
-/// a recursive visibility check.
-bool drgui_is_visible(const drgui_element* pElement);
-
-/// Recursively determines whether or not the element is marked as visible.
-bool drgui_is_visible_recursive(const drgui_element* pElement);
-
-
-/// Disables clipping against the parent for the given element.
-void drgui_disable_clipping(drgui_element* pElement);
-
-/// Enables clipping against the parent for the given element.
-void drgui_enable_clipping(drgui_element* pElement);
-
-/// Determines whether or not clipping is enabled for the given element.
-bool drgui_is_clipping_enabled(const drgui_element* pElement);
-
-
-/// Sets the element that should receive all future mouse related events.
-///
-/// @remarks
-/// Release the mouse capture with drgui_release_mosue().
-void drgui_capture_mouse(drgui_element* pElement);
-
-/// Releases the mouse capture.
-void drgui_release_mouse(drgui_context* pContext);
-
-/// Releases the mouse capture without posting the global-scoped event. Should only be used in very specific cases, usually in combination with awkward interop with the window system.
-void drgui_release_mouse_no_global_notify(drgui_context* pContext);
-
-/// Retrieves a pointer to the element with the mouse capture.
-drgui_element* drgui_get_element_with_mouse_capture(drgui_context* pContext);
-
-/// Determines whether or not the given element has the mouse capture.
-bool drgui_has_mouse_capture(drgui_element* pElement);
-
-
-/// Sets the element that should receive all future keyboard related events.
-///
-/// @remarks
-/// Releases the keyboard capture with drgui_release_keyboard().
-void drgui_capture_keyboard(drgui_element* pElement);
-
-/// Releases the keyboard capture.
-void drgui_release_keyboard(drgui_context* pContext);
-
-/// Releases the keyboard capture without posting the global-scoped event. Should only be used in very specific cases, usually in combination with awkward interop with the window system.
-void drgui_release_keyboard_no_global_notify(drgui_context* pContext);
-
-/// Retrieves a pointer to the element with the keyboard capture.
-drgui_element* drgui_get_element_with_keyboard_capture(drgui_context* pContext);
-
-/// Determines whether or not the given element has the keyboard capture.
-bool drgui_has_keyboard_capture(drgui_element* pElement);
-
-
-/// Sets the cursor to use when the mouse enters the given GUI element.
-void drgui_set_cursor(drgui_element* pElement, drgui_cursor_type cursor);
-
-/// Retrieves the cursor to use when the mouse enters the given GUI element.
-drgui_cursor_type drgui_get_cursor(drgui_element* pElement);
-
-
-//// Events ////
-
-/// Registers the on_move event callback.
-void drgui_set_on_move(drgui_element* pElement, drgui_on_move_proc callback);
-
-/// Registers the on_size event callback.
-void drgui_set_on_size(drgui_element* pElement, drgui_on_size_proc callback);
-
-/// Registers the on_mouse_enter event callback.
-void drgui_set_on_mouse_enter(drgui_element* pElement, drgui_on_mouse_enter_proc callback);
-
-/// Registers the on_mouse_leave event callback.
-void drgui_set_on_mouse_leave(drgui_element* pElement, drgui_on_mouse_leave_proc callback);
-
-/// Registers the on_mouse_move event callback.
-void drgui_set_on_mouse_move(drgui_element* pElement, drgui_on_mouse_move_proc callback);
-
-/// Registers the on_mouse_button_down event callback.
-void drgui_set_on_mouse_button_down(drgui_element* pElement, drgui_on_mouse_button_down_proc callback);
-
-/// Registers the on_mouse_button_up event callback.
-void drgui_set_on_mouse_button_up(drgui_element* pElement, drgui_on_mouse_button_up_proc callback);
-
-/// Registers the on_mouse_button_down event callback.
-void drgui_set_on_mouse_button_dblclick(drgui_element* pElement, drgui_on_mouse_button_dblclick_proc callback);
-
-/// Registers the on_mouse_wheel event callback.
-void drgui_set_on_mouse_wheel(drgui_element* pElement, drgui_on_mouse_wheel_proc callback);
-
-/// Registers the on_key_down event callback.
-void drgui_set_on_key_down(drgui_element* pElement, drgui_on_key_down_proc callback);
-
-/// Registers the on_key_up event callback.
-void drgui_set_on_key_up(drgui_element* pElement, drgui_on_key_up_proc callback);
-
-/// Registers the on_printable_key_down event callback.
-void drgui_set_on_printable_key_down(drgui_element* pElement, drgui_on_printable_key_down_proc callback);
-
-/// Registers the on_paint event callback.
-void drgui_set_on_paint(drgui_element* pElement, drgui_on_paint_proc callback);
-
-/// Registers the on_dirty event callback.
-void drgui_set_on_dirty(drgui_element* pElement, drgui_on_dirty_proc callback);
-
-/// Registers the on_hittest event callback.
-void drgui_set_on_hittest(drgui_element* pElement, drgui_on_hittest_proc callback);
-
-/// Registers the on_capture_mouse event callback.
-void drgui_set_on_capture_mouse(drgui_element* pElement, drgui_on_capture_mouse_proc callback);
-
-/// Registers the on_release_mouse event callback.
-void drgui_set_on_release_mouse(drgui_element* pElement, drgui_on_release_mouse_proc callback);
-
-/// Registers the on_capture_keyboard event callback.
-void drgui_set_on_capture_keyboard(drgui_element* pElement, drgui_on_capture_keyboard_proc callback);
-
-/// Registers the on_release_keyboard event callback.
-void drgui_set_on_release_keyboard(drgui_element* pElement, drgui_on_release_keyboard_proc callback);
-
-
-
-//// Containment and Hit Detection ////
-
-/// Determines whether or not the given point is inside the bounds of the given element.
-///
-/// @remarks
-/// This only checks if the point is inside the bounds of the element and does not take hit testing into account. This difference
-/// with this one and drgui_is_point_inside_element() is that the latter will use hit testing.
-bool drgui_is_point_inside_element_bounds(const drgui_element* pElement, float absolutePosX, float absolutePosY);
-
-/// Determines whether or not the given point is inside the given element.
-///
-/// @remarks
-/// This will use hit testing to determine whether or not the point is inside the element.
-bool drgui_is_point_inside_element(drgui_element* pElement, float absolutePosX, float absolutePosY);
-
-/// Finds the element under the given point taking mouse pass-through and hit testing into account.
-drgui_element* drgui_find_element_under_point(drgui_element* pTopLevelElement, float absolutePosX, float absolutePosY);
-
-/// Determines whether or not the given element is currently sitting directly under the mouse.
-bool drgui_is_element_under_mouse(drgui_element* pTopLevelElement);
-
-
-
-//// Hierarchy ////
-
-// Retrieves the parent of the given element.
-drgui_element* drgui_get_parent(drgui_element* pChildElement);
-
-/// Detaches the given element from it's parent.
-void drgui_detach(drgui_element* pChildElement);
-
-/// Attaches the given element as a child of the given parent element, and appends it to the end of the children list.
-void drgui_append(drgui_element* pChildElement, drgui_element* pParentElement);
-
-/// Attaches the given element as a child of the given parent element, and prepends it to the end of the children list.
-void drgui_prepend(drgui_element* pChildElement, drgui_element* pParentElement);
-
-/// Appends the given element to the given sibling.
-void drgui_append_sibling(drgui_element* pElementToAppend, drgui_element* pElementToAppendTo);
-
-/// Prepends the given element to the given sibling.
-void drgui_prepend_sibling(drgui_element* pElementToPrepend, drgui_element* pElementToPrependTo);
-
-/// Retrieves a pointer to the given element's top-level ancestor.
-///
-/// @remarks
-/// If pElement is the top level element, the return value will be pElement.
-drgui_element* drgui_find_top_level_element(drgui_element* pElement);
-
-/// Determines whether or not the given element is the parent of the other.
-///
-/// @remarks
-/// This is not recursive. Use drgui_is_ancestor() to do a recursive traversal.
-bool drgui_is_parent(drgui_element* pParentElement, drgui_element* pChildElement);
-
-/// Determines whether or not the given element is a child of the other.
-///
-/// @remarks
-/// This is not recursive. Use drgui_is_descendant() to do a recursive traversal.
-bool drgui_is_child(drgui_element* pChildElement, drgui_element* pParentElement);
-
-/// Determines whether or not the given element is an ancestor of the other.
-bool drgui_is_ancestor(drgui_element* pAncestorElement, drgui_element* pChildElement);
-
-/// Determines whether or not the given element is a descendant of the other.
-bool drgui_is_descendant(drgui_element* pChildElement, drgui_element* pAncestorElement);
-
-/// Determines whether or not the given element is itself or a descendant.
-bool drgui_is_self_or_ancestor(drgui_element* pAncestorElement, drgui_element* pChildElement);
-
-/// Determines whether or not the given element is itself or a descendant.
-bool drgui_is_self_or_descendant(drgui_element* pChildElement, drgui_element* pAncestorElement);
-
-
-
-//// Layout ////
-
-/// Sets the absolute position of the given element.
-void drgui_set_absolute_position(drgui_element* pElement, float positionX, float positionY);
-
-/// Retrieves the absolute position of the given element.
-void drgui_get_absolute_position(const drgui_element* pElement, float* positionXOut, float* positionYOut);
-float drgui_get_absolute_position_x(const drgui_element* pElement);
-float drgui_get_absolute_position_y(const drgui_element* pElement);
-
-
-/// Sets the relative position of the given element.
-void drgui_set_relative_position(drgui_element* pElement, float relativePosX, float relativePosY);
-
-/// Retrieves the relative position of the given element.
-void drgui_get_relative_position(const drgui_element* pElement, float* relativePosXOut, float* relativePosYOut);
-float drgui_get_relative_position_x(const drgui_element* pElement);
-float drgui_get_relative_position_y(const drgui_element* pElement);
-
-
-/// Sets the size of the given element.
-void drgui_set_size(drgui_element* pElement, float width, float height);
-
-/// Retrieves the size of the given element.
-void drgui_get_size(const drgui_element* pElement, float* widthOut, float* heightOut);
-float drgui_get_width(const drgui_element* pElement);
-float drgui_get_height(const drgui_element* pElement);
-
-
-
-/// Retrieves the absolute rectangle for the given element.
-drgui_rect drgui_get_absolute_rect(const drgui_element* pElement);
-
-/// Retrieves the relative rectangle for the given element.
-drgui_rect drgui_get_relative_rect(const drgui_element* pElement);
-
-/// Retrieves the local rectangle for the given element.
-///
-/// @remarks
-/// The local rectangle is equivalent to drgui_make_rect(0, 0, drgui_get_width(pElement), drgui_get_height(pElement));
-drgui_rect drgui_get_local_rect(const drgui_element* pElement);
-
-
-
-//// Painting ////
-
-/// Registers the custom painting callbacks.
-///
-/// @remarks
-/// This can only be called once, so it should always be done after initialization. This will fail if called
-/// more than once.
-bool drgui_register_painting_callbacks(drgui_context* pContext, void* pPaintingContext, drgui_painting_callbacks callbacks);
-
-
-/// Performs a recursive traversal of all visible elements in the given rectangle.
-///
-/// @param pParentElement [in] A pointer to the element to iterate.
-///
-/// @remarks
-/// pParentElement will be included in the iteration is it is within the rectangle.
-/// @par
-/// The rectangle should be relative to pParentElement.
-/// @par
-/// The iteration callback function takes a pointer to a rectangle structure that represents the visible portion of the
-/// element. This pointer can be modified by the callback to create an adjusted rectangle which can be used for clipping.
-bool drgui_iterate_visible_elements(drgui_element* pParentElement, drgui_rect relativeRect, drgui_visible_iteration_proc callback, void* pUserData);
-
-
-/// Disable's automatic dirtying of elements.
-void drgui_disable_auto_dirty(drgui_context* pContext);
-
-/// Enable's automatic dirtying of elements.
-void drgui_enable_auto_dirty(drgui_context* pContext);
-
-/// Determines whether or not automatic dirtying is enabled.
-bool drgui_is_auto_dirty_enabled(drgui_context* pContext);
-
-
-/// Begins accumulating a dirty rectangle.
-///
-/// Returns a pointer to the top level element that was made dirty.
-drgui_element* drgui_begin_dirty(drgui_element* pElement);
-
-/// Ends accumulating a dirty rectangle, and requests a redraw from the backend if the counter reaches zero.
-void drgui_end_dirty(drgui_element* pElement);
-
-/// Marks a region of the given element as dirty.
-///
-/// @remarks
-/// This will not redraw the element immediately, but instead post a paint event.
-void drgui_dirty(drgui_element* pElement, drgui_rect relativeRect);
-
-
-/// Draws the given element.
-///
-/// @remarks
-/// Do not call this on one element, then again on it's children. Any children that fall inside the specified
-/// rectangle will also be redrawn.
-/// @par
-/// This will call painting event handlers which will give the application time to do custom drawing.
-/// @par
-/// When using easy_draw to do drawing, pPaintData must be set to a pointer to the relevant easydraw_surface object.
-void drgui_draw(drgui_element* pElement, drgui_rect relativeRect, void* pPaintData);
-
-/// Retrieves the current clipping rectangle.
-void drgui_get_clip(drgui_element* pElement, drgui_rect* pRelativeRect, void* pPaintData);
-
-/// Sets the clipping rectangle to apply to all future draw operations on this element.
-void drgui_set_clip(drgui_element* pElement, drgui_rect relativeRect, void* pPaintData);
-
-/// Draws a rectangle on the given element.
-void drgui_draw_rect(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, void* pPaintData);
-
-/// Draws the outline of a rectangle on the given element.
-void drgui_draw_rect_outline(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float outlineWidth, void* pPaintData);
-
-/// Draws a filled rectangle with an outline on the given element.
-void drgui_draw_rect_with_outline(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float outlineWidth, drgui_color outlineColor, void* pPaintData);
-
-/// Draws a rectangle with rounded corners on the given element.
-void drgui_draw_round_rect(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float radius, void* pPaintData);
-
-/// Draws the outline of a rectangle with rounded corners on the given element.
-void drgui_draw_round_rect_outline(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float radius, float outlineWidth, void* pPaintData);
-
-/// Draws a filled rectangle and it's outline with rounded corners on the given element.
-void drgui_draw_round_rect_with_outline(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float radius, float outlineWidth, drgui_color outlineColor, void* pPaintData);
-
-/// Draws a run of text on the given element.
-///
-/// @remarks
-/// This does not do any complex formatting like multiple lines and whatnot. Complex formatting can be achieved with multiple
-/// calls to this function.
-/// @par
-/// \c textSizeInBytes can be -1 in which case the text string is treated as null terminated.
-void drgui_draw_text(drgui_element* pElement, drgui_font* pFont, const char* text, int textLengthInBytes, float posX, float posY, drgui_color color, drgui_color backgroundColor, void* pPaintData);
-
-/// Draws an image.
-void drgui_draw_image(drgui_element* pElement, drgui_image* pImage, drgui_draw_image_args* pArgs, void* pPaintData);
-
-
-/// Creates a font resource.
-drgui_font* drgui_create_font(drgui_context* pContext, const char* family, unsigned int size, drgui_font_weight weight, drgui_font_slant slant, float rotation, unsigned int flags);
-
-/// Deletes a font resource.
-void drgui_delete_font(drgui_font* pFont);
-
-/// Retrieves the metrics of the given font.
-bool drgui_get_font_metrics(drgui_font* pFont, drgui_font_metrics* pMetricsOut);
-
-/// Retrieves the metrics of the glyph for the given character when rendered with the given font.
-bool drgui_get_glyph_metrics(drgui_font* pFont, unsigned int utf32, drgui_glyph_metrics* pMetricsOut);
-
-/// Retrieves the dimensions of the given string when drawn with the given font.
-///
-/// @remarks
-/// When the length of the text is 0, the width will be set to 0 and the height will be set to the line height.
-bool drgui_measure_string(drgui_font* pFont, const char* text, size_t textLengthInBytes, float* pWidthOut, float* pHeightOut);
-
-/// Retrieves the position to place a text cursor based on the given point for the given string when drawn with the given font.
-bool drgui_get_text_cursor_position_from_point(drgui_font* pFont, const char* text, size_t textSizeInBytes, float maxWidth, float inputPosX, float* pTextCursorPosXOut, size_t* pCharacterIndexOut);
-
-/// Retrieves the position to palce a text cursor based on the character at the given index for the given string when drawn with the given font.
-bool drgui_get_text_cursor_position_from_char(drgui_font* pFont, const char* text, size_t characterIndex, float* pTextCursorPosXOut);
-
-
-
-/// Creates an image that can be passed to drgui_draw_image().
-///
-/// @remarks
-/// The dimensions and format of an image are immutable. If these need to change, then the image needs to be deleted and re-created.
-/// @par
-/// If pData is NULL, the default image data is undefined.
-/// @par
-/// If stride is set to 0, it is assumed to be tightly packed.
-/// @par
-/// Use drgui_map_image_data() and drgui_unmap_image_data() to update or retrieve image data.
-drgui_image* drgui_create_image(drgui_context* pContext, unsigned int width, unsigned int height, drgui_image_format format, unsigned int stride, const void* pData);
-
-/// Deletes the given image.
-void drgui_delete_image(drgui_image* pImage);
-
-/// Retrieves the size of the given image.
-void drgui_get_image_size(drgui_image* pImage, unsigned int* pWidthOut, unsigned int* pHeightOut);
-
-/// Retrieves the optimal image format for the given context.
-drgui_image_format drgui_get_optimal_image_format(drgui_context* pContext);
-
-/// Retrieves a pointer to a buffer representing the given image's data.
-///
-/// Call drgui_unmap_image_data() when you are done with this function.
-///
-/// Use this function to access an image's data. The returned pointer does not necessarilly point to the image's actual data, so when
-/// writing to this pointer, nothing is actually updated until drgui_unmap_image_data() is called.
-///
-/// The returned data will contain the image data at the time of the mapping.
-void* drgui_map_image_data(drgui_image* pImage, unsigned int accessFlags);
-
-/// Unmaps the given image data.
-void drgui_unmap_image_data(drgui_image* pImage);
-
-
-
-/////////////////////////////////////////////////////////////////
-//
-// HIGH-LEVEL API
-//
-/////////////////////////////////////////////////////////////////
-
-//// Hit Testing and Layout ////
-
-/// An on_size event callback that resizes every child element to that of the parent.
-void drgui_on_size_fit_children_to_parent(drgui_element* pElement, float newWidth, float newHeight);
-
-/// An on_hit_test event callback that can be used to always fail the mouse hit test.
-bool drgui_pass_through_hit_test(drgui_element* pElement, float mousePosX, float mousePosY);
-
-
-//// Painting ////
-
-/// Draws a border around the given element.
-void drgui_draw_border(drgui_element* pElement, float borderWidth, drgui_color color, void* pUserData);
-
-
-
-/////////////////////////////////////////////////////////////////
-//
-// UTILITY API
-//
-/////////////////////////////////////////////////////////////////
-
-/// Creates a color object from a set of RGBA color components.
-drgui_color drgui_rgba(drgui_byte r, drgui_byte g, drgui_byte b, drgui_byte a);
-
-/// Creates a color object from a set of RGB color components.
-drgui_color drgui_rgb(drgui_byte r, drgui_byte g, drgui_byte b);
-
-/// Clamps the given rectangle to another.
-drgui_rect drgui_clamp_rect(drgui_rect rect, drgui_rect other);
-
-/// Clamps the given rectangle to the given element and returns whether or not any of it is contained within the element's rectangle.
-bool drgui_clamp_rect_to_element(const drgui_element* pElement, drgui_rect* pRelativeRect);
-
-/// Converts the given rectangle from absolute to relative to the given element.
-drgui_rect drgui_make_rect_relative(const drgui_element* pElement, drgui_rect* pRect);
-
-/// Converts the given rectangle from relative to absolute based on the given element.
-drgui_rect drgui_make_rect_absolute(const drgui_element* pElement, drgui_rect* pRect);
-
-/// Converts the given point from absolute to relative to the given element.
-void drgui_make_point_relative(const drgui_element* pElement, float* positionX, float* positionY);
-
-/// Converts the given point from relative to absolute based on the given element.
-void drgui_make_point_absolute(const drgui_element* pElement, float* positionX, float* positionY);
-
-/// Creates a drgui_rect object.
-drgui_rect drgui_make_rect(float left, float top, float right, float bottom);
-
-/// Creates an inside-out rectangle.
-///
-/// @remarks
-/// An inside our rectangle is a negative-dimension rectangle with each edge at the extreme edges. The left edge will be at the
-/// right-most side and the right edge will be at the left-most side. The same applies for the top and bottom edges.
-drgui_rect drgui_make_inside_out_rect();
-
-/// Expands the given rectangle on all sides by the given amount.
-///
-/// @remarks
-/// This will increase the width and height of the rectangle by x 2.
-/// @par
-/// The growth amount can be negative, in which case it will be shrunk. Note that this does not do any checking to ensure the rectangle
-/// contains positive dimensions after a shrink.
-drgui_rect drgui_grow_rect(drgui_rect rect, float amount);
-
-/// Scales the given rectangle.
-///
-/// @param scaleX [in] The scale to apply to and
-/// @param scaleY [in] The scale to apply to and
-///
-/// @remarks
-/// This will modify the and properties which means the rectangle will change position. To adjust only the size, scale the
-/// rectangle manually.
-drgui_rect drgui_scale_rect(drgui_rect rect, float scaleX, float scaleY);
-
-/// Offsets the given rectangle.
-drgui_rect drgui_offset_rect(drgui_rect rect, float offsetX, float offsetY);
-
-/// Creates a rectangle that contains both of the given rectangles.
-drgui_rect drgui_rect_union(drgui_rect rect0, drgui_rect rect1);
-
-/// Determines whether or not the given rectangle contains the given point.
-///
-/// @remarks
-/// An important not here is that if the position is sitting on the right or bottom border, false will be returned. If, however, the point
-/// is sitting on the left or top border, true will be returned. The reason for this is that elements may sit exactly side-by-side with
-/// each other, and if we use this function to determine if a point is contained within an element (which we do), we would end up having
-/// this return true for both elements, which we don't want.
-bool drgui_rect_contains_point(drgui_rect rect, float posX, float posY);
-
-/// Determines whether or not two rectangles are equal.
-bool drgui_rect_equal(drgui_rect rect0, drgui_rect rect1);
-
-/// Determines whether or not the given rectangle has any volume (width and height > 0).
-bool drgui_rect_has_volume(drgui_rect rect);
-
-
-
-/////////////////////////////////////////////////////////////////
-//
-// EASY_DRAW-SPECIFIC API
-//
-/////////////////////////////////////////////////////////////////
-#ifndef DRGUI_NO_DR_2D
-
-/// A covenience function for creating a new context and registering the easy_draw painting callbacks.
-///
-/// @remarks
-/// This is equivalent to drgui_create_context() followed by drgui_register_dr_2d_callbacks().
-drgui_context* drgui_create_context_dr_2d(dr2d_context* pDrawingContext);
-
-/// Registers the drawing callbacks for use with easy_draw.
-///
-/// @remarks
-/// The user data of each callback is assumed to be a pointer to an easydraw_surface object.
-void drgui_register_dr_2d_callbacks(drgui_context* pContext, dr2d_context* pDrawingContext);
-
-#endif
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif //dr_gui_h
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// IMPLEMENTATION
-//
-///////////////////////////////////////////////////////////////////////////////
-#ifdef DR_GUI_IMPLEMENTATION
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifndef DRGUI_PRIVATE
-#define DRGUI_PRIVATE static
-#endif
-
-/////////////////////////////////////////////////////////////////
-//
-// PRIVATE CORE API
-//
-/////////////////////////////////////////////////////////////////
-
-
-// Context Flags
-#define IS_CONTEXT_DEAD (1U << 0)
-#define IS_AUTO_DIRTY_DISABLED (1U << 1)
-#define IS_RELEASING_KEYBOARD (1U << 2)
-
-// Element Flags
-#define IS_ELEMENT_HIDDEN (1U << 0)
-#define IS_ELEMENT_CLIPPING_DISABLED (1U << 1)
-#define IS_ELEMENT_DEAD (1U << 31)
-
-
-static int drgui__strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
-{
-#ifdef _MSC_VER
- return strcpy_s(dst, dstSizeInBytes, src);
-#else
- if (dst == 0) {
- return EINVAL;
- }
- if (dstSizeInBytes == 0) {
- return ERANGE;
- }
- if (src == 0) {
- dst[0] = '\0';
- return EINVAL;
- }
-
- size_t i;
- for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
- dst[i] = src[i];
- }
-
- if (i < dstSizeInBytes) {
- dst[i] = '\0';
- return 0;
- }
-
- dst[0] = '\0';
- return ERANGE;
-#endif
-}
-
-int drgui__strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
-{
-#ifdef _MSC_VER
- return strncpy_s(dst, dstSizeInBytes, src, count);
-#else
- if (dst == 0) {
- return EINVAL;
- }
- if (dstSizeInBytes == 0) {
- return EINVAL;
- }
- if (src == 0) {
- dst[0] = '\0';
- return EINVAL;
- }
-
- size_t maxcount = count;
- if (count == ((size_t)-1) || count >= dstSizeInBytes) { // -1 = _TRUNCATE
- maxcount = dstSizeInBytes - 1;
- }
-
- size_t i;
- for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
- dst[i] = src[i];
- }
-
- if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
- dst[i] = '\0';
- return 0;
- }
-
- dst[0] = '\0';
- return ERANGE;
-#endif
-}
-
-
-/// Increments the inbound event counter
-///
-/// @remarks
-/// This is called from every drgui_post_inbound_event_*() function and is used to keep track of whether or
-/// not an inbound event is being processed. We need to track this because if we are in the middle of event
-/// processing and an element is deleted, we want to delay it's deletion until the end of the event processing.
-/// @par
-/// Use drgui_end_inbound_event() to decrement the counter.
-void drgui_begin_inbound_event(drgui_context* pContext);
-
-/// Decrements the inbound event counter.
-///
-/// @remarks
-/// This is called from every drgui_post_inbound_event_*() function.
-/// @par
-/// When the internal counter reaches zero, deleted elements will be garbage collected.
-void drgui_end_inbound_event(drgui_context* pContext);
-
-/// Determines whether or not inbound events are being processed.
-///
-/// @remarks
-/// This is used to determine whether or not an element can be deleted immediately or should be garbage collected
-/// at the end of event processing.
-bool drgui_is_handling_inbound_event(const drgui_context* pContext);
-
-
-/// Increments the outbound event counter.
-///
-/// @remarks
-/// This will validate that the given element is allowed to have an event posted. When false is returned, nothing
-/// will have been locked and the outbound event should be cancelled.
-/// @par
-/// This will return false if the given element has been marked as dead, or if there is some other reason it should
-/// not be receiving events.
-bool drgui_begin_outbound_event(drgui_element* pElement);
-
-/// Decrements the outbound event counter.
-void drgui_end_outbound_event(drgui_element* pElement);
-
-/// Determines whether or not and outbound event is being processed.
-bool drgui_is_handling_outbound_event(drgui_context* pContext);
-
-
-/// Marks the given element as dead.
-void drgui_mark_element_as_dead(drgui_element* pElement);
-
-/// Determines whether or not the given element is marked as dead.
-bool drgui_is_element_marked_as_dead(const drgui_element* pElement);
-
-/// Deletes every element that has been marked as dead.
-void drgui_delete_elements_marked_as_dead(drgui_context* pContext);
-
-
-/// Marks the given context as deleted.
-void drgui_mark_context_as_dead(drgui_context* pContext);
-
-/// Determines whether or not the given context is marked as dead.
-bool drgui_is_context_marked_as_dead(const drgui_context* pContext);
-
-
-/// Deletes the given context for real.
-///
-/// If a context is deleted during the processing of an inbound event it will not be deleting immediately - this
-/// will delete the context for real.
-void drgui_delete_context_for_real(drgui_context* pContext);
-
-/// Deletes the given element for real.
-///
-/// Sometimes an element will not be deleted straight away but instead just marked as dead. We use this to delete
-/// the given element for real.
-void drgui_delete_element_for_real(drgui_element* pElement);
-
-
-/// Orphans the given element without triggering a redraw of the parent nor the child.
-void drgui_detach_without_redraw(drgui_element* pChildElement);
-
-/// Appends the given element without first detaching it from the old parent, nor does it post a redraw.
-void drgui_append_without_detach_or_redraw(drgui_element* pChildElement, drgui_element* pParentElement);
-
-/// Appends the given element without first detaching it from the old parent.
-void drgui_append_without_detach(drgui_element* pChildElement, drgui_element* pParentElement);
-
-/// Prepends the given element without first detaching it from the old parent, nor does it post a redraw.
-void drgui_prepend_without_detach_or_redraw(drgui_element* pChildElement, drgui_element* pParentElement);
-
-/// Prepends the given element without first detaching it from the old parent.
-void drgui_prepend_without_detach(drgui_element* pChildElement, drgui_element* pParentElement);
-
-/// Appends an element to another as it's sibling, but does not detach it from the previous parent nor trigger a redraw.
-void drgui_append_sibling_without_detach_or_redraw(drgui_element* pElementToAppend, drgui_element* pElementToAppendTo);
-
-/// Appends an element to another as it's sibling, but does not detach it from the previous parent.
-void drgui_append_sibling_without_detach(drgui_element* pElementToAppend, drgui_element* pElementToAppendTo);
-
-/// Prepends an element to another as it's sibling, but does not detach it from the previous parent nor trigger a redraw.
-void drgui_prepend_sibling_without_detach_or_redraw(drgui_element* pElementToPrepend, drgui_element* pElementToPrependTo);
-
-/// Prepends an element to another as it's sibling, but does not detach it from the previous parent.
-void drgui_prepend_sibling_without_detach(drgui_element* pElementToPrepend, drgui_element* pElementToPrependTo);
-
-
-/// Begins accumulating an invalidation rectangle.
-void drgui_begin_auto_dirty(drgui_element* pElement);
-
-/// Ends accumulating the invalidation rectangle and posts on_dirty is auto-dirty is enabled.
-void drgui_end_auto_dirty(drgui_element* pElement);
-
-/// Marks the given region of the given top level element as dirty, but only if automatic dirtying is enabled.
-///
-/// @remarks
-/// This is equivalent to drgui_begin_auto_dirty() immediately followed by drgui_end_auto_dirty().
-void drgui_auto_dirty(drgui_element* pTopLevelElement, drgui_rect rect);
-
-
-/// Recursively applies the given offset to the absolute positions of the children of the given element.
-///
-/// @remarks
-/// This is called when the absolute position of an element is changed.
-void drgui_apply_offset_to_children_recursive(drgui_element* pParentElement, float offsetX, float offsetY);
-
-
-/// The function to call when the mouse may have entered into a new element.
-void drgui_update_mouse_enter_and_leave_state(drgui_context* pContext, drgui_element* pNewElementUnderMouse);
-
-
-/// Functions for posting outbound events.
-void drgui_post_outbound_event_move(drgui_element* pElement, float newRelativePosX, float newRelativePosY);
-void drgui_post_outbound_event_size(drgui_element* pElement, float newWidth, float newHeight);
-void drgui_post_outbound_event_mouse_enter(drgui_element* pElement);
-void drgui_post_outbound_event_mouse_leave(drgui_element* pElement);
-void drgui_post_outbound_event_mouse_move(drgui_element* pElement, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-void drgui_post_outbound_event_mouse_button_down(drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-void drgui_post_outbound_event_mouse_button_up(drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-void drgui_post_outbound_event_mouse_button_dblclick(drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-void drgui_post_outbound_event_mouse_wheel(drgui_element* pElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-void drgui_post_outbound_event_key_down(drgui_element* pElement, drgui_key key, int stateFlags);
-void drgui_post_outbound_event_key_up(drgui_element* pElement, drgui_key key, int stateFlags);
-void drgui_post_outbound_event_printable_key_down(drgui_element* pElement, unsigned int character, int stateFlags);
-void drgui_post_outbound_event_dirty(drgui_element* pElement, drgui_rect relativeRect);
-void drgui_post_outbound_event_dirty_global(drgui_element* pElement, drgui_rect relativeRect);
-void drgui_post_outbound_event_capture_mouse(drgui_element* pElement);
-void drgui_post_outbound_event_capture_mouse_global(drgui_element* pElement);
-void drgui_post_outbound_event_release_mouse(drgui_element* pElement);
-void drgui_post_outbound_event_release_mouse_global(drgui_element* pElement);
-void drgui_post_outbound_event_capture_keyboard(drgui_element* pElement, drgui_element* pPrevCapturedElement);
-void drgui_post_outbound_event_capture_keyboard_global(drgui_element* pElement, drgui_element* pPrevCapturedElement);
-void drgui_post_outbound_event_release_keyboard(drgui_element* pElement, drgui_element* pNewCapturedElement);
-void drgui_post_outbound_event_release_keyboard_global(drgui_element* pElement, drgui_element* pNewCapturedElement);
-
-/// Posts a log message.
-void drgui_log(drgui_context* pContext, const char* message);
-
-
-void drgui_begin_inbound_event(drgui_context* pContext)
-{
- assert(pContext != NULL);
-
- pContext->inboundEventCounter += 1;
-}
-
-void drgui_end_inbound_event(drgui_context* pContext)
-{
- assert(pContext != NULL);
- assert(pContext->inboundEventCounter > 0);
-
- pContext->inboundEventCounter -= 1;
-
-
- // Here is where we want to clean up any elements that are marked as dead. When events are being handled elements are not deleted
- // immediately but instead only marked for deletion. This function will be called at the end of event processing which makes it
- // an appropriate place for cleaning up dead elements.
- if (!drgui_is_handling_inbound_event(pContext))
- {
- drgui_delete_elements_marked_as_dead(pContext);
-
- // If the context has been marked for deletion than we will need to delete that too.
- if (drgui_is_context_marked_as_dead(pContext))
- {
- drgui_delete_context_for_real(pContext);
- }
- }
-}
-
-bool drgui_is_handling_inbound_event(const drgui_context* pContext)
-{
- assert(pContext != NULL);
-
- return pContext->inboundEventCounter > 0;
-}
-
-
-
-bool drgui_begin_outbound_event(drgui_element* pElement)
-{
- assert(pElement != NULL);
- assert(pElement->pContext != NULL);
-
-
- // We want to cancel the outbound event if the element is marked as dead.
- if (drgui_is_element_marked_as_dead(pElement)) {
- drgui_log(pElement->pContext, "WARNING: Attemping to post an event to an element that is marked for deletion.");
- return false;
- }
-
-
- // At this point everything should be fine so we just increment the count (which should never go above 1) and return true.
- pElement->pContext->outboundEventLockCounter += 1;
-
- return true;
-}
-
-void drgui_end_outbound_event(drgui_element* pElement)
-{
- assert(pElement != NULL);
- assert(pElement->pContext != NULL);
- assert(pElement->pContext->outboundEventLockCounter > 0);
-
- pElement->pContext->outboundEventLockCounter -= 1;
-}
-
-bool drgui_is_handling_outbound_event(drgui_context* pContext)
-{
- assert(pContext != NULL);
- return pContext->outboundEventLockCounter > 0;
-}
-
-
-void drgui_mark_element_as_dead(drgui_element* pElement)
-{
- assert(pElement != NULL);
- assert(pElement->pContext != NULL);
-
- pElement->flags |= IS_ELEMENT_DEAD;
-
-
- if (pElement->pContext->pFirstDeadElement != NULL) {
- pElement->pNextDeadElement = pElement->pContext->pFirstDeadElement;
- }
-
- pElement->pContext->pFirstDeadElement = pElement;
-}
-
-bool drgui_is_element_marked_as_dead(const drgui_element* pElement)
-{
- if (pElement == NULL) {
- return false;
- }
-
- return (pElement->flags & IS_ELEMENT_DEAD) != 0;
-}
-
-void drgui_delete_elements_marked_as_dead(drgui_context* pContext)
-{
- assert(pContext != NULL);
-
- while (pContext->pFirstDeadElement != NULL)
- {
- drgui_element* pDeadElement = pContext->pFirstDeadElement;
- pContext->pFirstDeadElement = pContext->pFirstDeadElement->pNextDeadElement;
-
- drgui_delete_element_for_real(pDeadElement);
- }
-}
-
-
-void drgui_mark_context_as_dead(drgui_context* pContext)
-{
- assert(pContext != NULL);
- assert(!drgui_is_context_marked_as_dead(pContext));
-
- pContext->flags |= IS_CONTEXT_DEAD;
-}
-
-bool drgui_is_context_marked_as_dead(const drgui_context* pContext)
-{
- assert(pContext != NULL);
-
- return (pContext->flags & IS_CONTEXT_DEAD) != 0;
-}
-
-
-
-void drgui_delete_context_for_real(drgui_context* pContext)
-{
- assert(pContext != NULL);
-
- // All elements marked as dead need to be deleted.
- drgui_delete_elements_marked_as_dead(pContext);
-
- free(pContext);
-}
-
-void drgui_delete_element_for_real(drgui_element* pElementToDelete)
-{
- assert(pElementToDelete != NULL);
-
- drgui_context* pContext = pElementToDelete->pContext;
-
- // If the element is marked as dead
- if (drgui_is_element_marked_as_dead(pElementToDelete)) {
- if (pContext->pFirstDeadElement == pElementToDelete) {
- pContext->pFirstDeadElement = pContext->pFirstDeadElement->pNextDeadElement;
- } else {
- drgui_element* pPrevDeadElement = pContext->pFirstDeadElement;
- while (pPrevDeadElement != NULL) {
- if (pPrevDeadElement->pNextDeadElement == pElementToDelete) {
- break;
- }
-
- pPrevDeadElement = pPrevDeadElement->pNextDeadElement;
- }
-
- if (pPrevDeadElement != NULL) {
- pElementToDelete->pNextDeadElement = pElementToDelete->pNextDeadElement;
- }
- }
- }
-
- free(pElementToDelete);
-}
-
-
-void drgui_detach_without_redraw(drgui_element* pElement)
-{
- if (pElement->pParent != NULL) {
- if (pElement->pParent->pFirstChild == pElement) {
- pElement->pParent->pFirstChild = pElement->pNextSibling;
- }
-
- if (pElement->pParent->pLastChild == pElement) {
- pElement->pParent->pLastChild = pElement->pPrevSibling;
- }
-
-
- if (pElement->pPrevSibling != NULL) {
- pElement->pPrevSibling->pNextSibling = pElement->pNextSibling;
- }
-
- if (pElement->pNextSibling != NULL) {
- pElement->pNextSibling->pPrevSibling = pElement->pPrevSibling;
- }
- }
-
- pElement->pParent = NULL;
- pElement->pPrevSibling = NULL;
- pElement->pNextSibling = NULL;
-}
-
-void drgui_append_without_detach_or_redraw(drgui_element* pChildElement, drgui_element* pParentElement)
-{
- pChildElement->pParent = pParentElement;
- if (pChildElement->pParent != NULL) {
- if (pChildElement->pParent->pLastChild != NULL) {
- pChildElement->pPrevSibling = pChildElement->pParent->pLastChild;
- pChildElement->pPrevSibling->pNextSibling = pChildElement;
- }
-
- if (pChildElement->pParent->pFirstChild == NULL) {
- pChildElement->pParent->pFirstChild = pChildElement;
- }
-
- pChildElement->pParent->pLastChild = pChildElement;
- }
-}
-
-void drgui_append_without_detach(drgui_element* pChildElement, drgui_element* pParentElement)
-{
- drgui_append_without_detach_or_redraw(pChildElement, pParentElement);
- drgui_auto_dirty(pChildElement, drgui_make_rect(0, 0, pChildElement->width, pChildElement->height));
-}
-
-void drgui_prepend_without_detach_or_redraw(drgui_element* pChildElement, drgui_element* pParentElement)
-{
- pChildElement->pParent = pParentElement;
- if (pChildElement->pParent != NULL) {
- if (pChildElement->pParent->pFirstChild != NULL) {
- pChildElement->pNextSibling = pChildElement->pParent->pFirstChild;
- pChildElement->pNextSibling->pPrevSibling = pChildElement;
- }
-
- if (pChildElement->pParent->pLastChild == NULL) {
- pChildElement->pParent->pLastChild = pChildElement;
- }
-
- pChildElement->pParent->pFirstChild = pChildElement;
- }
-}
-
-void drgui_prepend_without_detach(drgui_element* pChildElement, drgui_element* pParentElement)
-{
- drgui_prepend_without_detach_or_redraw(pChildElement, pParentElement);
- drgui_auto_dirty(pChildElement, drgui_make_rect(0, 0, pChildElement->width, pChildElement->height));
-}
-
-void drgui_append_sibling_without_detach_or_redraw(drgui_element* pElementToAppend, drgui_element* pElementToAppendTo)
-{
- assert(pElementToAppend != NULL);
- assert(pElementToAppendTo != NULL);
-
- pElementToAppend->pParent = pElementToAppendTo->pParent;
- if (pElementToAppend->pParent != NULL)
- {
- pElementToAppend->pNextSibling = pElementToAppendTo->pNextSibling;
- pElementToAppend->pPrevSibling = pElementToAppendTo;
-
- pElementToAppendTo->pNextSibling->pPrevSibling = pElementToAppend;
- pElementToAppendTo->pNextSibling = pElementToAppend;
-
- if (pElementToAppend->pParent->pLastChild == pElementToAppendTo) {
- pElementToAppend->pParent->pLastChild = pElementToAppend;
- }
- }
-}
-
-void drgui_append_sibling_without_detach(drgui_element* pElementToAppend, drgui_element* pElementToAppendTo)
-{
- drgui_append_sibling_without_detach_or_redraw(pElementToAppend, pElementToAppendTo);
- drgui_auto_dirty(pElementToAppend, drgui_make_rect(0, 0, pElementToAppend->width, pElementToAppend->height));
-}
-
-void drgui_prepend_sibling_without_detach_or_redraw(drgui_element* pElementToPrepend, drgui_element* pElementToPrependTo)
-{
- assert(pElementToPrepend != NULL);
- assert(pElementToPrependTo != NULL);
-
- pElementToPrepend->pParent = pElementToPrependTo->pParent;
- if (pElementToPrepend->pParent != NULL)
- {
- pElementToPrepend->pPrevSibling = pElementToPrependTo->pNextSibling;
- pElementToPrepend->pNextSibling = pElementToPrependTo;
-
- pElementToPrependTo->pPrevSibling->pNextSibling = pElementToPrepend;
- pElementToPrependTo->pNextSibling = pElementToPrepend;
-
- if (pElementToPrepend->pParent->pFirstChild == pElementToPrependTo) {
- pElementToPrepend->pParent->pFirstChild = pElementToPrepend;
- }
- }
-}
-
-void drgui_prepend_sibling_without_detach(drgui_element* pElementToPrepend, drgui_element* pElementToPrependTo)
-{
- drgui_prepend_sibling_without_detach_or_redraw(pElementToPrepend, pElementToPrependTo);
- drgui_auto_dirty(pElementToPrepend, drgui_make_rect(0, 0, pElementToPrepend->width, pElementToPrepend->height));
-}
-
-
-void drgui_begin_auto_dirty(drgui_element* pElement)
-{
- assert(pElement != NULL);
- assert(pElement->pContext != NULL);
-
- if (drgui_is_auto_dirty_enabled(pElement->pContext)) {
- drgui_begin_dirty(pElement);
- }
-}
-
-void drgui_end_auto_dirty(drgui_element* pElement)
-{
- assert(pElement != NULL);
-
- drgui_context* pContext = pElement->pContext;
- assert(pContext != NULL);
-
- if (drgui_is_auto_dirty_enabled(pContext)) {
- drgui_end_dirty(pElement);
- }
-}
-
-void drgui_auto_dirty(drgui_element* pElement, drgui_rect relativeRect)
-{
- assert(pElement != NULL);
- assert(pElement->pContext != NULL);
-
- if (drgui_is_auto_dirty_enabled(pElement->pContext)) {
- drgui_dirty(pElement, relativeRect);
- }
-}
-
-
-void drgui__change_cursor(drgui_element* pElement, drgui_cursor_type cursor)
-{
- if (pElement == NULL || pElement->pContext == NULL) {
- return;
- }
-
- pElement->pContext->currentCursor = cursor;
-
- if (pElement->pContext->onChangeCursor) {
- pElement->pContext->onChangeCursor(pElement, cursor);
- }
-}
-
-
-
-void drgui_apply_offset_to_children_recursive(drgui_element* pParentElement, float offsetX, float offsetY)
-{
- assert(pParentElement != NULL);
-
- for (drgui_element* pChild = pParentElement->pFirstChild; pChild != NULL; pChild = pChild->pNextSibling)
- {
- drgui_begin_auto_dirty(pParentElement);
- {
- drgui_auto_dirty(pParentElement, drgui_get_local_rect(pParentElement));
- pChild->absolutePosX += offsetX;
- pChild->absolutePosY += offsetY;
-
- drgui_apply_offset_to_children_recursive(pChild, offsetX, offsetY);
- }
- drgui_end_auto_dirty(pParentElement);
- }
-}
-
-DRGUI_PRIVATE void drgui_post_on_mouse_leave_recursive(drgui_context* pContext, drgui_element* pNewElementUnderMouse, drgui_element* pOldElementUnderMouse)
-{
- (void)pContext;
-
- drgui_element* pOldAncestor = pOldElementUnderMouse;
- while (pOldAncestor != NULL)
- {
- bool isOldElementUnderMouse = pNewElementUnderMouse == pOldAncestor || drgui_is_ancestor(pOldAncestor, pNewElementUnderMouse);
- if (!isOldElementUnderMouse)
- {
- drgui_post_outbound_event_mouse_leave(pOldAncestor);
- }
-
- pOldAncestor = pOldAncestor->pParent;
- }
-}
-
-DRGUI_PRIVATE void drgui_post_on_mouse_enter_recursive(drgui_context* pContext, drgui_element* pNewElementUnderMouse, drgui_element* pOldElementUnderMouse)
-{
- if (pNewElementUnderMouse == NULL) {
- return;
- }
-
-
- if (pNewElementUnderMouse->pParent != NULL) {
- drgui_post_on_mouse_enter_recursive(pContext, pNewElementUnderMouse->pParent, pOldElementUnderMouse);
- }
-
- bool wasNewElementUnderMouse = pOldElementUnderMouse == pNewElementUnderMouse || drgui_is_ancestor(pNewElementUnderMouse, pOldElementUnderMouse);
- if (!wasNewElementUnderMouse)
- {
- drgui_post_outbound_event_mouse_enter(pNewElementUnderMouse);
- }
-}
-
-void drgui_update_mouse_enter_and_leave_state(drgui_context* pContext, drgui_element* pNewElementUnderMouse)
-{
- if (pContext == NULL) {
- return;
- }
-
- drgui_element* pOldElementUnderMouse = pContext->pElementUnderMouse;
- if (pOldElementUnderMouse != pNewElementUnderMouse)
- {
- // We don't change the enter and leave state if an element is capturing the mouse.
- if (pContext->pElementWithMouseCapture == NULL)
- {
- pContext->pElementUnderMouse = pNewElementUnderMouse;
-
- drgui_cursor_type newCursor = drgui_cursor_default;
- if (pNewElementUnderMouse != NULL) {
- newCursor = pNewElementUnderMouse->cursor;
- }
-
-
- // It's intuitive to check that the new cursor is different to the old one before trying to change it, but that is not actually
- // what we want to do. We'll let the event handler manage it themselves because it's possible the window manager might do some
- // window-specific cursor management and the old and new elements are on different windows.
- drgui__change_cursor(pNewElementUnderMouse, newCursor);
-
-
-
- // The the event handlers below, remember that ancestors are considered hovered if a descendant is the element under the mouse.
-
- // on_mouse_leave
- drgui_post_on_mouse_leave_recursive(pContext, pNewElementUnderMouse, pOldElementUnderMouse);
-
- // on_mouse_enter
- drgui_post_on_mouse_enter_recursive(pContext, pNewElementUnderMouse, pOldElementUnderMouse);
- }
- }
-}
-
-
-void drgui_post_outbound_event_move(drgui_element* pElement, float newRelativePosX, float newRelativePosY)
-{
- if (drgui_begin_outbound_event(pElement))
- {
- if (pElement->onMove) {
- pElement->onMove(pElement, newRelativePosX, newRelativePosY);
- }
-
- drgui_end_outbound_event(pElement);
- }
-}
-
-void drgui_post_outbound_event_size(drgui_element* pElement, float newWidth, float newHeight)
-{
- if (drgui_begin_outbound_event(pElement))
- {
- if (pElement->onSize) {
- pElement->onSize(pElement, newWidth, newHeight);
- }
-
- drgui_end_outbound_event(pElement);
- }
-}
-
-void drgui_post_outbound_event_mouse_enter(drgui_element* pElement)
-{
- if (drgui_begin_outbound_event(pElement))
- {
- if (pElement->onMouseEnter) {
- pElement->onMouseEnter(pElement);
- }
-
- drgui_end_outbound_event(pElement);
- }
-}
-
-void drgui_post_outbound_event_mouse_leave(drgui_element* pElement)
-{
- if (drgui_begin_outbound_event(pElement))
- {
- if (pElement->onMouseLeave) {
- pElement->onMouseLeave(pElement);
- }
-
- drgui_end_outbound_event(pElement);
- }
-}
-
-void drgui_post_outbound_event_mouse_move(drgui_element* pElement, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- if (drgui_begin_outbound_event(pElement))
-{
- if (pElement->onMouseMove) {
- pElement->onMouseMove(pElement, relativeMousePosX, relativeMousePosY, stateFlags);
- }
-
- drgui_end_outbound_event(pElement);
- }
-}
-
-void drgui_post_outbound_event_mouse_button_down(drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- if (drgui_begin_outbound_event(pElement))
- {
- if (pElement->onMouseButtonDown) {
- pElement->onMouseButtonDown(pElement, mouseButton, relativeMousePosX, relativeMousePosY, stateFlags);
- }
-
- drgui_end_outbound_event(pElement);
- }
-}
-
-void drgui_post_outbound_event_mouse_button_up(drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- if (drgui_begin_outbound_event(pElement))
- {
- if (pElement->onMouseButtonUp) {
- pElement->onMouseButtonUp(pElement, mouseButton, relativeMousePosX, relativeMousePosY, stateFlags);
- }
-
- drgui_end_outbound_event(pElement);
- }
-}
-
-void drgui_post_outbound_event_mouse_button_dblclick(drgui_element* pElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- if (drgui_begin_outbound_event(pElement))
- {
- if (pElement->onMouseButtonDblClick) {
- pElement->onMouseButtonDblClick(pElement, mouseButton, relativeMousePosX, relativeMousePosY, stateFlags);
- }
-
- drgui_end_outbound_event(pElement);
- }
-}
-
-void drgui_post_outbound_event_mouse_wheel(drgui_element* pElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- if (drgui_begin_outbound_event(pElement))
- {
- if (pElement->onMouseWheel) {
- pElement->onMouseWheel(pElement, delta, relativeMousePosX, relativeMousePosY, stateFlags);
- }
-
- drgui_end_outbound_event(pElement);
- }
-}
-
-void drgui_post_outbound_event_key_down(drgui_element* pElement, drgui_key key, int stateFlags)
-{
- if (drgui_begin_outbound_event(pElement))
- {
- if (pElement->onKeyDown) {
- pElement->onKeyDown(pElement, key, stateFlags);
- }
-
- drgui_end_outbound_event(pElement);
- }
-}
-
-void drgui_post_outbound_event_key_up(drgui_element* pElement, drgui_key key, int stateFlags)
-{
- if (drgui_begin_outbound_event(pElement))
- {
- if (pElement->onKeyUp) {
- pElement->onKeyUp(pElement, key, stateFlags);
- }
-
- drgui_end_outbound_event(pElement);
- }
-}
-
-void drgui_post_outbound_event_printable_key_down(drgui_element* pElement, unsigned int character, int stateFlags)
-{
- if (drgui_begin_outbound_event(pElement))
- {
- if (pElement->onPrintableKeyDown) {
- pElement->onPrintableKeyDown(pElement, character, stateFlags);
- }
-
- drgui_end_outbound_event(pElement);
- }
-}
-
-
-void drgui_post_outbound_event_dirty(drgui_element* pElement, drgui_rect relativeRect)
-{
- if (pElement != NULL)
- {
- if (pElement->onDirty) {
- pElement->onDirty(pElement, relativeRect);
- }
- }
-}
-
-void drgui_post_outbound_event_dirty_global(drgui_element* pElement, drgui_rect relativeRect)
-{
- if (pElement != NULL && pElement->pContext != NULL)
- {
- if (pElement->pContext->onGlobalDirty) {
- pElement->pContext->onGlobalDirty(pElement, relativeRect);
- }
- }
-}
-
-void drgui_post_outbound_event_capture_mouse(drgui_element* pElement)
-{
- if (pElement != NULL)
- {
- if (pElement->onCaptureMouse) {
- pElement->onCaptureMouse(pElement);
- }
- }
-}
-
-void drgui_post_outbound_event_capture_mouse_global(drgui_element* pElement)
-{
- if (pElement != NULL && pElement->pContext != NULL)
- {
- if (pElement->pContext->onGlobalCaptureMouse) {
- pElement->pContext->onGlobalCaptureMouse(pElement);
- }
- }
-}
-
-void drgui_post_outbound_event_release_mouse(drgui_element* pElement)
-{
- if (pElement != NULL)
- {
- if (pElement->onReleaseMouse) {
- pElement->onReleaseMouse(pElement);
- }
- }
-}
-
-void drgui_post_outbound_event_release_mouse_global(drgui_element* pElement)
-{
- if (pElement != NULL && pElement->pContext != NULL)
- {
- if (pElement->pContext->onGlobalReleaseMouse) {
- pElement->pContext->onGlobalReleaseMouse(pElement);
- }
- }
-}
-
-
-void drgui_post_outbound_event_capture_keyboard(drgui_element* pElement, drgui_element* pPrevCapturedElement)
-{
- if (pElement != NULL)
- {
- if (pElement->onCaptureKeyboard) {
- pElement->onCaptureKeyboard(pElement, pPrevCapturedElement);
- }
- }
-}
-
-void drgui_post_outbound_event_capture_keyboard_global(drgui_element* pElement, drgui_element* pPrevCapturedElement)
-{
- if (pElement != NULL && pElement->pContext != NULL)
- {
- if (pElement->pContext->onGlobalCaptureKeyboard) {
- pElement->pContext->onGlobalCaptureKeyboard(pElement, pPrevCapturedElement);
- }
- }
-}
-
-void drgui_post_outbound_event_release_keyboard(drgui_element* pElement, drgui_element* pNewCapturedElement)
-{
- if (pElement != NULL)
- {
- if (pElement->onReleaseKeyboard) {
- pElement->onReleaseKeyboard(pElement, pNewCapturedElement);
- }
- }
-}
-
-void drgui_post_outbound_event_release_keyboard_global(drgui_element* pElement, drgui_element* pNewCapturedElement)
-{
- if (pElement != NULL && pElement->pContext != NULL)
- {
- if (pElement->pContext->onGlobalReleaseKeyboard) {
- pElement->pContext->onGlobalReleaseKeyboard(pElement, pNewCapturedElement);
- }
- }
-}
-
-
-void drgui_log(drgui_context* pContext, const char* message)
-{
- if (pContext != NULL)
- {
- if (pContext->onLog) {
- pContext->onLog(pContext, message);
- }
- }
-}
-
-
-/////////////////////////////////////////////////////////////////
-//
-// CORE API
-//
-/////////////////////////////////////////////////////////////////
-
-drgui_context* drgui_create_context()
-{
- drgui_context* pContext = (drgui_context*)calloc(1, sizeof(drgui_context));
- if (pContext != NULL) {
- pContext->currentCursor = drgui_cursor_default;
- }
-
- return pContext;
-}
-
-void drgui_delete_context(drgui_context* pContext)
-{
- if (pContext == NULL) {
- return;
- }
-
-
- // Make sure the mouse capture is released.
- if (pContext->pElementWithMouseCapture != NULL)
- {
- drgui_log(pContext, "WARNING: Deleting the GUI context while an element still has the mouse capture.");
- drgui_release_mouse(pContext);
- }
-
- // Make sure the keyboard capture is released.
- if (pContext->pElementWithKeyboardCapture != NULL)
- {
- drgui_log(pContext, "WARNING: Deleting the GUI context while an element still has the keyboard capture.");
- drgui_release_keyboard(pContext);
- }
-
-
- if (drgui_is_handling_inbound_event(pContext))
- {
- // An inbound event is still being processed - we don't want to delete the context straight away because we can't
- // trust external event handlers to not try to access the context later on. To do this we just set the flag that
- // the context is deleted. It will then be deleted for real at the end of the inbound event handler.
- drgui_mark_context_as_dead(pContext);
- }
- else
- {
- // An inbound event is not being processed, so delete the context straight away.
- drgui_delete_context_for_real(pContext);
- }
-}
-
-
-
-/////////////////////////////////////////////////////////////////
-// Events
-
-void drgui_post_inbound_event_mouse_leave(drgui_element* pTopLevelElement)
-{
- if (pTopLevelElement == NULL) {
- return;
- }
-
- drgui_context* pContext = pTopLevelElement->pContext;
- if (pContext == NULL) {
- return;
- }
-
- drgui_begin_inbound_event(pContext);
- {
- // We assume that was previously under the mouse was either pTopLevelElement itself or one of it's descendants.
- drgui_update_mouse_enter_and_leave_state(pContext, NULL);
- }
- drgui_end_inbound_event(pContext);
-}
-
-void drgui_post_inbound_event_mouse_move(drgui_element* pTopLevelElement, int mousePosX, int mousePosY, int stateFlags)
-{
- if (pTopLevelElement == NULL || pTopLevelElement->pContext == NULL) {
- return;
- }
-
-
- drgui_begin_inbound_event(pTopLevelElement->pContext);
- {
- /// A pointer to the top level element that was passed in from the last inbound mouse move event.
- pTopLevelElement->pContext->pLastMouseMoveTopLevelElement = pTopLevelElement;
-
- /// The position of the mouse that was passed in from the last inbound mouse move event.
- pTopLevelElement->pContext->lastMouseMovePosX = (float)mousePosX;
- pTopLevelElement->pContext->lastMouseMovePosY = (float)mousePosY;
-
-
-
- // The first thing we need to do is find the new element that's sitting under the mouse.
- drgui_element* pNewElementUnderMouse = drgui_find_element_under_point(pTopLevelElement, (float)mousePosX, (float)mousePosY);
-
- // Now that we know which element is sitting under the mouse we need to check if the mouse has entered into a new element.
- drgui_update_mouse_enter_and_leave_state(pTopLevelElement->pContext, pNewElementUnderMouse);
-
-
- drgui_element* pEventReceiver = pTopLevelElement->pContext->pElementWithMouseCapture;
- if (pEventReceiver == NULL)
- {
- pEventReceiver = pNewElementUnderMouse;
- }
-
- if (pEventReceiver != NULL)
- {
- float relativeMousePosX = (float)mousePosX;
- float relativeMousePosY = (float)mousePosY;
- drgui_make_point_relative(pEventReceiver, &relativeMousePosX, &relativeMousePosY);
-
- drgui_post_outbound_event_mouse_move(pEventReceiver, (int)relativeMousePosX, (int)relativeMousePosY, stateFlags);
- }
- }
- drgui_end_inbound_event(pTopLevelElement->pContext);
-}
-
-void drgui_post_inbound_event_mouse_button_down(drgui_element* pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags)
-{
- if (pTopLevelElement == NULL || pTopLevelElement->pContext == NULL) {
- return;
- }
-
- drgui_context* pContext = pTopLevelElement->pContext;
- drgui_begin_inbound_event(pContext);
- {
- drgui_element* pEventReceiver = pContext->pElementWithMouseCapture;
- if (pEventReceiver == NULL)
- {
- pEventReceiver = pContext->pElementUnderMouse;
-
- if (pEventReceiver == NULL)
- {
- // We'll get here if this message is posted without a prior mouse move event.
- pEventReceiver = drgui_find_element_under_point(pTopLevelElement, (float)mousePosX, (float)mousePosY);
- }
- }
-
-
- if (pEventReceiver != NULL)
- {
- float relativeMousePosX = (float)mousePosX;
- float relativeMousePosY = (float)mousePosY;
- drgui_make_point_relative(pEventReceiver, &relativeMousePosX, &relativeMousePosY);
-
- drgui_post_outbound_event_mouse_button_down(pEventReceiver, mouseButton, (int)relativeMousePosX, (int)relativeMousePosY, stateFlags);
- }
- }
- drgui_end_inbound_event(pContext);
-}
-
-void drgui_post_inbound_event_mouse_button_up(drgui_element* pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags)
-{
- if (pTopLevelElement == NULL || pTopLevelElement->pContext == NULL) {
- return;
- }
-
- drgui_context* pContext = pTopLevelElement->pContext;
- drgui_begin_inbound_event(pContext);
- {
- drgui_element* pEventReceiver = pContext->pElementWithMouseCapture;
- if (pEventReceiver == NULL)
- {
- pEventReceiver = pContext->pElementUnderMouse;
-
- if (pEventReceiver == NULL)
- {
- // We'll get here if this message is posted without a prior mouse move event.
- pEventReceiver = drgui_find_element_under_point(pTopLevelElement, (float)mousePosX, (float)mousePosY);
- }
- }
-
-
- if (pEventReceiver != NULL)
- {
- float relativeMousePosX = (float)mousePosX;
- float relativeMousePosY = (float)mousePosY;
- drgui_make_point_relative(pEventReceiver, &relativeMousePosX, &relativeMousePosY);
-
- drgui_post_outbound_event_mouse_button_up(pEventReceiver, mouseButton, (int)relativeMousePosX, (int)relativeMousePosY, stateFlags);
- }
- }
- drgui_end_inbound_event(pContext);
-}
-
-void drgui_post_inbound_event_mouse_button_dblclick(drgui_element* pTopLevelElement, int mouseButton, int mousePosX, int mousePosY, int stateFlags)
-{
- if (pTopLevelElement == NULL || pTopLevelElement->pContext == NULL) {
- return;
- }
-
- drgui_context* pContext = pTopLevelElement->pContext;
- drgui_begin_inbound_event(pContext);
- {
- drgui_element* pEventReceiver = pContext->pElementWithMouseCapture;
- if (pEventReceiver == NULL)
- {
- pEventReceiver = pContext->pElementUnderMouse;
-
- if (pEventReceiver == NULL)
- {
- // We'll get here if this message is posted without a prior mouse move event.
- pEventReceiver = drgui_find_element_under_point(pTopLevelElement, (float)mousePosX, (float)mousePosY);
- }
- }
-
-
- if (pEventReceiver != NULL)
- {
- float relativeMousePosX = (float)mousePosX;
- float relativeMousePosY = (float)mousePosY;
- drgui_make_point_relative(pEventReceiver, &relativeMousePosX, &relativeMousePosY);
-
- drgui_post_outbound_event_mouse_button_dblclick(pEventReceiver, mouseButton, (int)relativeMousePosX, (int)relativeMousePosY, stateFlags);
- }
- }
- drgui_end_inbound_event(pContext);
-}
-
-void drgui_post_inbound_event_mouse_wheel(drgui_element* pTopLevelElement, int delta, int mousePosX, int mousePosY, int stateFlags)
-{
- if (pTopLevelElement == NULL || pTopLevelElement->pContext == NULL) {
- return;
- }
-
- drgui_context* pContext = pTopLevelElement->pContext;
- drgui_begin_inbound_event(pContext);
- {
- drgui_element* pEventReceiver = pContext->pElementWithMouseCapture;
- if (pEventReceiver == NULL)
- {
- pEventReceiver = pContext->pElementUnderMouse;
-
- if (pEventReceiver == NULL)
- {
- // We'll get here if this message is posted without a prior mouse move event.
- pEventReceiver = drgui_find_element_under_point(pTopLevelElement, (float)mousePosX, (float)mousePosY);
- }
- }
-
-
- if (pEventReceiver != NULL)
- {
- float relativeMousePosX = (float)mousePosX;
- float relativeMousePosY = (float)mousePosY;
- drgui_make_point_relative(pEventReceiver, &relativeMousePosX, &relativeMousePosY);
-
- drgui_post_outbound_event_mouse_wheel(pEventReceiver, delta, (int)relativeMousePosX, (int)relativeMousePosY, stateFlags);
- }
- }
- drgui_end_inbound_event(pContext);
-}
-
-void drgui_post_inbound_event_key_down(drgui_context* pContext, drgui_key key, int stateFlags)
-{
- if (pContext == NULL) {
- return;
- }
-
- drgui_begin_inbound_event(pContext);
- {
- if (pContext->pElementWithKeyboardCapture != NULL) {
- drgui_post_outbound_event_key_down(pContext->pElementWithKeyboardCapture, key, stateFlags);
- }
- }
- drgui_end_inbound_event(pContext);
-}
-
-void drgui_post_inbound_event_key_up(drgui_context* pContext, drgui_key key, int stateFlags)
-{
- if (pContext == NULL) {
- return;
- }
-
- drgui_begin_inbound_event(pContext);
- {
- if (pContext->pElementWithKeyboardCapture != NULL) {
- drgui_post_outbound_event_key_up(pContext->pElementWithKeyboardCapture, key, stateFlags);
- }
- }
- drgui_end_inbound_event(pContext);
-}
-
-void drgui_post_inbound_event_printable_key_down(drgui_context* pContext, unsigned int character, int stateFlags)
-{
- if (pContext == NULL) {
- return;
- }
-
- drgui_begin_inbound_event(pContext);
- {
- if (pContext->pElementWithKeyboardCapture != NULL) {
- drgui_post_outbound_event_printable_key_down(pContext->pElementWithKeyboardCapture, character, stateFlags);
- }
- }
- drgui_end_inbound_event(pContext);
-}
-
-
-
-void drgui_set_global_on_dirty(drgui_context * pContext, drgui_on_dirty_proc onDirty)
-{
- if (pContext != NULL) {
- pContext->onGlobalDirty = onDirty;
- }
-}
-
-void drgui_set_global_on_capture_mouse(drgui_context* pContext, drgui_on_capture_mouse_proc onCaptureMouse)
-{
- if (pContext != NULL) {
- pContext->onGlobalCaptureMouse = onCaptureMouse;
- }
-}
-
-void drgui_set_global_on_release_mouse(drgui_context* pContext, drgui_on_release_mouse_proc onReleaseMouse)
-{
- if (pContext != NULL) {
- pContext->onGlobalReleaseMouse = onReleaseMouse;
- }
-}
-
-void drgui_set_global_on_capture_keyboard(drgui_context* pContext, drgui_on_capture_keyboard_proc onCaptureKeyboard)
-{
- if (pContext != NULL) {
- pContext->onGlobalCaptureKeyboard = onCaptureKeyboard;
- }
-}
-
-void drgui_set_global_on_release_keyboard(drgui_context* pContext, drgui_on_capture_keyboard_proc onReleaseKeyboard)
-{
- if (pContext != NULL) {
- pContext->onGlobalReleaseKeyboard = onReleaseKeyboard;
- }
-}
-
-void drgui_set_global_on_change_cursor(drgui_context* pContext, drgui_on_change_cursor_proc onChangeCursor)
-{
- if (pContext != NULL) {
- pContext->onChangeCursor = onChangeCursor;
- }
-}
-
-void drgui_set_on_delete_element(drgui_context* pContext, drgui_on_delete_element_proc onDeleteElement)
-{
- if (pContext != NULL) {
- pContext->onDeleteElement = onDeleteElement;
- }
-}
-
-void drgui_set_on_log(drgui_context* pContext, drgui_on_log onLog)
-{
- if (pContext != NULL) {
- pContext->onLog = onLog;
- }
-}
-
-
-
-/////////////////////////////////////////////////////////////////
-// Elements
-
-drgui_element* drgui_create_element(drgui_context* pContext, drgui_element* pParent, size_t extraDataSize, const void* pExtraData)
-{
- if (pContext != NULL)
- {
- drgui_element* pElement = (drgui_element*)calloc(1, sizeof(drgui_element) + extraDataSize);
- if (pElement != NULL) {
- pElement->pContext = pContext;
- pElement->pParent = pParent;
- pElement->cursor = drgui_cursor_default;
- pElement->dirtyRect = drgui_make_inside_out_rect();
-
- pElement->extraDataSize = extraDataSize;
- if (pExtraData != NULL) {
- memcpy(pElement->pExtraData, pExtraData, extraDataSize);
- }
-
- // Add to the the hierarchy.
- drgui_append_without_detach_or_redraw(pElement, pElement->pParent);
-
-
- // Have the element positioned at 0,0 relative to the parent by default.
- if (pParent != NULL) {
- pElement->absolutePosX = pParent->absolutePosX;
- pElement->absolutePosY = pParent->absolutePosY;
- }
-
-
- return pElement;
- }
- }
-
- return NULL;
-}
-
-void drgui_delete_element(drgui_element* pElement)
-{
- if (pElement == NULL) {
- return;
- }
-
- drgui_context* pContext = pElement->pContext;
- if (pContext == NULL) {
- return;
- }
-
- if (drgui_is_element_marked_as_dead(pElement)) {
- drgui_log(pContext, "WARNING: Attempting to delete an element that is already marked for deletion.");
- return;
- }
-
- drgui_mark_element_as_dead(pElement);
-
-
- // Notify the application that the element is being deleted. Do this at the top so the event handler can access things like the hierarchy and
- // whatnot in case it needs it.
- if (pContext->onDeleteElement) {
- pContext->onDeleteElement(pElement);
- }
-
-
- // If this was element is marked as the one that was last under the mouse it needs to be unset.
- bool needsMouseUpdate = false;
- if (pContext->pElementUnderMouse == pElement)
- {
- pContext->pElementUnderMouse = NULL;
- needsMouseUpdate = true;
- }
-
- if (pContext->pLastMouseMoveTopLevelElement == pElement)
- {
- pContext->pLastMouseMoveTopLevelElement = NULL;
- pContext->lastMouseMovePosX = 0;
- pContext->lastMouseMovePosY = 0;
- needsMouseUpdate = false; // It was a top-level element so the mouse enter/leave state doesn't need an update.
- }
-
-
- // If this element has the mouse capture it needs to be released.
- if (pContext->pElementWithMouseCapture == pElement)
- {
- drgui_log(pContext, "WARNING: Deleting an element while it still has the mouse capture.");
- drgui_release_mouse(pContext);
- }
-
- // If this element has the keyboard capture it needs to be released.
- if (pContext->pElementWithKeyboardCapture == pElement)
- {
- drgui_log(pContext, "WARNING: Deleting an element while it still has the keyboard capture.");
- drgui_release_keyboard(pContext);
- }
-
- // Is this element in the middle of being marked as dirty?
- for (size_t iDirtyElement = 0; iDirtyElement < pContext->dirtyElementCount; ++iDirtyElement) {
- if (pContext->ppDirtyElements[iDirtyElement] == pElement) {
- drgui_log(pContext, "WARNING: Deleting an element while it is being marked as dirty.");
- for (size_t iDirtyElement2 = iDirtyElement; iDirtyElement2+1 < pContext->dirtyElementCount; ++iDirtyElement2) {
- pContext->ppDirtyElements[iDirtyElement2] = pContext->ppDirtyElements[iDirtyElement2+1];
- }
-
- pContext->dirtyElementCount -= 1;
- break;
- }
- }
-
-#if 0
- if (pContext->pDirtyTopLevelElement == pElement)
- {
- drgui_log(pContext, "WARNING: Deleting an element while it is being marked as dirty.");
- pContext->pDirtyTopLevelElement = NULL;
- }
-#endif
-
-
-
- // Deleting this element may have resulted in the mouse entering a new element. Here is where we do a mouse enter/leave update.
- if (needsMouseUpdate)
- {
- pElement->onHitTest = drgui_pass_through_hit_test; // <-- This ensures we don't include this element when searching for the new element under the mouse.
- drgui_update_mouse_enter_and_leave_state(pContext, drgui_find_element_under_point(pContext->pLastMouseMoveTopLevelElement, pContext->lastMouseMovePosX, pContext->lastMouseMovePosY));
- }
-
-
- // Here is where we need to detach the element from the hierarchy. When doing this we want to ensure the element is not redrawn when
- // it's children are detached. To do this we simply detach the event handler.
- pElement->onPaint = NULL;
-
- // The parent needs to be redraw after detaching.
- drgui_element* pParent = pElement->pParent;
- drgui_rect relativeRect = drgui_get_relative_rect(pElement);
-
-
- // Orphan the element first.
- drgui_detach_without_redraw(pElement);
-
- // Children need to be deleted before deleting the element itself.
- while (pElement->pLastChild != NULL) {
- drgui_delete_element(pElement->pLastChild);
- }
-
-
- // The parent needs to be redrawn.
- if (pParent) {
- drgui_dirty(pParent, relativeRect);
- }
-
-
- // Finally, we to decided whether or not the element should be deleted for real straight away or not. If the element is being
- // deleted within an event handler it should be delayed because the event handlers may try referencing it afterwards.
- if (!drgui_is_handling_inbound_event(pContext)) {
- drgui_delete_element_for_real(pElement);
- }
-}
-
-
-size_t drgui_get_extra_data_size(drgui_element* pElement)
-{
- if (pElement != NULL) {
- return pElement->extraDataSize;
- }
-
- return 0;
-}
-
-void* drgui_get_extra_data(drgui_element* pElement)
-{
- if (pElement != NULL) {
- return pElement->pExtraData;
- }
-
- return NULL;
-}
-
-
-bool drgui_set_type(drgui_element* pElement, const char* type)
-{
- if (pElement == NULL) {
- return false;
- }
-
- return drgui__strcpy_s(pElement->type, sizeof(pElement->type), (type == NULL) ? "" : type) == 0;
-}
-
-const char* drgui_get_type(drgui_element* pElement)
-{
- if (pElement == NULL) {
- return NULL;
- }
-
- return pElement->type;
-}
-
-bool drgui_is_of_type(drgui_element* pElement, const char* type)
-{
- if (pElement == NULL || type == NULL) {
- return false;
- }
-
- return strncmp(pElement->type, type, strlen(type)) == 0;
-}
-
-
-void drgui_hide(drgui_element* pElement)
-{
- if (pElement != NULL) {
- pElement->flags |= IS_ELEMENT_HIDDEN;
- drgui_auto_dirty(pElement, drgui_get_local_rect(pElement));
- }
-}
-
-void drgui_show(drgui_element* pElement)
-{
- if (pElement != NULL) {
- pElement->flags &= ~IS_ELEMENT_HIDDEN;
- drgui_auto_dirty(pElement, drgui_get_local_rect(pElement));
- }
-}
-
-bool drgui_is_visible(const drgui_element* pElement)
-{
- if (pElement != NULL) {
- return (pElement->flags & IS_ELEMENT_HIDDEN) == 0;
- }
-
- return false;
-}
-
-bool drgui_is_visible_recursive(const drgui_element* pElement)
-{
- if (drgui_is_visible(pElement))
- {
- assert(pElement->pParent != NULL);
-
- if (pElement->pParent != NULL) {
- return drgui_is_visible(pElement->pParent);
- }
- }
-
- return false;
-}
-
-
-void drgui_disable_clipping(drgui_element* pElement)
-{
- if (pElement != NULL) {
- pElement->flags |= IS_ELEMENT_CLIPPING_DISABLED;
- }
-}
-
-void drgui_enable_clipping(drgui_element* pElement)
-{
- if (pElement != NULL) {
- pElement->flags &= ~IS_ELEMENT_CLIPPING_DISABLED;
- }
-}
-
-bool drgui_is_clipping_enabled(const drgui_element* pElement)
-{
- if (pElement != NULL) {
- return (pElement->flags & IS_ELEMENT_CLIPPING_DISABLED) == 0;
- }
-
- return true;
-}
-
-
-
-void drgui_capture_mouse(drgui_element* pElement)
-{
- if (pElement == NULL) {
- return;
- }
-
- if (pElement->pContext == NULL) {
- return;
- }
-
-
- if (pElement->pContext->pElementWithMouseCapture != pElement)
- {
- // Release the previous capture first.
- if (pElement->pContext->pElementWithMouseCapture != NULL) {
- drgui_release_mouse(pElement->pContext);
- }
-
- assert(pElement->pContext->pElementWithMouseCapture == NULL);
-
- pElement->pContext->pElementWithMouseCapture = pElement;
-
- // Two events need to be posted - the global on_capture_mouse event and the local on_capture_mouse event.
- drgui_post_outbound_event_capture_mouse(pElement);
-
- if (pElement == pElement->pContext->pElementWithMouseCapture) { // <-- Only post the global event handler if the element still has the capture.
- drgui_post_outbound_event_capture_mouse_global(pElement);
- }
- }
-}
-
-void drgui_release_mouse(drgui_context* pContext)
-{
- if (pContext == NULL) {
- return;
- }
-
-
- // Events need to be posted before setting the internal pointer.
- if (!drgui_is_element_marked_as_dead(pContext->pElementWithMouseCapture)) { // <-- There's a chace the element is releaseing the keyboard due to being deleted. Don't want to post an event in this case.
- drgui_post_outbound_event_release_mouse(pContext->pElementWithMouseCapture);
- drgui_post_outbound_event_release_mouse_global(pContext->pElementWithMouseCapture);
- }
-
- // We want to set the internal pointer to NULL after posting the events since that is when it has truly released the mouse.
- pContext->pElementWithMouseCapture = NULL;
-
-
- // After releasing the mouse the cursor may be sitting on top of a different element - we want to recheck that.
- drgui_update_mouse_enter_and_leave_state(pContext, drgui_find_element_under_point(pContext->pLastMouseMoveTopLevelElement, pContext->lastMouseMovePosX, pContext->lastMouseMovePosY));
-}
-
-void drgui_release_mouse_no_global_notify(drgui_context* pContext)
-{
- if (pContext == NULL) {
- return;
- }
-
- drgui_on_release_mouse_proc prevProc = pContext->onGlobalReleaseMouse;
- pContext->onGlobalReleaseMouse = NULL;
- drgui_release_mouse(pContext);
- pContext->onGlobalReleaseMouse = prevProc;
-}
-
-drgui_element* drgui_get_element_with_mouse_capture(drgui_context* pContext)
-{
- if (pContext == NULL) {
- return NULL;
- }
-
- return pContext->pElementWithMouseCapture;
-}
-
-bool drgui_has_mouse_capture(drgui_element* pElement)
-{
- if (pElement == NULL) {
- return false;
- }
-
- return drgui_get_element_with_mouse_capture(pElement->pContext) == pElement;
-}
-
-
-DRGUI_PRIVATE void drgui_release_keyboard_private(drgui_context* pContext, drgui_element* pNewCapturedElement)
-{
- assert(pContext != NULL);
-
- // It is reasonable to expect that an application will want to change keyboard focus from within the release_keyboard
- // event handler. The problem with this is that is can cause a infinite dependency chain. We need to handle that case
- // by setting a flag that keeps track of whether or not we are in the middle of a release_keyboard event. At the end
- // we look at the element that want's the keyboard focuse and explicitly capture it at the end.
-
- pContext->flags |= IS_RELEASING_KEYBOARD;
- {
- drgui_element* pPrevCapturedElement = pContext->pElementWithKeyboardCapture;
- pContext->pElementWithKeyboardCapture = NULL;
-
- if (!drgui_is_element_marked_as_dead(pPrevCapturedElement)) { // <-- There's a chace the element is releaseing the keyboard due to being deleted. Don't want to post an event in this case.
- drgui_post_outbound_event_release_keyboard(pPrevCapturedElement, pNewCapturedElement);
- drgui_post_outbound_event_release_keyboard_global(pPrevCapturedElement, pNewCapturedElement);
- }
- }
- pContext->flags &= ~IS_RELEASING_KEYBOARD;
-
- // Explicitly capture the keyboard.
- drgui_capture_keyboard(pContext->pElementWantingKeyboardCapture);
- pContext->pElementWantingKeyboardCapture = NULL;
-}
-
-void drgui_capture_keyboard(drgui_element* pElement)
-{
- if (pElement == NULL) {
- return;
- }
-
- if (pElement->pContext == NULL) {
- return;
- }
-
-
- if ((pElement->pContext->flags & IS_RELEASING_KEYBOARD) != 0) {
- pElement->pContext->pElementWantingKeyboardCapture = pElement;
- return;
- }
-
-
- if (pElement->pContext->pElementWithKeyboardCapture != pElement)
- {
- // Release the previous capture first.
- drgui_element* pPrevElementWithKeyboardCapture = pElement->pContext->pElementWithKeyboardCapture;
- if (pPrevElementWithKeyboardCapture != NULL) {
- drgui_release_keyboard_private(pElement->pContext, pElement);
- }
-
- assert(pElement->pContext->pElementWithKeyboardCapture == NULL);
-
- pElement->pContext->pElementWithKeyboardCapture = pElement;
- pElement->pContext->pElementWantingKeyboardCapture = NULL;
-
- // Two events need to be posted - the global on_capture event and the local on_capture event. The problem, however, is that the
- // local event handler may change the keyboard capture internally, such as if it wants to pass it's focus onto an internal child
- // element or whatnot. In this case we don't want to fire the global event handler because it will result in superfluous event
- // posting, and could also be posted with an incorrect element.
- drgui_post_outbound_event_capture_keyboard(pElement, pPrevElementWithKeyboardCapture);
-
- if (pElement == pElement->pContext->pElementWithKeyboardCapture) { // <-- Only post the global event handler if the element still has the capture.
- drgui_post_outbound_event_capture_keyboard_global(pElement, pPrevElementWithKeyboardCapture);
- }
- }
-}
-
-void drgui_release_keyboard(drgui_context* pContext)
-{
- if (pContext == NULL) {
- return;
- }
-
- drgui_release_keyboard_private(pContext, NULL);
-}
-
-void drgui_release_keyboard_no_global_notify(drgui_context* pContext)
-{
- if (pContext == NULL) {
- return;
- }
-
- drgui_on_release_keyboard_proc prevProc = pContext->onGlobalReleaseKeyboard;
- pContext->onGlobalReleaseKeyboard = NULL;
- drgui_release_keyboard(pContext);
- pContext->onGlobalReleaseKeyboard = prevProc;
-}
-
-drgui_element* drgui_get_element_with_keyboard_capture(drgui_context* pContext)
-{
- if (pContext == NULL) {
- return NULL;
- }
-
- return pContext->pElementWithKeyboardCapture;
-}
-
-bool drgui_has_keyboard_capture(drgui_element* pElement)
-{
- if (pElement == NULL) {
- return false;
- }
-
- return drgui_get_element_with_keyboard_capture(pElement->pContext) == pElement;
-}
-
-
-void drgui_set_cursor(drgui_element* pElement, drgui_cursor_type cursor)
-{
- if (pElement == NULL) {
- return;
- }
-
- pElement->cursor = cursor;
-
- if (drgui_is_element_under_mouse(pElement) && pElement->pContext->currentCursor != cursor) {
- drgui__change_cursor(pElement, cursor);
- }
-}
-
-drgui_cursor_type drgui_get_cursor(drgui_element* pElement)
-{
- if (pElement == NULL) {
- return drgui_cursor_none;
- }
-
- return pElement->cursor;
-}
-
-
-
-//// Events ////
-
-void drgui_set_on_move(drgui_element * pElement, drgui_on_move_proc callback)
-{
- if (pElement != NULL) {
- pElement->onMove = callback;
- }
-}
-
-void drgui_set_on_size(drgui_element * pElement, drgui_on_size_proc callback)
-{
- if (pElement != NULL) {
- pElement->onSize = callback;
- }
-}
-
-void drgui_set_on_mouse_enter(drgui_element* pElement, drgui_on_mouse_enter_proc callback)
-{
- if (pElement != NULL) {
- pElement->onMouseEnter = callback;
- }
-}
-
-void drgui_set_on_mouse_leave(drgui_element* pElement, drgui_on_mouse_leave_proc callback)
-{
- if (pElement != NULL) {
- pElement->onMouseLeave = callback;
- }
-}
-
-void drgui_set_on_mouse_move(drgui_element* pElement, drgui_on_mouse_move_proc callback)
-{
- if (pElement != NULL) {
- pElement->onMouseMove = callback;
- }
-}
-
-void drgui_set_on_mouse_button_down(drgui_element* pElement, drgui_on_mouse_button_down_proc callback)
-{
- if (pElement != NULL) {
- pElement->onMouseButtonDown = callback;
- }
-}
-
-void drgui_set_on_mouse_button_up(drgui_element* pElement, drgui_on_mouse_button_up_proc callback)
-{
- if (pElement != NULL) {
- pElement->onMouseButtonUp = callback;
- }
-}
-
-void drgui_set_on_mouse_button_dblclick(drgui_element* pElement, drgui_on_mouse_button_dblclick_proc callback)
-{
- if (pElement != NULL) {
- pElement->onMouseButtonDblClick = callback;
- }
-}
-
-void drgui_set_on_mouse_wheel(drgui_element* pElement, drgui_on_mouse_wheel_proc callback)
-{
- if (pElement != NULL) {
- pElement->onMouseWheel = callback;
- }
-}
-
-void drgui_set_on_key_down(drgui_element* pElement, drgui_on_key_down_proc callback)
-{
- if (pElement != NULL) {
- pElement->onKeyDown = callback;
- }
-}
-
-void drgui_set_on_key_up(drgui_element* pElement, drgui_on_key_up_proc callback)
-{
- if (pElement != NULL) {
- pElement->onKeyUp = callback;
- }
-}
-
-void drgui_set_on_printable_key_down(drgui_element* pElement, drgui_on_printable_key_down_proc callback)
-{
- if (pElement != NULL) {
- pElement->onPrintableKeyDown = callback;
- }
-}
-
-void drgui_set_on_paint(drgui_element* pElement, drgui_on_paint_proc callback)
-{
- if (pElement != NULL) {
- pElement->onPaint = callback;
- }
-}
-
-void drgui_set_on_dirty(drgui_element * pElement, drgui_on_dirty_proc callback)
-{
- if (pElement != NULL) {
- pElement->onDirty = callback;
- }
-}
-
-void drgui_set_on_hittest(drgui_element* pElement, drgui_on_hittest_proc callback)
-{
- if (pElement != NULL) {
- pElement->onHitTest = callback;
- }
-}
-
-void drgui_set_on_capture_mouse(drgui_element* pElement, drgui_on_capture_mouse_proc callback)
-{
- if (pElement != NULL) {
- pElement->onCaptureMouse = callback;
- }
-}
-
-void drgui_set_on_release_mouse(drgui_element* pElement, drgui_on_release_mouse_proc callback)
-{
- if (pElement != NULL) {
- pElement->onReleaseMouse = callback;
- }
-}
-
-void drgui_set_on_capture_keyboard(drgui_element* pElement, drgui_on_capture_keyboard_proc callback)
-{
- if (pElement != NULL) {
- pElement->onCaptureKeyboard = callback;
- }
-}
-
-void drgui_set_on_release_keyboard(drgui_element* pElement, drgui_on_release_keyboard_proc callback)
-{
- if (pElement != NULL) {
- pElement->onReleaseKeyboard = callback;
- }
-}
-
-
-
-bool drgui_is_point_inside_element_bounds(const drgui_element* pElement, float absolutePosX, float absolutePosY)
-{
- if (absolutePosX < pElement->absolutePosX ||
- absolutePosX < pElement->absolutePosY)
- {
- return false;
- }
-
- if (absolutePosX >= pElement->absolutePosX + pElement->width ||
- absolutePosY >= pElement->absolutePosY + pElement->height)
- {
- return false;
- }
-
- return true;
-}
-
-bool drgui_is_point_inside_element(drgui_element* pElement, float absolutePosX, float absolutePosY)
-{
- if (drgui_is_point_inside_element_bounds(pElement, absolutePosX, absolutePosY))
- {
- // It is valid for onHitTest to be null, in which case we use the default hit test which assumes the element is just a rectangle
- // equal to the size of it's bounds. It's equivalent to onHitTest always returning true.
-
- if (pElement->onHitTest) {
- return pElement->onHitTest(pElement, absolutePosX - pElement->absolutePosX, absolutePosY - pElement->absolutePosY);
- }
-
- return true;
- }
-
- return false;
-}
-
-
-
-typedef struct
-{
- drgui_element* pElementUnderPoint;
- float absolutePosX;
- float absolutePosY;
-}drgui_find_element_under_point_data;
-
-bool drgui_find_element_under_point_iterator(drgui_element* pElement, drgui_rect* pRelativeVisibleRect, void* pUserData)
-{
- assert(pElement != NULL);
- assert(pRelativeVisibleRect != NULL);
-
- drgui_find_element_under_point_data* pData = (drgui_find_element_under_point_data*)pUserData;
- assert(pData != NULL);
-
- float relativePosX = pData->absolutePosX;
- float relativePosY = pData->absolutePosY;
- drgui_make_point_relative(pElement, &relativePosX, &relativePosY);
-
- if (drgui_rect_contains_point(*pRelativeVisibleRect, relativePosX, relativePosY))
- {
- if (pElement->onHitTest) {
- if (pElement->onHitTest(pElement, relativePosX, relativePosY)) {
- pData->pElementUnderPoint = pElement;
- }
- } else {
- pData->pElementUnderPoint = pElement;
- }
- }
-
-
- // Always return true to ensure the entire hierarchy is checked.
- return true;
-}
-
-drgui_element* drgui_find_element_under_point(drgui_element* pTopLevelElement, float absolutePosX, float absolutePosY)
-{
- if (pTopLevelElement == NULL) {
- return NULL;
- }
-
- drgui_find_element_under_point_data data;
- data.pElementUnderPoint = NULL;
- data.absolutePosX = absolutePosX;
- data.absolutePosY = absolutePosY;
- drgui_iterate_visible_elements(pTopLevelElement, drgui_get_absolute_rect(pTopLevelElement), drgui_find_element_under_point_iterator, &data);
-
- return data.pElementUnderPoint;
-}
-
-bool drgui_is_element_under_mouse(drgui_element* pElement)
-{
- if (pElement == NULL) {
- return false;
- }
-
- return drgui_find_element_under_point(pElement->pContext->pLastMouseMoveTopLevelElement, pElement->pContext->lastMouseMovePosX, pElement->pContext->lastMouseMovePosY) == pElement;
-}
-
-
-
-//// Hierarchy ////
-
-drgui_element* drgui_get_parent(drgui_element* pChildElement)
-{
- if (pChildElement == NULL) {
- return NULL;
- }
-
- return pChildElement->pParent;
-}
-
-void drgui_detach(drgui_element* pChildElement)
-{
- if (pChildElement == NULL) {
- return;
- }
-
- drgui_element* pOldParent = pChildElement->pParent;
-
-
- // We orphan the element using the private API. This will not mark the parent element as dirty so we need to do that afterwards.
- drgui_detach_without_redraw(pChildElement);
-
- // The region of the old parent needs to be redrawn.
- if (pOldParent != NULL) {
- drgui_auto_dirty(pOldParent, drgui_get_relative_rect(pOldParent));
- }
-}
-
-void drgui_append(drgui_element* pChildElement, drgui_element* pParentElement)
-{
- if (pChildElement == NULL) {
- return;
- }
-
- // We first need to orphan the element. If the parent element is the new parent is the same as the old one, as in we
- // are just moving the child element to the end of the children list, we want to delay the repaint until the end. To
- // do this we use drgui_detach_without_redraw() because that will not trigger a redraw.
- if (pChildElement->pParent != pParentElement) {
- drgui_detach(pChildElement);
- } else {
- drgui_detach_without_redraw(pChildElement);
- }
-
-
- // Now we attach it to the end of the new parent.
- if (pParentElement != NULL) {
- drgui_append_without_detach(pChildElement, pParentElement);
- }
-}
-
-void drgui_prepend(drgui_element* pChildElement, drgui_element* pParentElement)
-{
- if (pChildElement == NULL) {
- return;
- }
-
- // See comment in drgui_append() for explanation on this.
- if (pChildElement->pParent != pParentElement) {
- drgui_detach(pChildElement);
- } else {
- drgui_detach_without_redraw(pChildElement);
- }
-
-
- // Now we need to attach the element to the beginning of the parent.
- if (pParentElement != NULL) {
- drgui_prepend_without_detach(pChildElement, pParentElement);
- }
-}
-
-void drgui_append_sibling(drgui_element* pElementToAppend, drgui_element* pElementToAppendTo)
-{
- if (pElementToAppend == NULL || pElementToAppendTo == NULL) {
- return;
- }
-
- // See comment in drgui_append() for explanation on this.
- if (pElementToAppend->pParent != pElementToAppendTo->pParent) {
- drgui_detach(pElementToAppend);
- } else {
- drgui_detach_without_redraw(pElementToAppend);
- }
-
-
- // Now we need to attach the element such that it comes just after pElementToAppendTo
- drgui_append_sibling_without_detach(pElementToAppend, pElementToAppendTo);
-}
-
-void drgui_prepend_sibling(drgui_element* pElementToPrepend, drgui_element* pElementToPrependTo)
-{
- if (pElementToPrepend == NULL || pElementToPrependTo == NULL) {
- return;
- }
-
- // See comment in drgui_append() for explanation on this.
- if (pElementToPrepend->pParent != pElementToPrependTo->pParent) {
- drgui_detach(pElementToPrepend);
- } else {
- drgui_detach_without_redraw(pElementToPrepend);
- }
-
-
- // Now we need to attach the element such that it comes just after pElementToPrependTo
- drgui_prepend_sibling_without_detach(pElementToPrepend, pElementToPrependTo);
-}
-
-drgui_element* drgui_find_top_level_element(drgui_element* pElement)
-{
- if (pElement == NULL) {
- return NULL;
- }
-
- if (pElement->pParent != NULL) {
- return drgui_find_top_level_element(pElement->pParent);
- }
-
- return pElement;
-}
-
-bool drgui_is_parent(drgui_element* pParentElement, drgui_element* pChildElement)
-{
- if (pParentElement == NULL || pChildElement == NULL) {
- return false;
- }
-
- return pParentElement == pChildElement->pParent;
-}
-
-bool drgui_is_child(drgui_element* pChildElement, drgui_element* pParentElement)
-{
- return drgui_is_parent(pParentElement, pChildElement);
-}
-
-bool drgui_is_ancestor(drgui_element* pAncestorElement, drgui_element* pChildElement)
-{
- if (pAncestorElement == NULL || pChildElement == NULL) {
- return false;
- }
-
- drgui_element* pParent = pChildElement->pParent;
- while (pParent != NULL)
- {
- if (pParent == pAncestorElement) {
- return true;
- }
-
- pParent = pParent->pParent;
- }
-
-
- return false;
-}
-
-bool drgui_is_descendant(drgui_element* pChildElement, drgui_element* pAncestorElement)
-{
- return drgui_is_ancestor(pAncestorElement, pChildElement);
-}
-
-bool drgui_is_self_or_ancestor(drgui_element* pAncestorElement, drgui_element* pChildElement)
-{
- return pAncestorElement == pChildElement || drgui_is_ancestor(pAncestorElement, pChildElement);
-}
-
-bool drgui_is_self_or_descendant(drgui_element* pChildElement, drgui_element* pAncestorElement)
-{
- return pChildElement == pAncestorElement || drgui_is_descendant(pChildElement, pAncestorElement);
-}
-
-
-
-//// Layout ////
-
-void drgui_set_absolute_position(drgui_element* pElement, float positionX, float positionY)
-{
- if (pElement != NULL)
- {
- if (pElement->absolutePosX != positionX || pElement->absolutePosY != positionY)
- {
- float oldRelativePosX = drgui_get_relative_position_x(pElement);
- float oldRelativePosY = drgui_get_relative_position_y(pElement);
-
- drgui_begin_auto_dirty(pElement);
- {
- drgui_auto_dirty(pElement, drgui_get_local_rect(pElement)); // <-- Previous rectangle.
-
- float offsetX = positionX - pElement->absolutePosX;
- float offsetY = positionY - pElement->absolutePosY;
-
- pElement->absolutePosX = positionX;
- pElement->absolutePosY = positionY;
- drgui_auto_dirty(pElement, drgui_get_local_rect(pElement)); // <-- New rectangle.
-
-
- float newRelativePosX = drgui_get_relative_position_x(pElement);
- float newRelativePosY = drgui_get_relative_position_y(pElement);
-
- if (newRelativePosX != oldRelativePosX || newRelativePosY != oldRelativePosY) {
- drgui_post_outbound_event_move(pElement, newRelativePosX, newRelativePosY);
- }
-
-
- drgui_apply_offset_to_children_recursive(pElement, offsetX, offsetY);
- }
- drgui_end_auto_dirty(pElement);
- }
- }
-}
-
-void drgui_get_absolute_position(const drgui_element* pElement, float * positionXOut, float * positionYOut)
-{
- if (pElement != NULL)
- {
- if (positionXOut != NULL) {
- *positionXOut = pElement->absolutePosX;
- }
-
- if (positionYOut != NULL) {
- *positionYOut = pElement->absolutePosY;
- }
- }
-}
-
-float drgui_get_absolute_position_x(const drgui_element* pElement)
-{
- if (pElement != NULL) {
- return pElement->absolutePosX;
- }
-
- return 0.0f;
-}
-
-float drgui_get_absolute_position_y(const drgui_element* pElement)
-{
- if (pElement != NULL) {
- return pElement->absolutePosY;
- }
-
- return 0.0f;
-}
-
-
-void drgui_set_relative_position(drgui_element* pElement, float relativePosX, float relativePosY)
-{
- if (pElement != NULL) {
- if (pElement->pParent != NULL) {
- drgui_set_absolute_position(pElement, pElement->pParent->absolutePosX + relativePosX, pElement->pParent->absolutePosY + relativePosY);
- } else {
- drgui_set_absolute_position(pElement, relativePosX, relativePosY);
- }
- }
-}
-
-void drgui_get_relative_position(const drgui_element* pElement, float* positionXOut, float* positionYOut)
-{
- if (pElement != NULL)
- {
- if (pElement->pParent != NULL)
- {
- if (positionXOut != NULL) {
- *positionXOut = pElement->absolutePosX - pElement->pParent->absolutePosX;
- }
-
- if (positionYOut != NULL) {
- *positionYOut = pElement->absolutePosY - pElement->pParent->absolutePosY;
- }
- }
- else
- {
- if (positionXOut != NULL) {
- *positionXOut = pElement->absolutePosX;
- }
-
- if (positionYOut != NULL) {
- *positionYOut = pElement->absolutePosY;
- }
- }
- }
-}
-
-float drgui_get_relative_position_x(const drgui_element* pElement)
-{
- if (pElement != NULL) {
- if (pElement->pParent != NULL) {
- return pElement->absolutePosX - pElement->pParent->absolutePosX;
- } else {
- return pElement->absolutePosX;
- }
- }
-
- return 0;
-}
-
-float drgui_get_relative_position_y(const drgui_element* pElement)
-{
- if (pElement != NULL) {
- if (pElement->pParent != NULL) {
- return pElement->absolutePosY - pElement->pParent->absolutePosY;
- } else {
- return pElement->absolutePosY;
- }
- }
-
- return 0;
-}
-
-
-void drgui_set_size(drgui_element* pElement, float width, float height)
-{
- if (pElement != NULL)
- {
- if (pElement->width != width || pElement->height != height)
- {
- drgui_begin_auto_dirty(pElement);
- {
- drgui_auto_dirty(pElement, drgui_get_local_rect(pElement)); // <-- Previous rectangle.
-
- pElement->width = width;
- pElement->height = height;
- drgui_auto_dirty(pElement, drgui_get_local_rect(pElement)); // <-- New rectangle.
-
- drgui_post_outbound_event_size(pElement, width, height);
- }
- drgui_end_auto_dirty(pElement);
- }
- }
-}
-
-void drgui_get_size(const drgui_element* pElement, float* widthOut, float* heightOut)
-{
- if (pElement != NULL) {
- if (widthOut) *widthOut = pElement->width;
- if (heightOut) *heightOut = pElement->height;
- } else {
- if (widthOut) *widthOut = 0;
- if (heightOut) *heightOut = 0;
- }
-}
-
-float drgui_get_width(const drgui_element * pElement)
-{
- if (pElement != NULL) {
- return pElement->width;
- }
-
- return 0;
-}
-
-float drgui_get_height(const drgui_element * pElement)
-{
- if (pElement != NULL) {
- return pElement->height;
- }
-
- return 0;
-}
-
-
-drgui_rect drgui_get_absolute_rect(const drgui_element* pElement)
-{
- drgui_rect rect;
- if (pElement != NULL)
- {
- rect.left = pElement->absolutePosX;
- rect.top = pElement->absolutePosY;
- rect.right = rect.left + pElement->width;
- rect.bottom = rect.top + pElement->height;
- }
- else
- {
- rect.left = 0;
- rect.top = 0;
- rect.right = 0;
- rect.bottom = 0;
- }
-
- return rect;
-}
-
-drgui_rect drgui_get_relative_rect(const drgui_element* pElement)
-{
- drgui_rect rect;
- if (pElement != NULL)
- {
- rect.left = drgui_get_relative_position_x(pElement);
- rect.top = drgui_get_relative_position_y(pElement);
- rect.right = rect.left + pElement->width;
- rect.bottom = rect.top + pElement->height;
- }
- else
- {
- rect.left = 0;
- rect.top = 0;
- rect.right = 0;
- rect.bottom = 0;
- }
-
- return rect;
-}
-
-drgui_rect drgui_get_local_rect(const drgui_element* pElement)
-{
- drgui_rect rect;
- rect.left = 0;
- rect.top = 0;
-
- if (pElement != NULL)
- {
- rect.right = pElement->width;
- rect.bottom = pElement->height;
- }
- else
- {
- rect.right = 0;
- rect.bottom = 0;
- }
-
- return rect;
-}
-
-
-
-//// Painting ////
-
-bool drgui_register_painting_callbacks(drgui_context* pContext, void* pPaintingContext, drgui_painting_callbacks callbacks)
-{
- if (pContext == NULL) {
- return false;
- }
-
- // Fail if the painting callbacks have already been registered.
- if (pContext->pPaintingContext != NULL) {
- return false;
- }
-
-
- pContext->pPaintingContext = pPaintingContext;
- pContext->paintingCallbacks = callbacks;
-
- return true;
-}
-
-
-bool drgui_iterate_visible_elements(drgui_element* pParentElement, drgui_rect relativeRect, drgui_visible_iteration_proc callback, void* pUserData)
-{
- if (pParentElement == NULL) {
- return false;
- }
-
- if (callback == NULL) {
- return false;
- }
-
-
- if (!drgui_is_visible(pParentElement)) {
- return true;
- }
-
- drgui_rect clampedRelativeRect = relativeRect;
- if (drgui_clamp_rect_to_element(pParentElement, &clampedRelativeRect))
- {
- // We'll only get here if some part of the rectangle was inside the element.
- if (!callback(pParentElement, &clampedRelativeRect, pUserData)) {
- return false;
- }
- }
-
- for (drgui_element* pChild = pParentElement->pFirstChild; pChild != NULL; pChild = pChild->pNextSibling)
- {
- float childRelativePosX = drgui_get_relative_position_x(pChild);
- float childRelativePosY = drgui_get_relative_position_y(pChild);
-
- drgui_rect childRect;
- if (drgui_is_clipping_enabled(pChild)) {
- childRect = clampedRelativeRect;
- } else {
- childRect = relativeRect;
- }
-
-
- childRect.left -= childRelativePosX;
- childRect.top -= childRelativePosY;
- childRect.right -= childRelativePosX;
- childRect.bottom -= childRelativePosY;
-
- if (!drgui_iterate_visible_elements(pChild, childRect, callback, pUserData)) {
- return false;
- }
- }
-
-
- return true;
-}
-
-void drgui_disable_auto_dirty(drgui_context* pContext)
-{
- if (pContext != NULL) {
- pContext->flags |= IS_AUTO_DIRTY_DISABLED;
- }
-}
-
-void drgui_enable_auto_dirty(drgui_context* pContext)
-{
- if (pContext != NULL) {
- pContext->flags &= ~IS_AUTO_DIRTY_DISABLED;
- }
-}
-
-bool drgui_is_auto_dirty_enabled(drgui_context* pContext)
-{
- if (pContext != NULL) {
- return (pContext->flags & IS_AUTO_DIRTY_DISABLED) == 0;
- }
-
- return false;
-}
-
-
-drgui_element* drgui_begin_dirty(drgui_element* pElement)
-{
- if (pElement == NULL) {
- return NULL;
- }
-
- drgui_context* pContext = pElement->pContext;
- assert(pContext != NULL);
-
- drgui_element* pTopLevelElement = drgui_find_top_level_element(pElement);
- assert(pTopLevelElement != NULL);
-
- // The element needs to be added to the list of dirty elements if it doesn't exist already.
- bool isAlreadyDirty = false;
- for (size_t iDirtyElementCount = 0; iDirtyElementCount < pContext->dirtyElementCount; ++iDirtyElementCount) {
- if (pContext->ppDirtyElements[iDirtyElementCount] == pTopLevelElement) {
- isAlreadyDirty = true;
- break;
- }
- }
-
- if (!isAlreadyDirty) {
- if (pContext->dirtyElementCount == pContext->dirtyElementBufferSize) {
- size_t newBufferSize = pContext->dirtyElementBufferSize == 0 ? 1 : pContext->dirtyElementBufferSize*2;
- drgui_element** ppNewDirtyElements = (drgui_element**)realloc(pContext->ppDirtyElements, newBufferSize * sizeof(*ppNewDirtyElements));
- if (ppNewDirtyElements == NULL) {
- return NULL;
- }
-
- pContext->ppDirtyElements = ppNewDirtyElements;
- pContext->dirtyElementBufferSize = newBufferSize;
- }
-
- pContext->ppDirtyElements[pContext->dirtyCounter] = pTopLevelElement;
- pContext->dirtyElementCount += 1;
- }
-
-
- pContext->dirtyCounter += 1;
- return pTopLevelElement;
-}
-
-void drgui_end_dirty(drgui_element* pElement)
-{
- if (pElement == NULL) {
- return;
- }
-
- drgui_context* pContext = pElement->pContext;
- assert(pContext != NULL);
-
- assert(pContext->dirtyElementCount > 0);
- assert(pContext->dirtyCounter > 0);
-
- pContext->dirtyCounter -= 1;
- if (pContext->dirtyCounter == 0)
- {
- for (size_t i = 0; i < pContext->dirtyElementCount; ++i) {
- drgui_post_outbound_event_dirty_global(pContext->ppDirtyElements[i], pContext->ppDirtyElements[i]->dirtyRect);
- pContext->ppDirtyElements[i]->dirtyRect = drgui_make_inside_out_rect();
- }
-
- pContext->dirtyElementCount = 0;
- }
-}
-
-void drgui_dirty(drgui_element* pElement, drgui_rect relativeRect)
-{
- if (pElement == NULL) {
- return;
- }
-
- //drgui_context* pContext = pElement->pContext;
- //assert(pContext != NULL);
-
- drgui_element* pTopLevelElement = drgui_begin_dirty(pElement);
- if (pTopLevelElement == NULL) {
- return;
- }
-
- pTopLevelElement->dirtyRect = drgui_rect_union(pTopLevelElement->dirtyRect, drgui_make_rect_absolute(pElement, &relativeRect));
- drgui_end_dirty(pElement);
-}
-
-
-bool drgui_draw_iteration_callback(drgui_element* pElement, drgui_rect* pRelativeRect, void* pUserData)
-{
- assert(pElement != NULL);
- assert(pRelativeRect != NULL);
-
- if (pElement->onPaint != NULL)
- {
- // We want to set the initial clipping rectangle before drawing.
- drgui_set_clip(pElement, *pRelativeRect, pUserData);
-
- // We now call the painting function, but only after setting the clipping rectangle.
- pElement->onPaint(pElement, *pRelativeRect, pUserData);
-
- // The on_paint event handler may have adjusted the clipping rectangle so we need to ensure it's restored.
- drgui_set_clip(pElement, *pRelativeRect, pUserData);
- }
-
- return true;
-}
-
-void drgui_draw(drgui_element* pElement, drgui_rect relativeRect, void* pPaintData)
-{
- if (pElement == NULL) {
- return;
- }
-
- drgui_context* pContext = pElement->pContext;
- if (pContext == NULL) {
- return;
- }
-
- assert(pContext->paintingCallbacks.drawBegin != NULL);
- assert(pContext->paintingCallbacks.drawEnd != NULL);
-
- pContext->paintingCallbacks.drawBegin(pPaintData);
- {
- drgui_iterate_visible_elements(pElement, relativeRect, drgui_draw_iteration_callback, pPaintData);
- }
- pContext->paintingCallbacks.drawEnd(pPaintData);
-}
-
-void drgui_get_clip(drgui_element* pElement, drgui_rect* pRelativeRect, void* pPaintData)
-{
- if (pElement == NULL || pElement->pContext == NULL) {
- return;
- }
-
- pElement->pContext->paintingCallbacks.getClip(pRelativeRect, pPaintData);
-
- // The clip returned by the drawing callback will be absolute so we'll need to convert that to relative.
- drgui_make_rect_relative(pElement, pRelativeRect);
-}
-
-void drgui_set_clip(drgui_element* pElement, drgui_rect relativeRect, void* pPaintData)
-{
- if (pElement == NULL || pElement->pContext == NULL) {
- return;
- }
-
-
- // Make sure the rectangle is not negative.
- if (relativeRect.right < relativeRect.left) {
- relativeRect.right = relativeRect.left;
- }
-
- if (relativeRect.bottom < relativeRect.top) {
- relativeRect.bottom = relativeRect.top;
- }
-
- drgui_rect absoluteRect = relativeRect;
- drgui_make_rect_absolute(pElement, &absoluteRect);
-
- pElement->pContext->paintingCallbacks.setClip(absoluteRect, pPaintData);
-}
-
-void drgui_draw_rect(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, void* pPaintData)
-{
- if (pElement == NULL) {
- return;
- }
-
- assert(pElement->pContext != NULL);
-
- drgui_rect absoluteRect = relativeRect;
- drgui_make_rect_absolute(pElement, &absoluteRect);
-
- pElement->pContext->paintingCallbacks.drawRect(absoluteRect, color, pPaintData);
-}
-
-void drgui_draw_rect_outline(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float outlineWidth, void* pPaintData)
-{
- if (pElement == NULL) {
- return;
- }
-
- assert(pElement->pContext != NULL);
-
- drgui_rect absoluteRect = relativeRect;
- drgui_make_rect_absolute(pElement, &absoluteRect);
-
- pElement->pContext->paintingCallbacks.drawRectOutline(absoluteRect, color, outlineWidth, pPaintData);
-}
-
-void drgui_draw_rect_with_outline(drgui_element * pElement, drgui_rect relativeRect, drgui_color color, float outlineWidth, drgui_color outlineColor, void * pPaintData)
-{
- if (pElement == NULL) {
- return;
- }
-
- assert(pElement->pContext != NULL);
-
- drgui_rect absoluteRect = relativeRect;
- drgui_make_rect_absolute(pElement, &absoluteRect);
-
- pElement->pContext->paintingCallbacks.drawRectWithOutline(absoluteRect, color, outlineWidth, outlineColor, pPaintData);
-}
-
-void drgui_draw_round_rect(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float radius, void* pPaintData)
-{
- if (pElement == NULL) {
- return;
- }
-
- assert(pElement->pContext != NULL);
-
- drgui_rect absoluteRect = relativeRect;
- drgui_make_rect_absolute(pElement, &absoluteRect);
-
- pElement->pContext->paintingCallbacks.drawRoundRect(absoluteRect, color, radius, pPaintData);
-}
-
-void drgui_draw_round_rect_outline(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float radius, float outlineWidth, void* pPaintData)
-{
- if (pElement == NULL) {
- return;
- }
-
- assert(pElement->pContext != NULL);
-
- drgui_rect absoluteRect = relativeRect;
- drgui_make_rect_absolute(pElement, &absoluteRect);
-
- pElement->pContext->paintingCallbacks.drawRoundRectOutline(absoluteRect, color, radius, outlineWidth, pPaintData);
-}
-
-void drgui_draw_round_rect_with_outline(drgui_element* pElement, drgui_rect relativeRect, drgui_color color, float radius, float outlineWidth, drgui_color outlineColor, void* pPaintData)
-{
- if (pElement == NULL) {
- return;
- }
-
- assert(pElement->pContext != NULL);
-
- drgui_rect absoluteRect = relativeRect;
- drgui_make_rect_absolute(pElement, &absoluteRect);
-
- pElement->pContext->paintingCallbacks.drawRoundRectWithOutline(absoluteRect, color, radius, outlineWidth, outlineColor, pPaintData);
-}
-
-void drgui_draw_text(drgui_element* pElement, drgui_font* pFont, const char* text, int textLengthInBytes, float posX, float posY, drgui_color color, drgui_color backgroundColor, void* pPaintData)
-{
- if (pElement == NULL || pFont == NULL) {
- return;
- }
-
- assert(pElement->pContext != NULL);
-
- float absolutePosX = posX;
- float absolutePosY = posY;
- drgui_make_point_absolute(pElement, &absolutePosX, &absolutePosY);
-
- pElement->pContext->paintingCallbacks.drawText(pFont->internalFont, text, textLengthInBytes, absolutePosX, absolutePosY, color, backgroundColor, pPaintData);
-}
-
-void drgui_draw_image(drgui_element* pElement, drgui_image* pImage, drgui_draw_image_args* pArgs, void* pPaintData)
-{
- if (pElement == NULL || pImage == NULL || pArgs == NULL) {
- return;
- }
-
- assert(pElement->pContext != NULL);
-
- drgui_make_point_absolute(pElement, &pArgs->dstX, &pArgs->dstY);
- drgui_make_point_absolute(pElement, &pArgs->dstBoundsX, &pArgs->dstBoundsY);
-
- if ((pArgs->options & DRGUI_IMAGE_ALIGN_CENTER) != 0)
- {
- pArgs->dstX = pArgs->dstBoundsX + (pArgs->dstBoundsWidth - pArgs->dstWidth) / 2;
- pArgs->dstY = pArgs->dstBoundsY + (pArgs->dstBoundsHeight - pArgs->dstHeight) / 2;
- }
-
- drgui_rect prevClip;
- pElement->pContext->paintingCallbacks.getClip(&prevClip, pPaintData);
-
- bool restoreClip = false;
- if ((pArgs->options & DRGUI_IMAGE_CLIP_BOUNDS) != 0)
- {
- // We only need to clip if part of the destination rectangle falls outside of the bounds.
- if (pArgs->dstX < pArgs->dstBoundsX || pArgs->dstX + pArgs->dstWidth > pArgs->dstBoundsX + pArgs->dstBoundsWidth ||
- pArgs->dstY < pArgs->dstBoundsY || pArgs->dstY + pArgs->dstHeight > pArgs->dstBoundsY + pArgs->dstBoundsHeight)
- {
- restoreClip = true;
- pElement->pContext->paintingCallbacks.setClip(drgui_make_rect(pArgs->dstBoundsX, pArgs->dstBoundsY, pArgs->dstBoundsX + pArgs->dstBoundsWidth, pArgs->dstBoundsY + pArgs->dstBoundsHeight), pPaintData);
- }
- }
-
- if ((pArgs->options & DRGUI_IMAGE_DRAW_BOUNDS) != 0)
- {
- // The bounds is the area sitting around the outside of the destination rectangle.
- const float boundsLeft = pArgs->dstBoundsX;
- const float boundsTop = pArgs->dstBoundsY;
- const float boundsRight = boundsLeft + pArgs->dstBoundsWidth;
- const float boundsBottom = boundsTop + pArgs->dstBoundsHeight;
-
- const float imageLeft = pArgs->dstX;
- const float imageTop = pArgs->dstY;
- const float imageRight = imageLeft + pArgs->dstWidth;
- const float imageBottom = imageTop + pArgs->dstHeight;
-
- // Left.
- if (boundsLeft < imageLeft) {
- pElement->pContext->paintingCallbacks.drawRect(drgui_make_rect(boundsLeft, boundsTop, imageLeft, boundsBottom), pArgs->boundsColor, pPaintData);
- }
-
- // Right.
- if (boundsRight > imageRight) {
- pElement->pContext->paintingCallbacks.drawRect(drgui_make_rect(imageRight, boundsTop, boundsRight, boundsBottom), pArgs->boundsColor, pPaintData);
- }
-
- // Top.
- if (boundsTop < imageTop) {
- pElement->pContext->paintingCallbacks.drawRect(drgui_make_rect(imageLeft, boundsTop, imageRight, imageTop), pArgs->boundsColor, pPaintData);
- }
-
- // Bottom.
- if (boundsBottom > imageBottom) {
- pElement->pContext->paintingCallbacks.drawRect(drgui_make_rect(imageLeft, imageBottom, imageRight, boundsBottom), pArgs->boundsColor, pPaintData);
- }
- }
-
- pElement->pContext->paintingCallbacks.drawImage(pImage->hResource, pArgs, pPaintData);
-
- if (restoreClip) {
- pElement->pContext->paintingCallbacks.setClip(prevClip, pPaintData);
- }
-}
-
-
-drgui_font* drgui_create_font(drgui_context* pContext, const char* family, unsigned int size, drgui_font_weight weight, drgui_font_slant slant, float rotation, unsigned int flags)
-{
- if (pContext == NULL) {
- return NULL;
- }
-
- if (pContext->paintingCallbacks.createFont == NULL) {
- return NULL;
- }
-
-
- drgui_resource internalFont = pContext->paintingCallbacks.createFont(pContext->pPaintingContext, family, size, weight, slant, rotation, flags);
- if (internalFont == NULL) {
- return NULL;
- }
-
- drgui_font* pFont = (drgui_font*)malloc(sizeof(drgui_font));
- if (pFont == NULL) {
- return NULL;
- }
-
- pFont->pContext = pContext;
- pFont->family[0] = '\0';
- pFont->size = size;
- pFont->weight = weight;
- pFont->slant = slant;
- pFont->rotation = rotation;
- pFont->flags = flags;
- pFont->internalFont = internalFont;
-
- if (family != NULL) {
- drgui__strcpy_s(pFont->family, sizeof(pFont->family), family);
- }
-
- return pFont;
-}
-
-void drgui_delete_font(drgui_font* pFont)
-{
- if (pFont == NULL) {
- return;
- }
-
- assert(pFont->pContext != NULL);
-
- // Delete the internal font objects first.
- if (pFont->pContext->paintingCallbacks.deleteFont) {
- pFont->pContext->paintingCallbacks.deleteFont(pFont->internalFont);
- }
-
- free(pFont);
-}
-
-bool drgui_get_font_metrics(drgui_font* pFont, drgui_font_metrics* pMetricsOut)
-{
- if (pFont == NULL || pMetricsOut == NULL) {
- return false;
- }
-
- assert(pFont->pContext != NULL);
-
- if (pFont->pContext->paintingCallbacks.getFontMetrics == NULL) {
- return false;
- }
-
- return pFont->pContext->paintingCallbacks.getFontMetrics(pFont->internalFont, pMetricsOut);
-}
-
-bool drgui_get_glyph_metrics(drgui_font* pFont, unsigned int utf32, drgui_glyph_metrics* pMetricsOut)
-{
- if (pFont == NULL || pMetricsOut == NULL) {
- return false;
- }
-
- assert(pFont->pContext != NULL);
-
- if (pFont->pContext->paintingCallbacks.getGlyphMetrics == NULL) {
- return false;
- }
-
- return pFont->pContext->paintingCallbacks.getGlyphMetrics(pFont->internalFont, utf32, pMetricsOut);
-}
-
-bool drgui_measure_string(drgui_font* pFont, const char* text, size_t textLengthInBytes, float* pWidthOut, float* pHeightOut)
-{
- if (pFont == NULL) {
- return false;
- }
-
- if (text == NULL || textLengthInBytes == 0)
- {
- drgui_font_metrics metrics;
- if (!drgui_get_font_metrics(pFont, &metrics)) {
- return false;
- }
-
- if (pWidthOut) {
- *pWidthOut = 0;
- }
- if (pHeightOut) {
- *pHeightOut = (float)metrics.lineHeight;
- }
-
- return true;
- }
-
-
-
- assert(pFont->pContext != NULL);
-
- if (pFont->pContext->paintingCallbacks.measureString == NULL) {
- return false;
- }
-
- return pFont->pContext->paintingCallbacks.measureString(pFont->internalFont, text, textLengthInBytes, pWidthOut, pHeightOut);
-}
-
-bool drgui_get_text_cursor_position_from_point(drgui_font* pFont, const char* text, size_t textSizeInBytes, float maxWidth, float inputPosX, float* pTextCursorPosXOut, size_t* pCharacterIndexOut)
-{
- if (pFont == NULL) {
- return false;
- }
-
- assert(pFont->pContext != NULL);
-
- if (pFont->pContext->paintingCallbacks.getTextCursorPositionFromPoint) {
- return pFont->pContext->paintingCallbacks.getTextCursorPositionFromPoint(pFont->internalFont, text, textSizeInBytes, maxWidth, inputPosX, pTextCursorPosXOut, pCharacterIndexOut);
- }
-
- return false;
-}
-
-bool drgui_get_text_cursor_position_from_char(drgui_font* pFont, const char* text, size_t characterIndex, float* pTextCursorPosXOut)
-{
- if (pFont == NULL) {
- return false;
- }
-
- assert(pFont->pContext != NULL);
-
- if (pFont->pContext->paintingCallbacks.getTextCursorPositionFromChar) {
- return pFont->pContext->paintingCallbacks.getTextCursorPositionFromChar(pFont->internalFont, text, characterIndex, pTextCursorPosXOut);
- }
-
- return false;
-}
-
-
-
-drgui_image* drgui_create_image(drgui_context* pContext, unsigned int width, unsigned int height, drgui_image_format format, unsigned int stride, const void* pData)
-{
- if (pContext == NULL) {
- return NULL;
- }
-
- if (pContext->paintingCallbacks.createImage == NULL) {
- return NULL;
- }
-
-
- // If the stride is 0, assume tightly packed.
- if (stride == 0) {
- stride = width * 4;
- }
-
-
- drgui_resource internalImage = pContext->paintingCallbacks.createImage(pContext->pPaintingContext, width, height, format, stride, pData);
- if (internalImage == NULL) {
- return NULL;
- }
-
- drgui_image* pImage = (drgui_image*)malloc(sizeof(*pImage));
- if (pImage == NULL) {
- return NULL;
- }
-
- pImage->pContext = pContext;
- pImage->hResource = internalImage;
-
-
- return pImage;
-}
-
-void drgui_delete_image(drgui_image* pImage)
-{
- if (pImage == NULL) {
- return;
- }
-
- assert(pImage->pContext != NULL);
-
- // Delete the internal font object.
- if (pImage->pContext->paintingCallbacks.deleteImage) {
- pImage->pContext->paintingCallbacks.deleteImage(pImage->hResource);
- }
-
- // Free the font object last.
- free(pImage);
-}
-
-void drgui_get_image_size(drgui_image* pImage, unsigned int* pWidthOut, unsigned int* pHeightOut)
-{
- if (pWidthOut) *pWidthOut = 0;
- if (pHeightOut) *pHeightOut = 0;
-
- if (pImage == NULL) {
- return;
- }
-
- assert(pImage->pContext != NULL);
-
- if (pImage->pContext->paintingCallbacks.getImageSize == NULL) {
- return;
- }
-
- pImage->pContext->paintingCallbacks.getImageSize(pImage->hResource, pWidthOut, pHeightOut);
-}
-
-drgui_image_format drgui_get_optimal_image_format(drgui_context* pContext)
-{
- if (pContext == NULL || pContext->paintingCallbacks.getOptimalImageFormat == NULL) {
- return drgui_image_format_rgba8;
- }
-
- return pContext->paintingCallbacks.getOptimalImageFormat(pContext->pPaintingContext);
-}
-
-void* drgui_map_image_data(drgui_image* pImage, unsigned int accessFlags)
-{
- if (pImage == NULL) {
- return NULL;
- }
-
- if (pImage->pContext->paintingCallbacks.mapImageData == NULL || pImage->pContext->paintingCallbacks.unmapImageData == NULL) {
- return NULL;
- }
-
- return pImage->pContext->paintingCallbacks.mapImageData(pImage->hResource, accessFlags);
-}
-
-void drgui_unmap_image_data(drgui_image* pImage)
-{
- if (pImage == NULL) {
- return;
- }
-
- if (pImage->pContext->paintingCallbacks.unmapImageData == NULL) {
- return;
- }
-
- pImage->pContext->paintingCallbacks.unmapImageData(pImage->hResource);
-}
-
-
-
-/////////////////////////////////////////////////////////////////
-//
-// HIGH-LEVEL API
-//
-/////////////////////////////////////////////////////////////////
-
-//// Hit Testing and Layout ////
-
-void drgui_on_size_fit_children_to_parent(drgui_element* pElement, float newWidth, float newHeight)
-{
- for (drgui_element* pChild = pElement->pFirstChild; pChild != NULL; pChild = pChild->pNextSibling) {
- drgui_set_size(pChild, newWidth, newHeight);
- }
-}
-
-bool drgui_pass_through_hit_test(drgui_element* pElement, float mousePosX, float mousePosY)
-{
- (void)pElement;
- (void)mousePosX;
- (void)mousePosY;
-
- return false;
-}
-
-
-
-//// Painting ////
-
-void drgui_draw_border(drgui_element* pElement, float borderWidth, drgui_color color, void* pUserData)
-{
- drgui_draw_rect_outline(pElement, drgui_get_local_rect(pElement), color, borderWidth, pUserData);
-}
-
-
-
-/////////////////////////////////////////////////////////////////
-//
-// UTILITY API
-//
-/////////////////////////////////////////////////////////////////
-
-drgui_color drgui_rgba(drgui_byte r, drgui_byte g, drgui_byte b, drgui_byte a)
-{
- drgui_color color;
- color.r = r;
- color.g = g;
- color.b = b;
- color.a = a;
-
- return color;
-}
-
-drgui_color drgui_rgb(drgui_byte r, drgui_byte g, drgui_byte b)
-{
- drgui_color color;
- color.r = r;
- color.g = g;
- color.b = b;
- color.a = 255;
-
- return color;
-}
-
-drgui_rect drgui_clamp_rect(drgui_rect rect, drgui_rect other)
-{
- drgui_rect result;
- result.left = (rect.left >= other.left) ? rect.left : other.left;
- result.top = (rect.top >= other.top) ? rect.top : other.top;
- result.right = (rect.right <= other.right) ? rect.right : other.right;
- result.bottom = (rect.bottom <= other.bottom) ? rect.bottom : other.bottom;
-
- return result;
-}
-
-bool drgui_clamp_rect_to_element(const drgui_element* pElement, drgui_rect* pRelativeRect)
-{
- if (pElement == NULL || pRelativeRect == NULL) {
- return false;
- }
-
-
- if (pRelativeRect->left < 0) {
- pRelativeRect->left = 0;
- }
- if (pRelativeRect->top < 0) {
- pRelativeRect->top = 0;
- }
-
- if (pRelativeRect->right > pElement->width) {
- pRelativeRect->right = pElement->width;
- }
- if (pRelativeRect->bottom > pElement->height) {
- pRelativeRect->bottom = pElement->height;
- }
-
-
- return (pRelativeRect->right - pRelativeRect->left > 0) && (pRelativeRect->bottom - pRelativeRect->top > 0);
-}
-
-drgui_rect drgui_make_rect_relative(const drgui_element* pElement, drgui_rect* pRect)
-{
- if (pElement == NULL || pRect == NULL) {
- return drgui_make_rect(0, 0, 0, 0);
- }
-
- pRect->left -= pElement->absolutePosX;
- pRect->top -= pElement->absolutePosY;
- pRect->right -= pElement->absolutePosX;
- pRect->bottom -= pElement->absolutePosY;
-
- return *pRect;
-}
-
-drgui_rect drgui_make_rect_absolute(const drgui_element * pElement, drgui_rect * pRect)
-{
- if (pElement == NULL || pRect == NULL) {
- return drgui_make_rect(0, 0, 0, 0);
- }
-
- pRect->left += pElement->absolutePosX;
- pRect->top += pElement->absolutePosY;
- pRect->right += pElement->absolutePosX;
- pRect->bottom += pElement->absolutePosY;
-
- return *pRect;
-}
-
-void drgui_make_point_relative(const drgui_element* pElement, float* positionX, float* positionY)
-{
- if (pElement != NULL)
- {
- if (positionX != NULL) {
- *positionX -= pElement->absolutePosX;
- }
-
- if (positionY != NULL) {
- *positionY -= pElement->absolutePosY;
- }
- }
-}
-
-void drgui_make_point_absolute(const drgui_element* pElement, float* positionX, float* positionY)
-{
- if (pElement != NULL)
- {
- if (positionX != NULL) {
- *positionX += pElement->absolutePosX;
- }
-
- if (positionY != NULL) {
- *positionY += pElement->absolutePosY;
- }
- }
-}
-
-drgui_rect drgui_make_rect(float left, float top, float right, float bottom)
-{
- drgui_rect rect;
- rect.left = left;
- rect.top = top;
- rect.right = right;
- rect.bottom = bottom;
-
- return rect;
-}
-
-drgui_rect drgui_make_inside_out_rect()
-{
- drgui_rect rect;
- rect.left = FLT_MAX;
- rect.top = FLT_MAX;
- rect.right = -FLT_MAX;
- rect.bottom = -FLT_MAX;
-
- return rect;
-}
-
-drgui_rect drgui_grow_rect(drgui_rect rect, float amount)
-{
- drgui_rect result = rect;
- result.left -= amount;
- result.top -= amount;
- result.right += amount;
- result.bottom += amount;
-
- return result;
-}
-
-drgui_rect drgui_scale_rect(drgui_rect rect, float scaleX, float scaleY)
-{
- drgui_rect result = rect;
- result.left *= scaleX;
- result.top *= scaleY;
- result.right *= scaleX;
- result.bottom *= scaleY;
-
- return result;
-}
-
-drgui_rect drgui_offset_rect(drgui_rect rect, float offsetX, float offsetY)
-{
- return drgui_make_rect(rect.left + offsetX, rect.top + offsetY, rect.right + offsetX, rect.bottom + offsetY);
-}
-
-drgui_rect drgui_rect_union(drgui_rect rect0, drgui_rect rect1)
-{
- drgui_rect result;
- result.left = (rect0.left < rect1.left) ? rect0.left : rect1.left;
- result.top = (rect0.top < rect1.top) ? rect0.top : rect1.top;
- result.right = (rect0.right > rect1.right) ? rect0.right : rect1.right;
- result.bottom = (rect0.bottom > rect1.bottom) ? rect0.bottom : rect1.bottom;
-
- return result;
-}
-
-bool drgui_rect_contains_point(drgui_rect rect, float posX, float posY)
-{
- if (posX < rect.left || posY < rect.top) {
- return false;
- }
-
- if (posX >= rect.right || posY >= rect.bottom) {
- return false;
- }
-
- return true;
-}
-
-bool drgui_rect_equal(drgui_rect rect0, drgui_rect rect1)
-{
- return
- rect0.left == rect1.left &&
- rect0.top == rect1.top &&
- rect0.right == rect1.right &&
- rect0.bottom == rect1.bottom;
-}
-
-bool drgui_rect_has_volume(drgui_rect rect)
-{
- return rect.right > rect.left && rect.bottom > rect.top;
-}
-
-
-
-
-/////////////////////////////////////////////////////////////////
-//
-// EASY_DRAW-SPECIFIC API
-//
-/////////////////////////////////////////////////////////////////
-#ifndef DRGUI_NO_DR_2D
-
-void drgui_draw_begin_dr_2d(void* pPaintData);
-void drgui_draw_end_dr_2d(void* pPaintData);
-void drgui_set_clip_dr_2d(drgui_rect rect, void* pPaintData);
-void drgui_get_clip_dr_2d(drgui_rect* pRectOut, void* pPaintData);
-void drgui_draw_rect_dr_2d(drgui_rect rect, drgui_color color, void* pPaintData);
-void drgui_draw_rect_outline_dr_2d(drgui_rect, drgui_color, float, void*);
-void drgui_draw_rect_with_outline_dr_2d(drgui_rect, drgui_color, float, drgui_color, void*);
-void drgui_draw_round_rect_dr_2d(drgui_rect, drgui_color, float, void*);
-void drgui_draw_round_rect_outline_dr_2d(drgui_rect, drgui_color, float, float, void*);
-void drgui_draw_round_rect_with_outline_dr_2d(drgui_rect, drgui_color, float, float, drgui_color, void*);
-void drgui_draw_text_dr_2d(drgui_resource, const char*, int, float, float, drgui_color, drgui_color, void*);
-void drgui_draw_image_dr_2d(drgui_resource image, drgui_draw_image_args* pArgs, void* pPaintData);
-
-drgui_resource drgui_create_font_dr_2d(void*, const char*, unsigned int, drgui_font_weight, drgui_font_slant, float, unsigned int flags);
-void drgui_delete_font_dr_2d(drgui_resource);
-unsigned int drgui_get_font_size_dr_2d(drgui_resource hFont);
-bool drgui_get_font_metrics_dr_2d(drgui_resource, drgui_font_metrics*);
-bool drgui_get_glyph_metrics_dr_2d(drgui_resource, unsigned int, drgui_glyph_metrics*);
-bool drgui_measure_string_dr_2d(drgui_resource, const char*, size_t, float*, float*);
-bool drgui_get_text_cursor_position_from_point_dr_2d(drgui_resource font, const char* text, size_t textSizeInBytes, float maxWidth, float inputPosX, float* pTextCursorPosXOut, size_t* pCharacterIndexOut);
-bool drgui_get_text_cursor_position_from_char_dr_2d(drgui_resource font, const char* text, size_t characterIndex, float* pTextCursorPosXOut);
-
-drgui_resource drgui_create_image_dr_2d(void* pPaintingContext, unsigned int width, unsigned int height, drgui_image_format format, unsigned int stride, const void* pImageData);
-void drgui_delete_image_dr_2d(drgui_resource image);
-void drgui_get_image_size_dr_2d(drgui_resource image, unsigned int* pWidthOut, unsigned int* pHeightOut);
-drgui_image_format drgui_get_optimal_image_format_dr_2d(void* pPaintingContext);
-void* drgui_map_image_data_dr_2d(drgui_resource image, unsigned int accessFlags);
-void drgui_unmap_image_data_dr_2d(drgui_resource image);
-
-drgui_context* drgui_create_context_dr_2d(dr2d_context* pDrawingContext)
-{
- drgui_context* pContext = drgui_create_context();
- if (pContext != NULL) {
- drgui_register_dr_2d_callbacks(pContext, pDrawingContext);
- }
-
- return pContext;
-}
-
-void drgui_register_dr_2d_callbacks(drgui_context* pContext, dr2d_context* pDrawingContext)
-{
- drgui_painting_callbacks callbacks;
- callbacks.drawBegin = drgui_draw_begin_dr_2d;
- callbacks.drawEnd = drgui_draw_end_dr_2d;
- callbacks.setClip = drgui_set_clip_dr_2d;
- callbacks.getClip = drgui_get_clip_dr_2d;
- callbacks.drawRect = drgui_draw_rect_dr_2d;
- callbacks.drawRectOutline = drgui_draw_rect_outline_dr_2d;
- callbacks.drawRectWithOutline = drgui_draw_rect_with_outline_dr_2d;
- callbacks.drawRoundRect = drgui_draw_round_rect_dr_2d;
- callbacks.drawRoundRectOutline = drgui_draw_round_rect_outline_dr_2d;
- callbacks.drawRoundRectWithOutline = drgui_draw_round_rect_with_outline_dr_2d;
- callbacks.drawText = drgui_draw_text_dr_2d;
- callbacks.drawImage = drgui_draw_image_dr_2d;
-
- callbacks.createFont = drgui_create_font_dr_2d;
- callbacks.deleteFont = drgui_delete_font_dr_2d;
- callbacks.getFontSize = drgui_get_font_size_dr_2d;
- callbacks.getFontMetrics = drgui_get_font_metrics_dr_2d;
- callbacks.getGlyphMetrics = drgui_get_glyph_metrics_dr_2d;
- callbacks.measureString = drgui_measure_string_dr_2d;
-
- callbacks.createImage = drgui_create_image_dr_2d;
- callbacks.deleteImage = drgui_delete_image_dr_2d;
- callbacks.getImageSize = drgui_get_image_size_dr_2d;
- callbacks.getOptimalImageFormat = drgui_get_optimal_image_format_dr_2d;
- callbacks.mapImageData = drgui_map_image_data_dr_2d;
- callbacks.unmapImageData = drgui_unmap_image_data_dr_2d;
-
- callbacks.getTextCursorPositionFromPoint = drgui_get_text_cursor_position_from_point_dr_2d;
- callbacks.getTextCursorPositionFromChar = drgui_get_text_cursor_position_from_char_dr_2d;
-
- drgui_register_painting_callbacks(pContext, pDrawingContext, callbacks);
-}
-
-
-void drgui_draw_begin_dr_2d(void* pPaintData)
-{
- dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
- assert(pSurface != NULL);
-
- dr2d_begin_draw(pSurface);
-}
-
-void drgui_draw_end_dr_2d(void* pPaintData)
-{
- dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
- assert(pSurface != NULL);
-
- dr2d_end_draw(pSurface);
-}
-
-void drgui_set_clip_dr_2d(drgui_rect rect, void* pPaintData)
-{
- dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
- assert(pSurface != NULL);
-
- dr2d_set_clip(pSurface, rect.left, rect.top, rect.right, rect.bottom);
-}
-
-void drgui_get_clip_dr_2d(drgui_rect* pRectOut, void* pPaintData)
-{
- assert(pRectOut != NULL);
-
- dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
- assert(pSurface != NULL);
-
- dr2d_get_clip(pSurface, &pRectOut->left, &pRectOut->top, &pRectOut->right, &pRectOut->bottom);
-}
-
-void drgui_draw_rect_dr_2d(drgui_rect rect, drgui_color color, void* pPaintData)
-{
- dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
- assert(pSurface != NULL);
-
- dr2d_draw_rect(pSurface, rect.left, rect.top, rect.right, rect.bottom, dr2d_rgba(color.r, color.g, color.b, color.a));
-}
-
-void drgui_draw_rect_outline_dr_2d(drgui_rect rect, drgui_color color, float outlineWidth, void* pPaintData)
-{
- dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
- assert(pSurface != NULL);
-
- dr2d_draw_rect_outline(pSurface, rect.left, rect.top, rect.right, rect.bottom, dr2d_rgba(color.r, color.g, color.b, color.a), outlineWidth);
-}
-
-void drgui_draw_rect_with_outline_dr_2d(drgui_rect rect, drgui_color color, float outlineWidth, drgui_color outlineColor, void* pPaintData)
-{
- dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
- assert(pSurface != NULL);
-
- dr2d_draw_rect_with_outline(pSurface, rect.left, rect.top, rect.right, rect.bottom, dr2d_rgba(color.r, color.g, color.b, color.a), outlineWidth, dr2d_rgba(outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a));
-}
-
-void drgui_draw_round_rect_dr_2d(drgui_rect rect, drgui_color color, float radius, void* pPaintData)
-{
- dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
- assert(pSurface != NULL);
-
- dr2d_draw_round_rect(pSurface, rect.left, rect.top, rect.right, rect.bottom, dr2d_rgba(color.r, color.g, color.b, color.a), radius);
-}
-
-void drgui_draw_round_rect_outline_dr_2d(drgui_rect rect, drgui_color color, float radius, float outlineWidth, void* pPaintData)
-{
- dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
- assert(pSurface != NULL);
-
- dr2d_draw_round_rect_outline(pSurface, rect.left, rect.top, rect.right, rect.bottom, dr2d_rgba(color.r, color.g, color.b, color.a), radius, outlineWidth);
-}
-
-void drgui_draw_round_rect_with_outline_dr_2d(drgui_rect rect, drgui_color color, float radius, float outlineWidth, drgui_color outlineColor, void* pPaintData)
-{
- dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
- assert(pSurface != NULL);
-
- dr2d_draw_round_rect_with_outline(pSurface, rect.left, rect.top, rect.right, rect.bottom, dr2d_rgba(color.r, color.g, color.b, color.a), radius, outlineWidth, dr2d_rgba(outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a));
-}
-
-void drgui_draw_text_dr_2d(drgui_resource font, const char* text, int textSizeInBytes, float posX, float posY, drgui_color color, drgui_color backgroundColor, void* pPaintData)
-{
- dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
- assert(pSurface != NULL);
-
- dr2d_draw_text(pSurface, (dr2d_font*)font, text, textSizeInBytes, posX, posY, dr2d_rgba(color.r, color.g, color.b, color.a), dr2d_rgba(backgroundColor.r, backgroundColor.g, backgroundColor.b, backgroundColor.a));
-}
-
-void drgui_draw_image_dr_2d(drgui_resource image, drgui_draw_image_args* pArgs, void* pPaintData)
-{
- dr2d_surface* pSurface = (dr2d_surface*)pPaintData;
- assert(pSurface != NULL);
-
- dr2d_draw_image_args args;
- args.dstX = pArgs->dstX;
- args.dstY = pArgs->dstY;
- args.dstWidth = pArgs->dstWidth;
- args.dstHeight = pArgs->dstHeight;
- args.srcX = pArgs->srcX;
- args.srcY = pArgs->srcY;
- args.srcWidth = pArgs->srcWidth;
- args.srcHeight = pArgs->srcHeight;
- args.foregroundTint = dr2d_rgba(pArgs->foregroundTint.r, pArgs->foregroundTint.g, pArgs->foregroundTint.b, pArgs->foregroundTint.a);
- args.backgroundColor = dr2d_rgba(pArgs->backgroundColor.r, pArgs->backgroundColor.g, pArgs->backgroundColor.b, pArgs->backgroundColor.a);
- args.options = pArgs->options;
- dr2d_draw_image(pSurface, (dr2d_image*)image, &args);
-}
-
-
-drgui_resource drgui_create_font_dr_2d(void* pPaintingContext, const char* family, unsigned int size, drgui_font_weight weight, drgui_font_slant slant, float rotation, unsigned int flags)
-{
- return dr2d_create_font((dr2d_context*)pPaintingContext, family, size, (dr2d_font_weight)weight, (dr2d_font_slant)slant, rotation, flags);
-}
-
-void drgui_delete_font_dr_2d(drgui_resource font)
-{
- dr2d_delete_font((dr2d_font*)font);
-}
-
-unsigned int drgui_get_font_size_dr_2d(drgui_resource font)
-{
- return dr2d_get_font_size((dr2d_font*)font);
-}
-
-bool drgui_get_font_metrics_dr_2d(drgui_resource font, drgui_font_metrics* pMetricsOut)
-{
- assert(pMetricsOut != NULL);
-
- dr2d_font_metrics metrics;
- if (!dr2d_get_font_metrics((dr2d_font*)font, &metrics)) {
- return false;
- }
-
- pMetricsOut->ascent = metrics.ascent;
- pMetricsOut->descent = metrics.descent;
- pMetricsOut->lineHeight = metrics.lineHeight;
- pMetricsOut->spaceWidth = metrics.spaceWidth;
-
- return true;
-}
-
-bool drgui_get_glyph_metrics_dr_2d(drgui_resource font, unsigned int utf32, drgui_glyph_metrics* pMetricsOut)
-{
- assert(pMetricsOut != NULL);
-
- dr2d_glyph_metrics metrics;
- if (!dr2d_get_glyph_metrics((dr2d_font*)font, utf32, &metrics)) {
- return false;
- }
-
- pMetricsOut->width = metrics.width;
- pMetricsOut->height = metrics.height;
- pMetricsOut->originX = metrics.originX;
- pMetricsOut->originY = metrics.originY;
- pMetricsOut->advanceX = metrics.advanceX;
- pMetricsOut->advanceY = metrics.advanceY;
-
- return true;
-}
-
-bool drgui_measure_string_dr_2d(drgui_resource font, const char* text, size_t textSizeInBytes, float* pWidthOut, float* pHeightOut)
-{
- return dr2d_measure_string((dr2d_font*)font, text, textSizeInBytes, pWidthOut, pHeightOut);
-}
-
-bool drgui_get_text_cursor_position_from_point_dr_2d(drgui_resource font, const char* text, size_t textSizeInBytes, float maxWidth, float inputPosX, float* pTextCursorPosXOut, size_t* pCharacterIndexOut)
-{
- return dr2d_get_text_cursor_position_from_point((dr2d_font*)font, text, textSizeInBytes, maxWidth, inputPosX, pTextCursorPosXOut, pCharacterIndexOut);
-}
-
-bool drgui_get_text_cursor_position_from_char_dr_2d(drgui_resource font, const char* text, size_t characterIndex, float* pTextCursorPosXOut)
-{
- return dr2d_get_text_cursor_position_from_char((dr2d_font*)font, text, characterIndex, pTextCursorPosXOut);
-}
-
-
-drgui_resource drgui_create_image_dr_2d(void* pPaintingContext, unsigned int width, unsigned int height, drgui_image_format format, unsigned int stride, const void* pImageData)
-{
- dr2d_image_format dr2dFormat;
- switch (format)
- {
- case drgui_image_format_bgra8: dr2dFormat = dr2d_image_format_bgra8; break;
- case drgui_image_format_argb8: dr2dFormat = dr2d_image_format_argb8; break;
- default: dr2dFormat = dr2d_image_format_rgba8;
- }
-
- return dr2d_create_image((dr2d_context*)pPaintingContext, width, height, dr2dFormat, stride, pImageData);
-}
-
-void drgui_delete_image_dr_2d(drgui_resource image)
-{
- dr2d_delete_image((dr2d_image*)image);
-}
-
-void drgui_get_image_size_dr_2d(drgui_resource image, unsigned int* pWidthOut, unsigned int* pHeightOut)
-{
- dr2d_get_image_size((dr2d_image*)image, pWidthOut, pHeightOut);
-}
-
-drgui_image_format drgui_get_optimal_image_format_dr_2d(void* pPaintingContext)
-{
- return (drgui_image_format)dr2d_get_optimal_image_format((dr2d_context*)pPaintingContext);
-}
-
-void* drgui_map_image_data_dr_2d(drgui_resource image, unsigned int accessFlags)
-{
- return dr2d_map_image_data((dr2d_image*)image, accessFlags);
-}
-
-void drgui_unmap_image_data_dr_2d(drgui_resource image)
-{
- dr2d_unmap_image_data((dr2d_image*)image);
-}
-
-#endif //DRGUI_NO_DR_2D
-#endif //DR_GUI_IMPLEMENTATION
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-//
-// Text Engine
-//
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-#ifndef DRGUI_NO_TEXT_EDITING
-// QUICK NOTES
-//
-// - Text engines are used to make it easier to manage the layout of a block of text.
-// - Text engines support basic editing which requires inbound events to be posted from the higher level application.
-// - Text engines are not GUI elements. They are lower level objects that are used by higher level GUI elements.
-// - Text engines normalize line endings to \n format. Keep this in mind when retrieving the text of a layout.
-// - Text engine use the notion of a container which is used for determining which text runs are visible.
-
-#ifndef drgui_text_engine_h
-#define drgui_text_engine_h
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct drgui_text_engine drgui_text_engine;
-
-typedef enum
-{
- drgui_text_engine_alignment_left,
- drgui_text_engine_alignment_top,
- drgui_text_engine_alignment_center,
- drgui_text_engine_alignment_right,
- drgui_text_engine_alignment_bottom,
-} drgui_text_engine_alignment;
-
-typedef struct
-{
- /// A pointer to the start of the string. This is NOT null terminated.
- const char* text;
-
- /// The length of the string, in bytes.
- size_t textLength;
-
-
- /// The font.
- drgui_font* pFont;
-
- /// The foreground color of the text.
- drgui_color textColor;
-
- /// The backgorund color of the text.
- drgui_color backgroundColor;
-
-
- /// The position to draw the text on the x axis.
- float posX;
-
- /// The position to draw the text on the y axis.
- float posY;
-
- /// The width of the run.
- float width;
-
- /// The height of the run.
- float height;
-
-
- // PROPERTIES BELOW ARE FOR INTERNAL USE ONLY
-
- /// Index of the line the run is placed on. For runs that are new line characters, this will represent the number of lines that came before it. For
- /// example, if this run represents the new-line character for the first line, this will be 0 and so on.
- size_t iLine;
-
- /// Index in the main text string of the first character of the run.
- size_t iChar;
-
- /// Index in the main text string of the character just past the last character in the run.
- size_t iCharEnd;
-
-} drgui_text_run;
-
-typedef void (* drgui_text_engine_on_paint_text_proc) (drgui_text_engine* pTL, drgui_text_run* pRun, drgui_element* pElement, void* pPaintData);
-typedef void (* drgui_text_engine_on_paint_rect_proc) (drgui_text_engine* pTL, drgui_rect rect, drgui_color color, drgui_element* pElement, void* pPaintData);
-typedef void (* drgui_text_engine_on_cursor_move_proc) (drgui_text_engine* pTL);
-typedef void (* drgui_text_engine_on_dirty_proc) (drgui_text_engine* pTL, drgui_rect rect);
-typedef void (* drgui_text_engine_on_text_changed_proc) (drgui_text_engine* pTL);
-typedef void (* drgui_text_engine_on_undo_point_changed_proc)(drgui_text_engine* pTL, unsigned int iUndoPoint);
-
-
-/// Creates a new text engine object.
-drgui_text_engine* drgui_create_text_engine(drgui_context* pContext, size_t extraDataSize, void* pExtraData);
-
-/// Deletes the given text engine.
-void drgui_delete_text_engine(drgui_text_engine* pTL);
-
-
-/// Retrieves the size of the extra data associated with the given text engine.
-size_t drgui_text_engine_get_extra_data_size(drgui_text_engine* pTL);
-
-/// Retrieves a pointer to the extra data associated with the given text engine.
-void* drgui_text_engine_get_extra_data(drgui_text_engine* pTL);
-
-
-/// Sets the given text engine's text.
-void drgui_text_engine_set_text(drgui_text_engine* pTL, const char* text);
-
-/// Retrieves the given text engine's text.
-///
-/// @return The length of the string, not including the null terminator.
-///
-/// @remarks
-/// Call this function with set to NULL to retieve the required size of .
-size_t drgui_text_engine_get_text(drgui_text_engine* pTL, char* textOut, size_t textOutSize);
-
-
-/// Sets the function to call when a region of the text engine needs to be redrawn.
-void drgui_text_engine_set_on_dirty(drgui_text_engine* pTL, drgui_text_engine_on_dirty_proc proc);
-
-/// Sets the function to call when the content of the given text engine has changed.
-void drgui_text_engine_set_on_text_changed(drgui_text_engine* pTL, drgui_text_engine_on_text_changed_proc proc);
-
-/// Sets the function to call when the content of the given text engine's current undo point has moved.
-void drgui_text_engine_set_on_undo_point_changed(drgui_text_engine* pTL, drgui_text_engine_on_undo_point_changed_proc proc);
-
-
-/// Sets the size of the container.
-void drgui_text_engine_set_container_size(drgui_text_engine* pTL, float containerWidth, float containerHeight);
-
-/// Retrieves the size of the container.
-void drgui_text_engine_get_container_size(drgui_text_engine* pTL, float* pContainerWidthOut, float* pContainerHeightOut);
-
-/// Retrieves the width of the container.
-float drgui_text_engine_get_container_width(drgui_text_engine* pTL);
-
-/// Retrieves the height of the container.
-float drgui_text_engine_get_container_height(drgui_text_engine* pTL);
-
-
-/// Sets the inner offset of the given text engine.
-void drgui_text_engine_set_inner_offset(drgui_text_engine* pTL, float innerOffsetX, float innerOffsetY);
-
-/// Sets the inner offset of the given text engine on the x axis.
-void drgui_text_engine_set_inner_offset_x(drgui_text_engine* pTL, float innerOffsetX);
-
-/// Sets the inner offset of the given text engine on the y axis.
-void drgui_text_engine_set_inner_offset_y(drgui_text_engine* pTL, float innerOffsetY);
-
-/// Retrieves the inner offset of the given text engine.
-void drgui_text_engine_get_inner_offset(drgui_text_engine* pTL, float* pInnerOffsetX, float* pInnerOffsetY);
-
-/// Retrieves the inner offset of the given text engine on the x axis.
-float drgui_text_engine_get_inner_offset_x(drgui_text_engine* pTL);
-
-/// Retrieves the inner offset of the given text engine on the x axis.
-float drgui_text_engine_get_inner_offset_y(drgui_text_engine* pTL);
-
-
-/// Sets the default font to use for text runs.
-void drgui_text_engine_set_default_font(drgui_text_engine* pTL, drgui_font* pFont);
-
-/// Retrieves the default font to use for text runs.
-drgui_font* drgui_text_engine_get_default_font(drgui_text_engine* pTL);
-
-/// Sets the default text color of the given text engine.
-void drgui_text_engine_set_default_text_color(drgui_text_engine* pTL, drgui_color color);
-
-/// Retrieves the default text color of the given text engine.
-drgui_color drgui_text_engine_get_default_text_color(drgui_text_engine* pTL);
-
-/// Sets the default background color of the given text engine.
-void drgui_text_engine_set_default_bg_color(drgui_text_engine* pTL, drgui_color color);
-
-/// Retrieves the default background color of the given text engine.
-drgui_color drgui_text_engine_get_default_bg_color(drgui_text_engine* pTL);
-
-/// Sets the background color of selected text.
-void drgui_text_engine_set_selection_bg_color(drgui_text_engine* pTL, drgui_color color);
-
-/// Retrieves the background color of selected text.
-drgui_color drgui_text_engine_get_selection_bg_color(drgui_text_engine* pTL);
-
-/// Sets the background color of the line the cursor is sitting on.
-void drgui_text_engine_set_active_line_bg_color(drgui_text_engine* pTL, drgui_color color);
-
-/// Retrieves the background color of the line the cursor is sitting on.
-drgui_color drgui_text_engine_get_active_line_bg_color(drgui_text_engine* pTL);
-
-
-/// Sets the size of a tab in spaces.
-void drgui_text_engine_set_tab_size(drgui_text_engine* pTL, unsigned int sizeInSpaces);
-
-/// Retrieves the size of a tab in spaces.
-unsigned int drgui_text_engine_get_tab_size(drgui_text_engine* pTL);
-
-
-/// Sets the horizontal alignment of the given text engine.
-void drgui_text_engine_set_horizontal_align(drgui_text_engine* pTL, drgui_text_engine_alignment alignment);
-
-/// Retrieves the horizontal aligment of the given text engine.
-drgui_text_engine_alignment drgui_text_engine_get_horizontal_align(drgui_text_engine* pTL);
-
-/// Sets the vertical alignment of the given text engine.
-void drgui_text_engine_set_vertical_align(drgui_text_engine* pTL, drgui_text_engine_alignment alignment);
-
-/// Retrieves the vertical aligment of the given text engine.
-drgui_text_engine_alignment drgui_text_engine_get_vertical_align(drgui_text_engine* pTL);
-
-
-/// Retrieves the rectangle of the text relative to the bounds, taking alignment into account.
-drgui_rect drgui_text_engine_get_text_rect_relative_to_bounds(drgui_text_engine* pTL);
-
-
-/// Sets the width of the text cursor.
-void drgui_text_engine_set_cursor_width(drgui_text_engine* pTL, float cursorWidth);
-
-/// Retrieves the width of the text cursor.
-float drgui_text_engine_get_cursor_width(drgui_text_engine* pTL);
-
-/// Sets the color of the text cursor.
-void drgui_text_engine_set_cursor_color(drgui_text_engine* pTL, drgui_color cursorColor);
-
-/// Retrieves the color of the text cursor.
-drgui_color drgui_text_engine_get_cursor_color(drgui_text_engine* pTL);
-
-/// Sets the blink rate of the cursor in milliseconds.
-void drgui_text_engine_set_cursor_blink_rate(drgui_text_engine* pTL, unsigned int blinkRateInMilliseconds);
-
-/// Retrieves the blink rate of the cursor in milliseconds.
-unsigned int drgui_text_engine_get_cursor_blink_rate(drgui_text_engine* pTL);
-
-/// Shows the cursor.
-void drgui_text_engine_show_cursor(drgui_text_engine* pTL);
-
-/// Hides the cursor.
-void drgui_text_engine_hide_cursor(drgui_text_engine* pTL);
-
-/// Determines whether or not the cursor is visible.
-bool drgui_text_engine_is_showing_cursor(drgui_text_engine* pTL);
-
-/// Retrieves the position of the cursor, relative to the container.
-void drgui_text_engine_get_cursor_position(drgui_text_engine* pTL, float* pPosXOut, float* pPosYOut);
-
-/// Retrieves the rectangle of the cursor, relative to the container.
-drgui_rect drgui_text_engine_get_cursor_rect(drgui_text_engine* pTL);
-
-/// Retrieves the index of the line the cursor is currently sitting on.
-size_t drgui_text_engine_get_cursor_line(drgui_text_engine* pTL);
-
-/// Retrieves the index of the column the cursor is currently sitting on.
-size_t drgui_text_engine_get_cursor_column(drgui_text_engine* pTL);
-
-/// Retrieves the index of the character the cursor is currently sitting on.
-size_t drgui_text_engine_get_cursor_character(drgui_text_engine* pTL);
-
-/// Moves the cursor to the closest character based on the given input position.
-void drgui_text_engine_move_cursor_to_point(drgui_text_engine* pTL, float posX, float posY);
-
-/// Moves the cursor of the given text engine to the left by one character.
-bool drgui_text_engine_move_cursor_left(drgui_text_engine* pTL);
-
-/// Moves the cursor of the given text engine to the right by one character.
-bool drgui_text_engine_move_cursor_right(drgui_text_engine* pTL);
-
-/// Moves the cursor of the given text engine up one line.
-bool drgui_text_engine_move_cursor_up(drgui_text_engine* pTL);
-
-/// Moves the cursor of the given text engine down one line.
-bool drgui_text_engine_move_cursor_down(drgui_text_engine* pTL);
-
-/// Moves the cursor up or down the given number of lines.
-bool drgui_text_engine_move_cursor_y(drgui_text_engine* pTL, int amount);
-
-/// Moves the cursor of the given text engine to the end of the line.
-bool drgui_text_engine_move_cursor_to_end_of_line(drgui_text_engine* pTL);
-
-/// Moves the cursor of the given text engine to the start of the line.
-bool drgui_text_engine_move_cursor_to_start_of_line(drgui_text_engine* pTL);
-
-/// Moves the cursor of the given text engine to the end of the line at the given index.
-bool drgui_text_engine_move_cursor_to_end_of_line_by_index(drgui_text_engine* pTL, size_t iLine);
-
-/// Moves the cursor of the given text engine to the start of the line at the given index.
-bool drgui_text_engine_move_cursor_to_start_of_line_by_index(drgui_text_engine* pTL, size_t iLine);
-
-/// Moves the cursor of the given text engine to the end of the text.
-bool drgui_text_engine_move_cursor_to_end_of_text(drgui_text_engine* pTL);
-
-/// Moves the cursor of the given text engine to the end of the text.
-bool drgui_text_engine_move_cursor_to_start_of_text(drgui_text_engine* pTL);
-
-/// Moves the cursor to the start of the selected text.
-void drgui_text_engine_move_cursor_to_start_of_selection(drgui_text_engine* pTL);
-
-/// Moves the cursor to the end of the selected text.
-void drgui_text_engine_move_cursor_to_end_of_selection(drgui_text_engine* pTL);
-
-/// Moves the cursor to the given character index.
-void drgui_text_engine_move_cursor_to_character(drgui_text_engine* pTL, size_t characterIndex);
-
-/// Determines whether or not the cursor is sitting at the start of the selection.
-bool drgui_text_engine_is_cursor_at_start_of_selection(drgui_text_engine* pTL);
-
-/// Determines whether or not the cursor is sitting at the end fo the selection.
-bool drgui_text_engine_is_cursor_at_end_of_selection(drgui_text_engine* pTL);
-
-/// Swaps the position of the cursor based on the current selection.
-void drgui_text_engine_swap_selection_markers(drgui_text_engine* pTL);
-
-/// Sets the function to call when the cursor in the given text engine is mvoed.
-void drgui_text_engine_set_on_cursor_move(drgui_text_engine* pTL, drgui_text_engine_on_cursor_move_proc proc);
-
-/// Refreshes the cursor and selection marker positions.
-void drgui_text_engine_refresh_markers(drgui_text_engine* pTL);
-
-
-/// Inserts a character into the given text engine.
-///
-/// @return True if the text within the text engine has changed.
-bool drgui_text_engine_insert_character(drgui_text_engine* pTL, unsigned int character, size_t insertIndex);
-
-/// Inserts the given string at the given character index.
-///
-/// @return True if the text within the text engine has changed.
-bool drgui_text_engine_insert_text(drgui_text_engine* pTL, const char* text, size_t insertIndex);
-
-/// Deletes a range of text in the given text engine.
-///
-/// @return True if the text within the text engine has changed.
-bool drgui_text_engine_delete_text_range(drgui_text_engine* pTL, size_t iFirstCh, size_t iLastChPlus1);
-
-/// Inserts a character at the position of the cursor.
-///
-/// @return True if the text within the text engine has changed.
-bool drgui_text_engine_insert_character_at_cursor(drgui_text_engine* pTL, unsigned int character);
-
-/// Inserts a character at the position of the cursor.
-///
-/// @return True if the text within the text engine has changed.
-bool drgui_text_engine_insert_text_at_cursor(drgui_text_engine* pTL, const char* text);
-
-/// Deletes the character to the left of the cursor.
-///
-/// @return True if the text within the text engine has changed.
-bool drgui_text_engine_delete_character_to_left_of_cursor(drgui_text_engine* pTL);
-
-/// Deletes the character to the right of the cursor.
-///
-/// @return True if the text within the text engine has changed.
-bool drgui_text_engine_delete_character_to_right_of_cursor(drgui_text_engine* pTL);
-
-/// Deletes the currently selected text.
-///
-/// @return True if the text within the text engine has changed.
-bool drgui_text_engine_delete_selected_text(drgui_text_engine* pTL);
-
-
-/// Enter's into selection mode.
-///
-/// @remarks
-/// An application will typically enter selection mode when the Shift key is pressed, and then leave when the key is released.
-/// @par
-/// This will increment an internal counter, which is decremented with a corresponding call to drgui_text_engine_leave_selection_mode().
-/// Selection mode will be enabled so long as this counter is greater than 0. Thus, you must ensure you cleanly leave selection
-/// mode.
-void drgui_text_engine_enter_selection_mode(drgui_text_engine* pTL);
-
-/// Leaves selection mode.
-///
-/// @remarks
-/// This decrements the internal counter. Selection mode will not be disabled while this reference counter is greater than 0. Always
-/// ensure a leave is correctly matched with an enter.
-void drgui_text_engine_leave_selection_mode(drgui_text_engine* pTL);
-
-/// Determines whether or not the given text engine is in selection mode.
-bool drgui_text_engine_is_in_selection_mode(drgui_text_engine* pTL);
-
-/// Determines whether or not anything is selected in the given text engine.
-bool drgui_text_engine_is_anything_selected(drgui_text_engine* pTL);
-
-/// Deselects everything in the given text engine.
-void drgui_text_engine_deselect_all(drgui_text_engine* pTL);
-
-/// Selects everything in the given text engine.
-void drgui_text_engine_select_all(drgui_text_engine* pTL);
-
-/// Selects the given range of text.
-void drgui_text_engine_select(drgui_text_engine* pTL, size_t firstCharacter, size_t lastCharacter);
-
-/// Retrieves a copy of the selected text.
-///
-/// @remarks
-/// This returns the length of the selected text. Call this once with set to NULL to calculate the required size of the
-/// buffer.
-/// @par
-/// If the output buffer is not larger enough, the string will be truncated.
-size_t drgui_text_engine_get_selected_text(drgui_text_engine* pTL, char* textOut, size_t textOutLength);
-
-/// Retrieves the index of the first line of the current selection.
-size_t drgui_text_engine_get_selection_first_line(drgui_text_engine* pTL);
-
-/// Retrieves the index of the last line of the current selection.
-size_t drgui_text_engine_get_selection_last_line(drgui_text_engine* pTL);
-
-/// Moves the selection anchor to the end of the given line.
-void drgui_text_engine_move_selection_anchor_to_end_of_line(drgui_text_engine* pTL, size_t iLine);
-
-/// Moves the selection anchor to the start of the given line.
-void drgui_text_engine_move_selection_anchor_to_start_of_line(drgui_text_engine* pTL, size_t iLine);
-
-/// Retrieves the line the selection anchor is sitting on.
-size_t drgui_text_engine_get_selection_anchor_line(drgui_text_engine* pTL);
-
-
-/// Prepares the next undo/redo point.
-///
-/// @remarks
-/// This captures the state that will be applied when the undo/redo point is undone.
-bool drgui_text_engine_prepare_undo_point(drgui_text_engine* pTL);
-
-/// Creates a snapshot of the current state of the text engine and pushes it to the top of the undo/redo stack.
-bool drgui_text_engine_commit_undo_point(drgui_text_engine* pTL);
-
-/// Performs an undo operation.
-bool drgui_text_engine_undo(drgui_text_engine* pTL);
-
-/// Performs a redo operation.
-bool drgui_text_engine_redo(drgui_text_engine* pTL);
-
-/// Retrieves the number of undo points remaining in the stack.
-unsigned int drgui_text_engine_get_undo_points_remaining_count(drgui_text_engine* pTL);
-
-/// Retrieves the number of redo points remaining in the stack.
-unsigned int drgui_text_engine_get_redo_points_remaining_count(drgui_text_engine* pTL);
-
-/// Clears the undo stack.
-void drgui_text_engine_clear_undo_stack(drgui_text_engine* pTL);
-
-
-
-/// Retrieves the number of lines in the given text engine.
-size_t drgui_text_engine_get_line_count(drgui_text_engine* pTL);
-
-/// Retrieves the number of lines that can fit on the visible portion of the layout, starting from the given line.
-///
-/// @remarks
-/// Use this for controlling the page size for scrollbars.
-size_t drgui_text_engine_get_visible_line_count_starting_at(drgui_text_engine* pTL, size_t iFirstLine);
-
-/// Retrieves the position of the line at the given index on the y axis.
-///
-/// @remarks
-/// Use this for calculating the inner offset for scrolling on the y axis.
-float drgui_text_engine_get_line_pos_y(drgui_text_engine* pTL, size_t iLine);
-
-/// Finds the line under the given point on the y axis relative to the container.
-size_t drgui_text_engine_get_line_at_pos_y(drgui_text_engine* pTL, float posY);
-
-/// Retrieves the index of the first character of the line at the given index.
-size_t drgui_text_engine_get_line_first_character(drgui_text_engine* pTL, size_t iLine);
-
-/// Retrieves the index of the last character of the line at the given index.
-size_t drgui_text_engine_get_line_last_character(drgui_text_engine* pTL, size_t iLine);
-
-/// Retrieves teh index of the first and last character of the line at the given index.
-void drgui_text_engine_get_line_character_range(drgui_text_engine* pTL, size_t iLine, size_t* pCharStartOut, size_t* pCharEndOut);
-
-
-/// Sets the function to call when a run of text needs to be painted for the given text engine.
-void drgui_text_engine_set_on_paint_text(drgui_text_engine* pTL, drgui_text_engine_on_paint_text_proc proc);
-
-/// Sets the function to call when a quad needs to the be painted for the given text engine.
-void drgui_text_engine_set_on_paint_rect(drgui_text_engine* pTL, drgui_text_engine_on_paint_rect_proc proc);
-
-/// Paints the given text engine by calling the appropriate painting callbacks.
-///
-/// @remarks
-/// Typically a text engine will be painted to a GUI element. A pointer to an element can be passed to this function
-/// which will be passed to the callback functions. This is purely for convenience and nothing is actually drawn to
-/// the element outside of the callback functions.
-void drgui_text_engine_paint(drgui_text_engine* pTL, drgui_rect rect, drgui_element* pElement, void* pPaintData);
-
-
-/// Steps the given text engine by the given number of milliseconds.
-///
-/// @remarks
-/// This will trigger the on_dirty callback when the cursor switches it's blink states.
-void drgui_text_engine_step(drgui_text_engine* pTL, unsigned int milliseconds);
-
-
-/// Calls the given painting callbacks for the line numbers of the given text engine.
-void drgui_text_engine_paint_line_numbers(drgui_text_engine* pTL, float lineNumbersWidth, float lineNumbersHeight, drgui_color textColor, drgui_color backgroundColor, drgui_text_engine_on_paint_text_proc onPaintText, drgui_text_engine_on_paint_rect_proc onPaintRect, drgui_element* pElement, void* pPaintData);
-
-
-/// Finds the given string starting from the cursor and then looping back.
-bool drgui_text_engine_find_next(drgui_text_engine* pTL, const char* text, size_t* pSelectionStartOut, size_t* pSelectionEndOut);
-
-/// Finds the given string starting from the cursor, but does not loop back.
-bool drgui_text_engine_find_next_no_loop(drgui_text_engine* pTL, const char* text, size_t* pSelectionStartOut, size_t* pSelectionEndOut);
-
-#ifdef __cplusplus
-}
-#endif
-#endif //drgui_text_engine_h
-
-
-#ifdef DR_GUI_IMPLEMENTATION
-typedef struct
-{
- /// The index of the run within the line the marker is positioned on.
- size_t iRun;
-
- /// The index of the character within the run the marker is positioned to the left of.
- size_t iChar;
-
- /// The position on the x axis, relative to the x position of the run.
- float relativePosX;
-
- /// The absolute position on the x axis to place the marker when moving up and down lines. Note that this is not relative
- /// to the run, but rather the line. This will be updated when the marker is moved left and right.
- float absoluteSickyPosX;
-
-} drgui_text_marker;
-
-/// Keeps track of the current state of the text engine. Used for calculating the difference between two states for undo/redo.
-typedef struct
-{
- /// The text. Can be null in some cases where it isn't used.
- char* text;
-
- /// The index of the character the cursor is positioned at.
- size_t cursorPos;
-
- /// The index of the character the selection anchor is positioned at.
- size_t selectionAnchorPos;
-
- /// Whether or not anything is selected.
- bool isAnythingSelected;
-
-} drgui_text_engine_state;
-
-typedef struct
-{
- /// The position in the main string where the change is located. The length of the relevant string is used to determines how
- /// large of a chunk of text needs to be replaced.
- size_t diffPos;
-
- /// The string that was replaced. On undo, this will be inserted into the text engine. Can be empty, in which case this state
- /// object was created in response to an insert operation.
- char* oldText;
-
- /// The string that replaces the old text. On redo, this will be inserted into the text engine. This can be empty, in which case
- /// this state object was created in response to a delete operation.
- char* newText;
-
- /// The state of the text engine at the time the undo point was prepared, not including the text. The attribute
- /// of this object is always null.
- drgui_text_engine_state oldState;
-
- /// The state of the text engine at the time the undo point was committed, not including the text. The attribute
- /// of this object is always null.
- drgui_text_engine_state newState;
-
-} drgui_text_engine_undo_state;
-
-struct drgui_text_engine
-{
- /// The main text of the layout.
- char* text;
-
- /// The length of the text.
- size_t textLength;
-
-
- /// The function to call when the text engine needs to be redrawn.
- drgui_text_engine_on_dirty_proc onDirty;
-
- /// The function to call when the content of the text engine changes.
- drgui_text_engine_on_text_changed_proc onTextChanged;
-
- /// The function to call when the current undo point has changed.
- drgui_text_engine_on_undo_point_changed_proc onUndoPointChanged;
-
-
- /// The width of the container.
- float containerWidth;
-
- /// The height of the container.
- float containerHeight;
-
- /// The inner offset of the container.
- float innerOffsetX;
-
- /// The inner offset of the container.
- float innerOffsetY;
-
-
- /// The default font.
- drgui_font* pDefaultFont;
-
- /// The default text color.
- drgui_color defaultTextColor;
-
- /// The default background color.
- drgui_color defaultBackgroundColor;
-
- /// The background color to use for selected text.
- drgui_color selectionBackgroundColor;
-
- /// The background color to use for the line the cursor is currently sitting on.
- drgui_color lineBackgroundColor;
-
- /// The size of a tab in spaces.
- unsigned int tabSizeInSpaces;
-
- /// The horizontal alignment.
- drgui_text_engine_alignment horzAlign;
-
- /// The vertical alignment.
- drgui_text_engine_alignment vertAlign;
-
- /// The width of the text cursor.
- float cursorWidth;
-
- /// The color of the text cursor.
- drgui_color cursorColor;
-
- /// The blink rate in milliseconds of the cursor.
- unsigned int cursorBlinkRate;
-
- /// The amount of time in milliseconds to toggle the cursor's blink state.
- unsigned int timeToNextCursorBlink;
-
- /// Whether or not the cursor is showing based on it's blinking state.
- bool isCursorBlinkOn;
-
- /// Whether or not the cursor is being shown. False by default.
- bool isShowingCursor;
-
-
- /// The total width of the text.
- float textBoundsWidth;
-
- /// The total height of the text.
- float textBoundsHeight;
-
-
- /// The cursor.
- drgui_text_marker cursor;
-
- /// The selection anchor.
- drgui_text_marker selectionAnchor;
-
-
- /// The selection mode counter. When this is greater than 0 we are in selection mode, otherwise we are not. This
- /// is incremented by enter_selection_mode() and decremented by leave_selection_mode().
- unsigned int selectionModeCounter;
-
- /// Whether or not anything is selected.
- bool isAnythingSelected;
-
-
- /// The function to call when a text run needs to be painted.
- drgui_text_engine_on_paint_text_proc onPaintText;
-
- /// The function to call when a rectangle needs to be painted.
- drgui_text_engine_on_paint_rect_proc onPaintRect;
-
- /// The function to call when the cursor moves.
- drgui_text_engine_on_cursor_move_proc onCursorMove;
-
-
- /// The prepared undo/redo state. This will be filled with some state by PrepareUndoRedoPoint() and again with CreateUndoRedoPoint().
- drgui_text_engine_state preparedState;
-
- /// The undo/redo stack.
- drgui_text_engine_undo_state* pUndoStack;
-
- /// The number of items in the undo/redo stack.
- unsigned int undoStackCount;
-
- /// The index of the undo/redo state item we are currently sitting on.
- unsigned int iUndoState;
-
-
- /// The counter used to determine when an onDirty event needs to be posted.
- unsigned int dirtyCounter;
-
- /// The accumulated dirty rectangle. When dirtyCounter hits 0, this is the rectangle that's posted to the onDirty callback.
- drgui_rect accumulatedDirtyRect;
-
-
- /// A pointer to the buffer containing details about every run in the layout.
- drgui_text_run* pRuns;
-
- /// The number of runs in .
- size_t runCount;
-
- /// The size of the buffer in drgui_text_run's. This is used to determine whether or not the buffer
- /// needs to be reallocated upon adding a new run.
- size_t runBufferSize;
-
-
- /// The size of the extra data.
- size_t extraDataSize;
-
- /// A pointer to the extra data.
- char pExtraData[1];
-};
-
-/// Structure containing information about a line. This is used by first_line() and next_line().
-typedef struct
-{
- /// The index of the line.
- size_t index;
-
- /// The position of the line on the y axis.
- float posY;
-
- /// The height of the line.
- float height;
-
- /// The index of the first run on the line.
- size_t iFirstRun;
-
- /// The index of the last run on the line.
- size_t iLastRun;
-
-} drgui_text_engine_line;
-
-
-/// Performs a complete refresh of the given text engine.
-///
-/// @remarks
-/// This will delete every run and re-create them.
-DRGUI_PRIVATE void drgui_text_engine__refresh(drgui_text_engine* pTL);
-
-/// Refreshes the alignment of the given text engine.
-DRGUI_PRIVATE void drgui_text_engine__refresh_alignment(drgui_text_engine* pTL);
-
-/// Appends a text run to the list of runs in the given text engine.
-DRGUI_PRIVATE void drgui_text_engine__push_text_run(drgui_text_engine* pTL, drgui_text_run* pRun);
-
-/// Clears the internal list of text runs.
-DRGUI_PRIVATE void drgui_text_engine__clear_text_runs(drgui_text_engine* pTL);
-
-/// Helper for calculating the offset to apply to each line based on the alignment of the given text engine.
-DRGUI_PRIVATE void drgui_text_engine__calculate_line_alignment_offset(drgui_text_engine* pTL, float lineWidth, float* pOffsetXOut, float* pOffsetYOut);
-
-/// Helper for determine whether or not the given text run is whitespace.
-DRGUI_PRIVATE bool drgui_text_engine__is_text_run_whitespace(drgui_text_engine* pTL, drgui_text_run* pRun);
-
-/// Helper for calculating the width of a tab.
-DRGUI_PRIVATE float drgui_text_engine__get_tab_width(drgui_text_engine* pTL);
-
-
-/// Finds the line that's closest to the given point relative to the text.
-DRGUI_PRIVATE bool drgui_text_engine__find_closest_line_to_point(drgui_text_engine* pTL, float inputPosYRelativeToText, size_t* pFirstRunIndexOnLineOut, size_t* pLastRunIndexOnLinePlus1Out);
-
-/// Finds the run that's closest to the given point relative to the text.
-DRGUI_PRIVATE bool drgui_text_engine__find_closest_run_to_point(drgui_text_engine* pTL, float inputPosXRelativeToText, float inputPosYRelativeToText, size_t* pRunIndexOut);
-
-/// Retrieves some basic information about a line, namely the index of the last run on the line, and the line's height.
-DRGUI_PRIVATE bool drgui_text_engine__find_line_info(drgui_text_engine* pTL, size_t iFirstRunOnLine, size_t* pLastRunIndexOnLinePlus1Out, float* pLineHeightOut);
-
-/// Retrieves some basic information about a line by it's index.
-DRGUI_PRIVATE bool drgui_text_engine__find_line_info_by_index(drgui_text_engine* pTL, size_t iLine, drgui_rect* pRectOut, size_t* pFirstRunIndexOut, size_t* pLastRunIndexPlus1Out);
-
-/// Finds the last run on the line that the given run is sitting on.
-DRGUI_PRIVATE bool drgui_text_engine__find_last_run_on_line_starting_from_run(drgui_text_engine* pTL, size_t iRun, size_t* pLastRunIndexOnLineOut);
-
-/// Finds the first run on the line that the given run is sitting on.
-DRGUI_PRIVATE bool drgui_text_engine__find_first_run_on_line_starting_from_run(drgui_text_engine* pTL, size_t iRun, size_t* pFirstRunIndexOnLineOut);
-
-/// Finds the run containing the character at the given index.
-DRGUI_PRIVATE bool drgui_text_engine__find_run_at_character(drgui_text_engine* pTL, size_t iChar, size_t* pRunIndexOut);
-
-
-/// Creates a blank text marker.
-DRGUI_PRIVATE drgui_text_marker drgui_text_engine__new_marker();
-
-/// Moves the given text marker to the given point, relative to the container.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_point_relative_to_container(drgui_text_engine* pTL, drgui_text_marker* pMarker, float inputPosX, float inputPosY);
-
-/// Retrieves the position of the given text marker relative to the container.
-DRGUI_PRIVATE void drgui_text_engine__get_marker_position_relative_to_container(drgui_text_engine* pTL, drgui_text_marker* pMarker, float* pPosXOut, float* pPosYOut);
-
-/// Moves the marker to the given point, relative to the text rectangle.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_point(drgui_text_engine* pTL, drgui_text_marker* pMarker, float inputPosXRelativeToText, float inputPosYRelativeToText);
-
-/// Moves the given marker to the left by one character.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_left(drgui_text_engine* pTL, drgui_text_marker* pMarker);
-
-/// Moves the given marker to the right by one character.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_right(drgui_text_engine* pTL, drgui_text_marker* pMarker);
-
-/// Moves the given marker up one line.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_up(drgui_text_engine* pTL, drgui_text_marker* pMarker);
-
-/// Moves the given marker down one line.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_down(drgui_text_engine* pTL, drgui_text_marker* pMarker);
-
-/// Moves the given marker down one line.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_y(drgui_text_engine* pTL, drgui_text_marker* pMarker, int amount);
-
-/// Moves the given marker to the end of the line it's currently sitting on.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_end_of_line(drgui_text_engine* pTL, drgui_text_marker* pMarker);
-
-/// Moves the given marker to the start of the line it's currently sitting on.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_start_of_line(drgui_text_engine* pTL, drgui_text_marker* pMarker);
-
-/// Moves the given marker to the end of the line at the given index.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_end_of_line_by_index(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iLine);
-
-/// Moves the given marker to the start of the line at the given index.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_start_of_line_by_index(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iLine);
-
-/// Moves the given marker to the end of the text.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_end_of_text(drgui_text_engine* pTL, drgui_text_marker* pMarker);
-
-/// Moves the given marker to the start of the text.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_start_of_text(drgui_text_engine* pTL, drgui_text_marker* pMarker);
-
-/// Moves the given marker to the last character of the given run.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_last_character_of_run(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iRun);
-
-/// Moves the given marker to the first character of the given run.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_first_character_of_run(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iRun);
-
-/// Moves the given marker to the last character of the previous run.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_last_character_of_prev_run(drgui_text_engine* pTL, drgui_text_marker* pMarker);
-
-/// Moves the given marker to the first character of the next run.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_first_character_of_next_run(drgui_text_engine* pTL, drgui_text_marker* pMarker);
-
-/// Moves the given marker to the character at the given position.
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_character(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iChar);
-
-
-/// Updates the relative position of the given marker.
-///
-/// @remarks
-/// This assumes the iRun and iChar properties are valid.
-DRGUI_PRIVATE bool drgui_text_engine__update_marker_relative_position(drgui_text_engine* pTL, drgui_text_marker* pMarker);
-
-/// Updates the sticky position of the given marker.
-DRGUI_PRIVATE void drgui_text_engine__update_marker_sticky_position(drgui_text_engine* pTL, drgui_text_marker* pMarker);
-
-
-/// Retrieves the index of the character the given marker is located at.
-DRGUI_PRIVATE size_t drgui_text_engine__get_marker_absolute_char_index(drgui_text_engine* pTL, drgui_text_marker* pMarker);
-
-
-/// Helper function for determining whether or not there is any spacing between the selection markers.
-DRGUI_PRIVATE bool drgui_text_engine__has_spacing_between_selection_markers(drgui_text_engine* pTL);
-
-/// Splits the given run into sub-runs based on the current selection rectangle. Returns the sub-run count.
-DRGUI_PRIVATE size_t drgui_text_engine__split_text_run_by_selection(drgui_text_engine* pTL, drgui_text_run* pRunToSplit, drgui_text_run pSubRunsOut[3]);
-
-
-/// Retrieves pointers to the selection markers in the correct order.
-DRGUI_PRIVATE bool drgui_text_engine__get_selection_markers(drgui_text_engine* pTL, drgui_text_marker** ppSelectionMarker0Out, drgui_text_marker** ppSelectionMarker1Out);
-
-
-/// Retrieves an iterator to the first line in the text engine.
-DRGUI_PRIVATE bool drgui_text_engine__first_line(drgui_text_engine* pTL, drgui_text_engine_line* pLine);
-
-/// Retrieves an iterator to the next line in the text engine.
-DRGUI_PRIVATE bool drgui_text_engine__next_line(drgui_text_engine* pTL, drgui_text_engine_line* pLine);
-
-
-/// Removes the undo/redo state stack items after the current undo/redo point.
-DRGUI_PRIVATE void drgui_text_engine__trim_undo_stack(drgui_text_engine* pTL);
-
-/// Initializes the given undo state object by diff-ing the given layout states.
-DRGUI_PRIVATE bool drgui_text_engine__diff_states(drgui_text_engine_state* pPrevState, drgui_text_engine_state* pCurrentState, drgui_text_engine_undo_state* pUndoStateOut);
-
-/// Uninitializes the given undo state object. This basically just free's the internal string.
-DRGUI_PRIVATE void drgui_text_engine__uninit_undo_state(drgui_text_engine_undo_state* pUndoState);
-
-/// Pushes an undo state onto the undo stack.
-DRGUI_PRIVATE void drgui_text_engine__push_undo_state(drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState);
-
-/// Applies the given undo state.
-DRGUI_PRIVATE void drgui_text_engine__apply_undo_state(drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState);
-
-/// Applies the given undo state as a redo operation.
-DRGUI_PRIVATE void drgui_text_engine__apply_redo_state(drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState);
-
-
-/// Retrieves a rectangle relative to the given text engine that's equal to the size of the container.
-DRGUI_PRIVATE drgui_rect drgui_text_engine__local_rect(drgui_text_engine* pTL);
-
-
-/// Called when the cursor moves.
-DRGUI_PRIVATE void drgui_text_engine__on_cursor_move(drgui_text_engine* pTL);
-
-/// Called when the text engine needs to be redrawn.
-DRGUI_PRIVATE void drgui_text_engine__on_dirty(drgui_text_engine* pTL, drgui_rect rect);
-
-/// Increments the counter. The counter is decremented with drgui_text_engine__end_dirty(). Use this for batching redraws.
-DRGUI_PRIVATE void drgui_text_engine__begin_dirty(drgui_text_engine* pTL);
-
-/// Decrements the dirty counter, and if it hits 0 posts the onDirty callback.
-DRGUI_PRIVATE void drgui_text_engine__end_dirty(drgui_text_engine* pTL);
-
-
-
-
-drgui_text_engine* drgui_create_text_engine(drgui_context* pContext, size_t extraDataSize, void* pExtraData)
-{
- if (pContext == NULL) {
- return NULL;
- }
-
- drgui_text_engine* pTL = (drgui_text_engine*)malloc(sizeof(drgui_text_engine) + extraDataSize);
- if (pTL == NULL) {
- return NULL;
- }
-
- pTL->text = NULL;
- pTL->textLength = 0;
- pTL->onDirty = NULL;
- pTL->onTextChanged = NULL;
- pTL->onUndoPointChanged = NULL;
- pTL->containerWidth = 0;
- pTL->containerHeight = 0;
- pTL->innerOffsetX = 0;
- pTL->innerOffsetY = 0;
- pTL->pDefaultFont = NULL;
- pTL->defaultTextColor = drgui_rgb(224, 224, 224);
- pTL->defaultBackgroundColor = drgui_rgb(48, 48, 48);
- pTL->selectionBackgroundColor = drgui_rgb(64, 128, 192);
- pTL->lineBackgroundColor = drgui_rgb(40, 40, 40);
- pTL->tabSizeInSpaces = 4;
- pTL->horzAlign = drgui_text_engine_alignment_left;
- pTL->vertAlign = drgui_text_engine_alignment_top;
- pTL->cursorWidth = 1;
- pTL->cursorColor = drgui_rgb(224, 224, 224);
- pTL->cursorBlinkRate = 500;
- pTL->timeToNextCursorBlink = pTL->cursorBlinkRate;
- pTL->isCursorBlinkOn = true;
- pTL->isShowingCursor = false;
- pTL->textBoundsWidth = 0;
- pTL->textBoundsHeight = 0;
- pTL->cursor = drgui_text_engine__new_marker();
- pTL->selectionAnchor = drgui_text_engine__new_marker();
- pTL->selectionModeCounter = 0;
- pTL->isAnythingSelected = false;
- pTL->onPaintText = NULL;
- pTL->onPaintRect = NULL;
- pTL->onCursorMove = NULL;
- pTL->preparedState.text = NULL;
- pTL->pUndoStack = NULL;
- pTL->undoStackCount = 0;
- pTL->iUndoState = 0;
- pTL->dirtyCounter = 0;
- pTL->accumulatedDirtyRect = drgui_make_inside_out_rect();
- pTL->pRuns = NULL;
- pTL->runCount = 0;
- pTL->runBufferSize = 0;
-
- pTL->extraDataSize = extraDataSize;
- if (pExtraData != NULL) {
- memcpy(pTL->pExtraData, pExtraData, extraDataSize);
- }
-
- return pTL;
-}
-
-void drgui_delete_text_engine(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return;
- }
-
- drgui_text_engine_clear_undo_stack(pTL);
-
- free(pTL->pRuns);
- free(pTL->preparedState.text);
- free(pTL->text);
- free(pTL);
-}
-
-
-size_t drgui_text_engine_get_extra_data_size(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return 0;
- }
-
- return pTL->extraDataSize;
-}
-
-void* drgui_text_engine_get_extra_data(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return NULL;
- }
-
- return pTL->pExtraData;
-}
-
-
-void drgui_text_engine_set_text(drgui_text_engine* pTL, const char* text)
-{
- if (pTL == NULL) {
- return;
- }
-
- size_t textLength = strlen(text);
-
- free(pTL->text);
- pTL->text = (char*)malloc(textLength + 1); // +1 for null terminator.
-
- // We now need to copy over the text, however we need to skip past \r characters in order to normalize line endings
- // and keep everything simple.
- char* dst = pTL->text;
- const char* src = text;
- while (*src != '\0')
- {
- if (*src != '\r') {
- *dst++ = *src;
- }
-
- src++;
- }
- *dst = '\0';
-
- pTL->textLength = dst - pTL->text;
-
- // A change in text means we need to refresh the layout.
- drgui_text_engine__refresh(pTL);
-
- // If the position of the cursor is past the last character we'll need to move it.
- if (drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor) >= pTL->textLength) {
- drgui_text_engine_move_cursor_to_end_of_text(pTL);
- }
-
- if (pTL->onTextChanged) {
- pTL->onTextChanged(pTL);
- }
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
-}
-
-size_t drgui_text_engine_get_text(drgui_text_engine* pTL, char* textOut, size_t textOutSize)
-{
- if (pTL == NULL) {
- return 0;
- }
-
- if (textOut == NULL) {
- return pTL->textLength;
- }
-
-
- if (drgui__strcpy_s(textOut, textOutSize, (pTL->text != NULL) ? pTL->text : "") == 0) {
- return pTL->textLength;
- }
-
- return 0; // Error with strcpy_s().
-}
-
-
-void drgui_text_engine_set_on_dirty(drgui_text_engine* pTL, drgui_text_engine_on_dirty_proc proc)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->onDirty = proc;
-}
-
-void drgui_text_engine_set_on_text_changed(drgui_text_engine* pTL, drgui_text_engine_on_text_changed_proc proc)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->onTextChanged = proc;
-}
-
-void drgui_text_engine_set_on_undo_point_changed(drgui_text_engine* pTL, drgui_text_engine_on_undo_point_changed_proc proc)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->onUndoPointChanged = proc;
-}
-
-
-void drgui_text_engine_set_container_size(drgui_text_engine* pTL, float containerWidth, float containerHeight)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->containerWidth = containerWidth;
- pTL->containerHeight = containerHeight;
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
-}
-
-void drgui_text_engine_get_container_size(drgui_text_engine* pTL, float* pContainerWidthOut, float* pContainerHeightOut)
-{
- float containerWidth = 0;
- float containerHeight = 0;
-
- if (pTL != NULL)
- {
- containerWidth = pTL->containerWidth;
- containerHeight = pTL->containerHeight;
- }
-
-
- if (pContainerWidthOut) {
- *pContainerWidthOut = containerWidth;
- }
- if (pContainerHeightOut) {
- *pContainerHeightOut = containerHeight;
- }
-}
-
-float drgui_text_engine_get_container_width(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return 0;
- }
-
- return pTL->containerWidth;
-}
-
-float drgui_text_engine_get_container_height(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return 0;
- }
-
- return pTL->containerHeight;
-}
-
-
-void drgui_text_engine_set_inner_offset(drgui_text_engine* pTL, float innerOffsetX, float innerOffsetY)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->innerOffsetX = innerOffsetX;
- pTL->innerOffsetY = innerOffsetY;
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
-}
-
-void drgui_text_engine_set_inner_offset_x(drgui_text_engine* pTL, float innerOffsetX)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->innerOffsetX = innerOffsetX;
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
-}
-
-void drgui_text_engine_set_inner_offset_y(drgui_text_engine* pTL, float innerOffsetY)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->innerOffsetY = innerOffsetY;
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
-}
-
-void drgui_text_engine_get_inner_offset(drgui_text_engine* pTL, float* pInnerOffsetX, float* pInnerOffsetY)
-{
- float innerOffsetX = 0;
- float innerOffsetY = 0;
-
- if (pTL != NULL)
- {
- innerOffsetX = pTL->innerOffsetX;
- innerOffsetY = pTL->innerOffsetY;
- }
-
-
- if (pInnerOffsetX) {
- *pInnerOffsetX = innerOffsetX;
- }
- if (pInnerOffsetY) {
- *pInnerOffsetY = innerOffsetY;
- }
-}
-
-float drgui_text_engine_get_inner_offset_x(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return 0;
- }
-
- return pTL->innerOffsetX;
-}
-
-float drgui_text_engine_get_inner_offset_y(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return 0;
- }
-
- return pTL->innerOffsetY;
-}
-
-
-void drgui_text_engine_set_default_font(drgui_text_engine* pTL, drgui_font* pFont)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->pDefaultFont = pFont;
-
- // A change in font requires a layout refresh.
- drgui_text_engine__refresh(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
-}
-
-drgui_font* drgui_text_engine_get_default_font(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return NULL;
- }
-
- return pTL->pDefaultFont;
-}
-
-void drgui_text_engine_set_default_text_color(drgui_text_engine* pTL, drgui_color color)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->defaultTextColor = color;
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
-}
-
-drgui_color drgui_text_engine_get_default_text_color(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return drgui_rgb(0, 0, 0);
- }
-
- return pTL->defaultTextColor;
-}
-
-void drgui_text_engine_set_default_bg_color(drgui_text_engine* pTL, drgui_color color)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->defaultBackgroundColor = color;
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
-}
-
-drgui_color drgui_text_engine_get_default_bg_color(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return drgui_rgb(0, 0, 0);
- }
-
- return pTL->defaultBackgroundColor;
-}
-
-void drgui_text_engine_set_selection_bg_color(drgui_text_engine* pTL, drgui_color color)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->selectionBackgroundColor = color;
-
- if (drgui_text_engine_is_anything_selected(pTL)) {
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-}
-
-drgui_color drgui_text_engine_get_selection_bg_color(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return drgui_rgb(0, 0, 0);
- }
-
- return pTL->selectionBackgroundColor;
-}
-
-void drgui_text_engine_set_active_line_bg_color(drgui_text_engine* pTL, drgui_color color)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->lineBackgroundColor = color;
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
-}
-
-drgui_color drgui_text_engine_get_active_line_bg_color(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return drgui_rgb(0, 0, 0);
- }
-
- return pTL->lineBackgroundColor;
-}
-
-
-void drgui_text_engine_set_tab_size(drgui_text_engine* pTL, unsigned int sizeInSpaces)
-{
- if (pTL == NULL) {
- return;
- }
-
- if (pTL->tabSizeInSpaces != sizeInSpaces)
- {
- pTL->tabSizeInSpaces = sizeInSpaces;
-
- drgui_text_engine__refresh(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-}
-
-unsigned int drgui_text_engine_get_tab_size(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return 0;
- }
-
- return pTL->tabSizeInSpaces;
-}
-
-
-void drgui_text_engine_set_horizontal_align(drgui_text_engine* pTL, drgui_text_engine_alignment alignment)
-{
- if (pTL == NULL) {
- return;
- }
-
- if (pTL->horzAlign != alignment)
- {
- pTL->horzAlign = alignment;
-
- drgui_text_engine__refresh_alignment(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-}
-
-drgui_text_engine_alignment drgui_text_engine_get_horizontal_align(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return drgui_text_engine_alignment_left;
- }
-
- return pTL->horzAlign;
-}
-
-void drgui_text_engine_set_vertical_align(drgui_text_engine* pTL, drgui_text_engine_alignment alignment)
-{
- if (pTL == NULL) {
- return;
- }
-
- if (pTL->vertAlign != alignment)
- {
- pTL->vertAlign = alignment;
-
- drgui_text_engine__refresh_alignment(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-}
-
-drgui_text_engine_alignment drgui_text_engine_get_vertical_align(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return drgui_text_engine_alignment_top;
- }
-
- return pTL->vertAlign;
-}
-
-
-drgui_rect drgui_text_engine_get_text_rect_relative_to_bounds(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return drgui_make_rect(0, 0, 0, 0);
- }
-
- drgui_rect rect;
- rect.left = 0;
- rect.top = 0;
-
-
- switch (pTL->horzAlign)
- {
- case drgui_text_engine_alignment_right:
- {
- rect.left = pTL->containerWidth - pTL->textBoundsWidth;
- break;
- }
-
- case drgui_text_engine_alignment_center:
- {
- rect.left = (pTL->containerWidth - pTL->textBoundsWidth) / 2;
- break;
- }
-
- case drgui_text_engine_alignment_left:
- case drgui_text_engine_alignment_top: // Invalid for horizontal align.
- case drgui_text_engine_alignment_bottom: // Invalid for horizontal align.
- default:
- {
- break;
- }
- }
-
-
- switch (pTL->vertAlign)
- {
- case drgui_text_engine_alignment_bottom:
- {
- rect.top = pTL->containerHeight - pTL->textBoundsHeight;
- break;
- }
-
- case drgui_text_engine_alignment_center:
- {
- rect.top = (pTL->containerHeight - pTL->textBoundsHeight) / 2;
- break;
- }
-
- case drgui_text_engine_alignment_top:
- case drgui_text_engine_alignment_left: // Invalid for vertical align.
- case drgui_text_engine_alignment_right: // Invalid for vertical align.
- default:
- {
- break;
- }
- }
-
-
- rect.left += pTL->innerOffsetX;
- rect.top += pTL->innerOffsetY;
- rect.right = rect.left + pTL->textBoundsWidth;
- rect.bottom = rect.top + pTL->textBoundsHeight;
-
- return rect;
-}
-
-
-void drgui_text_engine_set_cursor_width(drgui_text_engine* pTL, float cursorWidth)
-{
- if (pTL == NULL) {
- return;
- }
-
- drgui_rect oldCursorRect = drgui_text_engine_get_cursor_rect(pTL);
- pTL->cursorWidth = cursorWidth;
- if (pTL->cursorWidth > 0 && pTL->cursorWidth < 1) {
- pTL->cursorWidth = 1;
- }
-
- drgui_text_engine__on_dirty(pTL, drgui_rect_union(oldCursorRect, drgui_text_engine_get_cursor_rect(pTL)));
-}
-
-float drgui_text_engine_get_cursor_width(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return 0;
- }
-
- return pTL->cursorWidth;
-}
-
-void drgui_text_engine_set_cursor_color(drgui_text_engine* pTL, drgui_color cursorColor)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->cursorColor = cursorColor;
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine_get_cursor_rect(pTL));
-}
-
-drgui_color drgui_text_engine_get_cursor_color(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return drgui_rgb(0, 0, 0);
- }
-
- return pTL->cursorColor;
-}
-
-void drgui_text_engine_set_cursor_blink_rate(drgui_text_engine* pTL, unsigned int blinkRateInMilliseconds)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->cursorBlinkRate = blinkRateInMilliseconds;
-}
-
-unsigned int drgui_text_engine_get_cursor_blink_rate(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return 0;
- }
-
- return pTL->cursorBlinkRate;
-}
-
-void drgui_text_engine_show_cursor(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return;
- }
-
- if (!pTL->isShowingCursor)
- {
- pTL->isShowingCursor = true;
-
- pTL->timeToNextCursorBlink = pTL->cursorBlinkRate;
- pTL->isCursorBlinkOn = true;
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine_get_cursor_rect(pTL));
- }
-}
-
-void drgui_text_engine_hide_cursor(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return;
- }
-
- if (pTL->isShowingCursor)
- {
- pTL->isShowingCursor = false;
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine_get_cursor_rect(pTL));
- }
-}
-
-bool drgui_text_engine_is_showing_cursor(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return false;
- }
-
- return pTL->isShowingCursor;
-}
-
-void drgui_text_engine_get_cursor_position(drgui_text_engine* pTL, float* pPosXOut, float* pPosYOut)
-{
- if (pTL == NULL) {
- return;
- }
-
- drgui_text_engine__get_marker_position_relative_to_container(pTL, &pTL->cursor, pPosXOut, pPosYOut);
-}
-
-drgui_rect drgui_text_engine_get_cursor_rect(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return drgui_make_rect(0, 0, 0, 0);
- }
-
- drgui_rect lineRect = drgui_make_rect(0, 0, 0, 0);
-
- if (pTL->runCount > 0)
- {
- drgui_text_engine__find_line_info_by_index(pTL, pTL->pRuns[pTL->cursor.iRun].iLine, &lineRect, NULL, NULL);
- }
- else if (pTL->pDefaultFont != NULL)
- {
- drgui_font_metrics defaultFontMetrics;
- drgui_get_font_metrics(pTL->pDefaultFont, &defaultFontMetrics);
-
- lineRect.bottom = (float)defaultFontMetrics.lineHeight;
- }
-
-
-
- float cursorPosX;
- float cursorPosY;
- drgui_text_engine_get_cursor_position(pTL, &cursorPosX, &cursorPosY);
-
- return drgui_make_rect(cursorPosX, cursorPosY, cursorPosX + pTL->cursorWidth, cursorPosY + (lineRect.bottom - lineRect.top));
-}
-
-size_t drgui_text_engine_get_cursor_line(drgui_text_engine* pTL)
-{
- if (pTL == NULL || pTL->runCount == 0) {
- return 0;
- }
-
- return pTL->pRuns[pTL->cursor.iRun].iLine;
-}
-
-size_t drgui_text_engine_get_cursor_column(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return 0;
- }
-
- float posX;
- float posY;
- drgui_text_engine_get_cursor_position(pTL, &posX, &posY);
-
- drgui_font_metrics fontMetrics;
- drgui_get_font_metrics(pTL->pDefaultFont, &fontMetrics);
-
- return (unsigned int)((int)posX / fontMetrics.spaceWidth);
-}
-
-size_t drgui_text_engine_get_cursor_character(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return 0;
- }
-
- return drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor);
-}
-
-void drgui_text_engine_move_cursor_to_point(drgui_text_engine* pTL, float posX, float posY)
-{
- if (pTL == NULL) {
- return;
- }
-
- size_t iRunOld = pTL->cursor.iRun;
- size_t iCharOld = pTL->cursor.iChar;
- drgui_text_engine__move_marker_to_point_relative_to_container(pTL, &pTL->cursor, posX, posY);
-
- if (drgui_text_engine_is_in_selection_mode(pTL)) {
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
- }
-
- if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
- drgui_text_engine__on_cursor_move(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-}
-
-bool drgui_text_engine_move_cursor_left(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return false;
- }
-
- size_t iRunOld = pTL->cursor.iRun;
- size_t iCharOld = pTL->cursor.iChar;
- if (drgui_text_engine__move_marker_left(pTL, &pTL->cursor)) {
- if (drgui_text_engine_is_in_selection_mode(pTL)) {
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
- }
-
- if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
- drgui_text_engine__on_cursor_move(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-
- return true;
- }
-
- return false;
-}
-
-bool drgui_text_engine_move_cursor_right(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return false;
- }
-
- size_t iRunOld = pTL->cursor.iRun;
- size_t iCharOld = pTL->cursor.iChar;
- if (drgui_text_engine__move_marker_right(pTL, &pTL->cursor)) {
- if (drgui_text_engine_is_in_selection_mode(pTL)) {
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
- }
-
- if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
- drgui_text_engine__on_cursor_move(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-
- return true;
- }
-
- return false;
-}
-
-bool drgui_text_engine_move_cursor_up(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return false;
- }
-
- size_t iRunOld = pTL->cursor.iRun;
- size_t iCharOld = pTL->cursor.iChar;
- if (drgui_text_engine__move_marker_up(pTL, &pTL->cursor)) {
- if (drgui_text_engine_is_in_selection_mode(pTL)) {
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
- }
-
- if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
- drgui_text_engine__on_cursor_move(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-
- return true;
- }
-
- return false;
-}
-
-bool drgui_text_engine_move_cursor_down(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return false;
- }
-
- size_t iRunOld = pTL->cursor.iRun;
- size_t iCharOld = pTL->cursor.iChar;
- if (drgui_text_engine__move_marker_down(pTL, &pTL->cursor)) {
- if (drgui_text_engine_is_in_selection_mode(pTL)) {
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
- }
-
- if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
- drgui_text_engine__on_cursor_move(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-
- return true;
- }
-
- return false;
-}
-
-bool drgui_text_engine_move_cursor_y(drgui_text_engine* pTL, int amount)
-{
- if (pTL == NULL) {
- return false;
- }
-
- size_t iRunOld = pTL->cursor.iRun;
- size_t iCharOld = pTL->cursor.iChar;
- if (drgui_text_engine__move_marker_y(pTL, &pTL->cursor, amount)) {
- if (drgui_text_engine_is_in_selection_mode(pTL)) {
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
- }
-
- if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
- drgui_text_engine__on_cursor_move(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-
- return true;
- }
-
- return false;
-}
-
-bool drgui_text_engine_move_cursor_to_end_of_line(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return false;
- }
-
- size_t iRunOld = pTL->cursor.iRun;
- size_t iCharOld = pTL->cursor.iChar;
- if (drgui_text_engine__move_marker_to_end_of_line(pTL, &pTL->cursor)) {
- if (drgui_text_engine_is_in_selection_mode(pTL)) {
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
- }
-
- if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
- drgui_text_engine__on_cursor_move(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-
- return true;
- }
-
- return false;
-}
-
-bool drgui_text_engine_move_cursor_to_start_of_line(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return false;
- }
-
- size_t iRunOld = pTL->cursor.iRun;
- size_t iCharOld = pTL->cursor.iChar;
- if (drgui_text_engine__move_marker_to_start_of_line(pTL, &pTL->cursor)) {
- if (drgui_text_engine_is_in_selection_mode(pTL)) {
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
- }
-
- if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
- drgui_text_engine__on_cursor_move(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-
- return true;
- }
-
- return false;
-}
-
-bool drgui_text_engine_move_cursor_to_end_of_line_by_index(drgui_text_engine* pTL, size_t iLine)
-{
- if (pTL == NULL) {
- return false;
- }
-
- size_t iRunOld = pTL->cursor.iRun;
- size_t iCharOld = pTL->cursor.iChar;
- if (drgui_text_engine__move_marker_to_end_of_line_by_index(pTL, &pTL->cursor, iLine)) {
- if (drgui_text_engine_is_in_selection_mode(pTL)) {
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
- }
-
- if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
- drgui_text_engine__on_cursor_move(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-
- return true;
- }
-
- return false;
-}
-
-bool drgui_text_engine_move_cursor_to_start_of_line_by_index(drgui_text_engine* pTL, size_t iLine)
-{
- if (pTL == NULL) {
- return false;
- }
-
- size_t iRunOld = pTL->cursor.iRun;
- size_t iCharOld = pTL->cursor.iChar;
- if (drgui_text_engine__move_marker_to_start_of_line_by_index(pTL, &pTL->cursor, iLine)) {
- if (drgui_text_engine_is_in_selection_mode(pTL)) {
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
- }
-
- if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
- drgui_text_engine__on_cursor_move(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-
- return true;
- }
-
- return false;
-}
-
-bool drgui_text_engine_move_cursor_to_end_of_text(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return false;
- }
-
- size_t iRunOld = pTL->cursor.iRun;
- size_t iCharOld = pTL->cursor.iChar;
- if (drgui_text_engine__move_marker_to_end_of_text(pTL, &pTL->cursor)) {
- if (drgui_text_engine_is_in_selection_mode(pTL)) {
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
- }
-
- if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
- drgui_text_engine__on_cursor_move(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-
- return true;
- }
-
- return false;
-}
-
-bool drgui_text_engine_move_cursor_to_start_of_text(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return false;
- }
-
- size_t iRunOld = pTL->cursor.iRun;
- size_t iCharOld = pTL->cursor.iChar;
- if (drgui_text_engine__move_marker_to_start_of_text(pTL, &pTL->cursor)) {
- if (drgui_text_engine_is_in_selection_mode(pTL)) {
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
- }
-
- if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
- drgui_text_engine__on_cursor_move(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-
- return true;
- }
-
- return false;
-}
-
-void drgui_text_engine_move_cursor_to_start_of_selection(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return;
- }
-
- drgui_text_marker* pSelectionMarker0;
- drgui_text_marker* pSelectionMarker1;
- if (drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1))
- {
- pTL->cursor = *pSelectionMarker0;
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-}
-
-void drgui_text_engine_move_cursor_to_end_of_selection(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return;
- }
-
- drgui_text_marker* pSelectionMarker0;
- drgui_text_marker* pSelectionMarker1;
- if (drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1))
- {
- pTL->cursor = *pSelectionMarker1;
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-}
-
-void drgui_text_engine_move_cursor_to_character(drgui_text_engine* pTL, size_t characterIndex)
-{
- if (pTL == NULL) {
- return;
- }
-
- size_t iRunOld = pTL->cursor.iRun;
- size_t iCharOld = pTL->cursor.iChar;
- if (drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, characterIndex)) {
- if (drgui_text_engine_is_in_selection_mode(pTL)) {
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
- }
-
- if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
- drgui_text_engine__on_cursor_move(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
- }
-}
-
-bool drgui_text_engine_is_cursor_at_start_of_selection(drgui_text_engine* pTL)
-{
- drgui_text_marker* pSelectionMarker0;
- drgui_text_marker* pSelectionMarker1;
- if (drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1)) {
- return &pTL->cursor == pSelectionMarker0;
- }
-
- return false;
-}
-
-bool drgui_text_engine_is_cursor_at_end_of_selection(drgui_text_engine* pTL)
-{
- drgui_text_marker* pSelectionMarker0;
- drgui_text_marker* pSelectionMarker1;
- if (drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1)) {
- return &pTL->cursor == pSelectionMarker1;
- }
-
- return false;
-}
-
-void drgui_text_engine_swap_selection_markers(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return;
- }
-
- drgui_text_marker* pSelectionMarker0;
- drgui_text_marker* pSelectionMarker1;
- if (drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1))
- {
- size_t iRunOld = pTL->cursor.iRun;
- size_t iCharOld = pTL->cursor.iChar;
-
- drgui_text_marker temp = *pSelectionMarker0;
- *pSelectionMarker0 = *pSelectionMarker1;
- *pSelectionMarker1 = temp;
-
- if (iRunOld != pTL->cursor.iRun || iCharOld != pTL->cursor.iChar) {
- drgui_text_engine__on_cursor_move(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
- }
- }
-}
-
-void drgui_text_engine_set_on_cursor_move(drgui_text_engine* pTL, drgui_text_engine_on_cursor_move_proc proc)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->onCursorMove = proc;
-}
-
-void drgui_text_engine_refresh_markers(drgui_text_engine* pTL)
-{
- if (pTL == NULL || pTL->pRuns == NULL) {
- return;
- }
-
- // Cursor.
- drgui_text_run* pRun = pTL->pRuns + pTL->cursor.iRun;
- drgui_get_text_cursor_position_from_char(pRun->pFont, pTL->text + pRun->iChar, pTL->cursor.iChar, OUT &pTL->cursor.relativePosX);
-
- pRun = pTL->pRuns + pTL->selectionAnchor.iRun;
- drgui_get_text_cursor_position_from_char(pRun->pFont, pTL->text + pRun->iChar, pTL->selectionAnchor.iChar, OUT &pTL->selectionAnchor.relativePosX);
-}
-
-bool drgui_text_engine_insert_character(drgui_text_engine* pTL, unsigned int character, size_t insertIndex)
-{
- if (pTL == NULL) {
- return false;
- }
-
- // Transform '\r' to '\n'.
- if (character == '\r') {
- character = '\n';
- }
-
-
- // TODO: Add proper support for UTF-8.
- char* pOldText = pTL->text;
- char* pNewText = (char*)malloc(pTL->textLength + 1 + 1); // +1 for the new character and +1 for the null terminator.
-
- if (insertIndex > 0) {
- memcpy(pNewText, pOldText, insertIndex);
- }
-
- pNewText[insertIndex] = (char)character;
-
- if (insertIndex < pTL->textLength) {
- memcpy(pNewText + insertIndex + 1, pOldText + insertIndex, pTL->textLength - insertIndex);
- }
-
- pTL->textLength += 1;
- pTL->text = pNewText;
- pNewText[pTL->textLength] = '\0';
-
- free(pOldText);
-
-
-
-
- // The layout will have changed so it needs to be refreshed.
- drgui_text_engine__refresh(pTL);
-
- if (pTL->onTextChanged) {
- pTL->onTextChanged(pTL);
- }
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
-
- return true;
-}
-
-bool drgui_text_engine_insert_text(drgui_text_engine* pTL, const char* text, size_t insertIndex)
-{
- if (pTL == NULL || text == NULL) {
- return false;;
- }
-
- size_t newTextLength = strlen(text);
- if (newTextLength == 0) {
- return false;
- }
-
-
- // TODO: Add proper support for UTF-8.
- char* pOldText = pTL->text;
- char* pNewText = (char*)malloc(pTL->textLength + newTextLength + 1); // +1 for the new character and +1 for the null terminator.
-
- if (insertIndex > 0) {
- memcpy(pNewText, pOldText, insertIndex);
- }
-
-
- // Replace \r\n with \n.
- {
- char* dst = pNewText + insertIndex;
- const char* src = text;
- size_t srcLen = newTextLength;
- while (*src != '\0' && srcLen > 0)
- {
- if (*src != '\r') {
- *dst++ = *src;
- }
-
- src++;
- srcLen -= 1;
- }
-
- newTextLength = dst - (pNewText + insertIndex);
- }
-
- if (insertIndex < pTL->textLength) {
- memcpy(pNewText + insertIndex + newTextLength, pOldText + insertIndex, pTL->textLength - insertIndex);
- }
-
- pTL->textLength += newTextLength;
- pTL->text = pNewText;
- pNewText[pTL->textLength] = '\0';
-
- free(pOldText);
-
-
- // The layout will have changed so it needs to be refreshed.
- drgui_text_engine__refresh(pTL);
-
- if (pTL->onTextChanged) {
- pTL->onTextChanged(pTL);
- }
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
-
- return true;
-}
-
-bool drgui_text_engine_delete_text_range(drgui_text_engine* pTL, size_t iFirstCh, size_t iLastChPlus1)
-{
- if (pTL == NULL || iLastChPlus1 == iFirstCh) {
- return false;
- }
-
- if (iFirstCh > iLastChPlus1) {
- size_t temp = iFirstCh;
- iFirstCh = iLastChPlus1;
- iLastChPlus1 = temp;
- }
-
-
- size_t bytesToRemove = iLastChPlus1 - iFirstCh;
- if (bytesToRemove > 0)
- {
- memmove(pTL->text + iFirstCh, pTL->text + iLastChPlus1, pTL->textLength - iLastChPlus1);
- pTL->textLength -= bytesToRemove;
- pTL->text[pTL->textLength] = '\0';
-
- // The layout will have changed.
- drgui_text_engine__refresh(pTL);
-
- if (pTL->onTextChanged) {
- pTL->onTextChanged(pTL);
- }
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
-
- return true;
- }
-
- return false;
-}
-
-bool drgui_text_engine_insert_character_at_cursor(drgui_text_engine* pTL, unsigned int character)
-{
- if (pTL == NULL) {
- return false;
- }
-
- size_t iAbsoluteMarkerChar = 0;
-
- drgui_text_run* pRun = pTL->pRuns + pTL->cursor.iRun;
- if (pTL->runCount > 0 && pRun != NULL) {
- iAbsoluteMarkerChar = pRun->iChar + pTL->cursor.iChar;
- }
-
- drgui_text_engine__begin_dirty(pTL);
- {
- drgui_text_engine_insert_character(pTL, character, iAbsoluteMarkerChar);
- drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, iAbsoluteMarkerChar + 1);
- }
- drgui_text_engine__end_dirty(pTL);
-
-
- // The cursor's sticky position needs to be updated whenever the text is edited.
- drgui_text_engine__update_marker_sticky_position(pTL, &pTL->cursor);
-
-
- drgui_text_engine__on_cursor_move(pTL);
-
- return true;
-}
-
-bool drgui_text_engine_insert_text_at_cursor(drgui_text_engine* pTL, const char* text)
-{
- if (pTL == NULL || text == NULL) {
- return false;
- }
-
- drgui_text_engine__begin_dirty(pTL);
- {
- size_t cursorPos = drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor);
- drgui_text_engine_insert_text(pTL, text, cursorPos);
- drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, cursorPos + strlen(text));
- }
- drgui_text_engine__end_dirty(pTL);
-
-
- // The cursor's sticky position needs to be updated whenever the text is edited.
- drgui_text_engine__update_marker_sticky_position(pTL, &pTL->cursor);
-
- drgui_text_engine__on_cursor_move(pTL);
-
- return true;
-}
-
-bool drgui_text_engine_delete_character_to_left_of_cursor(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return false;
- }
-
- // We just move the cursor to the left, and then delete the character to the right.
- if (drgui_text_engine_move_cursor_left(pTL)) {
- drgui_text_engine_delete_character_to_right_of_cursor(pTL);
- return true;
- }
-
- return false;
-}
-
-bool drgui_text_engine_delete_character_to_right_of_cursor(drgui_text_engine* pTL)
-{
- if (pTL == NULL || pTL->runCount == 0) {
- return false;
- }
-
- drgui_text_run* pRun = pTL->pRuns + pTL->cursor.iRun;
- size_t iAbsoluteMarkerChar = pRun->iChar + pTL->cursor.iChar;
-
- if (iAbsoluteMarkerChar < pTL->textLength)
- {
- // TODO: Add proper support for UTF-8.
- memmove(pTL->text + iAbsoluteMarkerChar, pTL->text + iAbsoluteMarkerChar + 1, pTL->textLength - iAbsoluteMarkerChar);
- pTL->textLength -= 1;
- pTL->text[pTL->textLength] = '\0';
-
-
-
- // The layout will have changed.
- drgui_text_engine__refresh(pTL);
- drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, iAbsoluteMarkerChar);
-
- if (pTL->onTextChanged) {
- pTL->onTextChanged(pTL);
- }
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
-
- return true;
- }
-
- return false;
-}
-
-bool drgui_text_engine_delete_selected_text(drgui_text_engine* pTL)
-{
- // Don't do anything if nothing is selected.
- if (!drgui_text_engine_is_anything_selected(pTL)) {
- return false;
- }
-
- drgui_text_marker* pSelectionMarker0 = &pTL->selectionAnchor;
- drgui_text_marker* pSelectionMarker1 = &pTL->cursor;
- if (pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar > pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar)
- {
- drgui_text_marker* temp = pSelectionMarker0;
- pSelectionMarker0 = pSelectionMarker1;
- pSelectionMarker1 = temp;
- }
-
- size_t iSelectionChar0 = pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar;
- size_t iSelectionChar1 = pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar;
-
- drgui_text_engine__begin_dirty(pTL);
- bool wasTextChanged = drgui_text_engine_delete_text_range(pTL, iSelectionChar0, iSelectionChar1);
- if (wasTextChanged)
- {
- // The marker needs to be updated based on the new layout.
- drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, iSelectionChar0);
-
- // The cursor's sticky position also needs to be updated.
- drgui_text_engine__update_marker_sticky_position(pTL, &pTL->cursor);
-
- drgui_text_engine__on_cursor_move(pTL);
-
-
- // Reset the selection marker.
- pTL->selectionAnchor = pTL->cursor;
- pTL->isAnythingSelected = false;
- }
-
- drgui_text_engine__end_dirty(pTL);
- return wasTextChanged;
-}
-
-
-void drgui_text_engine_enter_selection_mode(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return;
- }
-
- // If we've just entered selection mode and nothing is currently selected, we want to set the selection anchor to the current cursor position.
- if (!drgui_text_engine_is_in_selection_mode(pTL) && !pTL->isAnythingSelected) {
- pTL->selectionAnchor = pTL->cursor;
- }
-
- pTL->selectionModeCounter += 1;
-}
-
-void drgui_text_engine_leave_selection_mode(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return;
- }
-
- if (pTL->selectionModeCounter > 0) {
- pTL->selectionModeCounter -= 1;
- }
-}
-
-bool drgui_text_engine_is_in_selection_mode(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return false;
- }
-
- return pTL->selectionModeCounter > 0;
-}
-
-bool drgui_text_engine_is_anything_selected(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return false;
- }
-
- return pTL->isAnythingSelected;
-}
-
-void drgui_text_engine_deselect_all(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->isAnythingSelected = false;
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
-}
-
-void drgui_text_engine_select_all(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return;
- }
-
- drgui_text_engine__move_marker_to_start_of_text(pTL, &pTL->selectionAnchor);
- drgui_text_engine__move_marker_to_end_of_text(pTL, &pTL->cursor);
-
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
-
- drgui_text_engine__on_cursor_move(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
-}
-
-void drgui_text_engine_select(drgui_text_engine* pTL, size_t firstCharacter, size_t lastCharacter)
-{
- if (pTL == NULL) {
- return;
- }
-
- drgui_text_engine__move_marker_to_character(pTL, &pTL->selectionAnchor, firstCharacter);
- drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, lastCharacter);
-
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
-
- drgui_text_engine__on_cursor_move(pTL);
- drgui_text_engine__on_dirty(pTL, drgui_text_engine__local_rect(pTL));
-}
-
-size_t drgui_text_engine_get_selected_text(drgui_text_engine* pTL, char* textOut, size_t textOutSize)
-{
- if (pTL == NULL || (textOut != NULL && textOutSize == 0)) {
- return 0;
- }
-
- if (!drgui_text_engine_is_anything_selected(pTL)) {
- return 0;
- }
-
-
- drgui_text_marker* pSelectionMarker0;
- drgui_text_marker* pSelectionMarker1;
- if (!drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1)) {
- return false;
- }
-
- size_t iSelectionChar0 = pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar;
- size_t iSelectionChar1 = pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar;
-
- size_t selectedTextLength = iSelectionChar1 - iSelectionChar0;
-
- if (textOut != NULL) {
- drgui__strncpy_s(textOut, textOutSize, pTL->text + iSelectionChar0, selectedTextLength);
- }
-
- return selectedTextLength;
-}
-
-size_t drgui_text_engine_get_selection_first_line(drgui_text_engine* pTL)
-{
- if (pTL == NULL || pTL->runCount == 0) {
- return 0;
- }
-
- drgui_text_marker* pSelectionMarker0;
- drgui_text_marker* pSelectionMarker1;
- if (!drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1)) {
- return 0;
- }
-
- return pTL->pRuns[pSelectionMarker0->iRun].iLine;
-}
-
-size_t drgui_text_engine_get_selection_last_line(drgui_text_engine* pTL)
-{
- if (pTL == NULL || pTL->runCount == 0) {
- return 0;
- }
-
- drgui_text_marker* pSelectionMarker0;
- drgui_text_marker* pSelectionMarker1;
- if (!drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1)) {
- return 0;
- }
-
- return pTL->pRuns[pSelectionMarker1->iRun].iLine;
-}
-
-void drgui_text_engine_move_selection_anchor_to_end_of_line(drgui_text_engine* pTL, size_t iLine)
-{
- if (pTL == NULL) {
- return;
- }
-
- drgui_text_engine__move_marker_to_end_of_line_by_index(pTL, &pTL->selectionAnchor, iLine);
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
-}
-
-void drgui_text_engine_move_selection_anchor_to_start_of_line(drgui_text_engine* pTL, size_t iLine)
-{
- if (pTL == NULL) {
- return;
- }
-
- drgui_text_engine__move_marker_to_start_of_line_by_index(pTL, &pTL->selectionAnchor, iLine);
- pTL->isAnythingSelected = drgui_text_engine__has_spacing_between_selection_markers(pTL);
-}
-
-size_t drgui_text_engine_get_selection_anchor_line(drgui_text_engine* pTL)
-{
- if (pTL == NULL || pTL->runCount == 0) {
- return 0;
- }
-
- return pTL->pRuns[pTL->selectionAnchor.iRun].iLine;
-}
-
-
-
-bool drgui_text_engine_prepare_undo_point(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return false;
- }
-
- // If we have a previously prepared state we'll need to clear it.
- if (pTL->preparedState.text != NULL) {
- free(pTL->preparedState.text);
- }
-
- pTL->preparedState.text = (char*)malloc(pTL->textLength + 1);
- drgui__strcpy_s(pTL->preparedState.text, pTL->textLength + 1, (pTL->text != NULL) ? pTL->text : "");
-
- pTL->preparedState.cursorPos = drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor);
- pTL->preparedState.selectionAnchorPos = drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->selectionAnchor);
- pTL->preparedState.isAnythingSelected = pTL->isAnythingSelected;
-
- return true;
-}
-
-bool drgui_text_engine_commit_undo_point(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return false;
- }
-
- // The undo point must have been prepared earlier.
- if (pTL->preparedState.text == NULL) {
- return false;
- }
-
-
- // The undo state is creating by diff-ing the prepared state and the current state.
- drgui_text_engine_state currentState;
- currentState.text = pTL->text;
- currentState.cursorPos = drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor);
- currentState.selectionAnchorPos = drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->selectionAnchor);
- currentState.isAnythingSelected = pTL->isAnythingSelected;
-
- drgui_text_engine_undo_state undoState;
- if (!drgui_text_engine__diff_states(&pTL->preparedState, ¤tState, &undoState)) {
- return false;
- }
-
-
- // At this point we have the undo state ready and we just need to add it the undo stack. Before doing so, however,
- // we need to trim the end fo the stack.
- drgui_text_engine__trim_undo_stack(pTL);
- drgui_text_engine__push_undo_state(pTL, &undoState);
-
- return true;
-}
-
-bool drgui_text_engine_undo(drgui_text_engine* pTL)
-{
- if (pTL == NULL || pTL->pUndoStack == NULL) {
- return false;
- }
-
- if (drgui_text_engine_get_undo_points_remaining_count(pTL) > 0)
- {
- drgui_text_engine_undo_state* pUndoState = pTL->pUndoStack + (pTL->iUndoState - 1);
- assert(pUndoState != NULL);
-
- drgui_text_engine__apply_undo_state(pTL, pUndoState);
- pTL->iUndoState -= 1;
-
- if (pTL->onUndoPointChanged) {
- pTL->onUndoPointChanged(pTL, pTL->iUndoState);
- }
-
- return true;
- }
-
- return false;
-}
-
-bool drgui_text_engine_redo(drgui_text_engine* pTL)
-{
- if (pTL == NULL || pTL->pUndoStack == NULL) {
- return false;
- }
-
- if (drgui_text_engine_get_redo_points_remaining_count(pTL) > 0)
- {
- drgui_text_engine_undo_state* pUndoState = pTL->pUndoStack + pTL->iUndoState;
- assert(pUndoState != NULL);
-
- drgui_text_engine__apply_redo_state(pTL, pUndoState);
- pTL->iUndoState += 1;
-
- if (pTL->onUndoPointChanged) {
- pTL->onUndoPointChanged(pTL, pTL->iUndoState);
- }
-
- return true;
- }
-
- return false;
-}
-
-unsigned int drgui_text_engine_get_undo_points_remaining_count(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return 0;
- }
-
- return pTL->iUndoState;
-}
-
-unsigned int drgui_text_engine_get_redo_points_remaining_count(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return 0;
- }
-
- if (pTL->undoStackCount > 0)
- {
- assert(pTL->iUndoState <= pTL->undoStackCount);
- return pTL->undoStackCount - pTL->iUndoState;
- }
-
- return 0;
-}
-
-void drgui_text_engine_clear_undo_stack(drgui_text_engine* pTL)
-{
- if (pTL == NULL || pTL->pUndoStack == NULL) {
- return;
- }
-
- for (unsigned int i = 0; i < pTL->undoStackCount; ++i) {
- drgui_text_engine__uninit_undo_state(pTL->pUndoStack + i);
- }
-
- free(pTL->pUndoStack);
-
- pTL->pUndoStack = NULL;
- pTL->undoStackCount = 0;
-
- if (pTL->iUndoState > 0) {
- pTL->iUndoState = 0;
-
- if (pTL->onUndoPointChanged) {
- pTL->onUndoPointChanged(pTL, pTL->iUndoState);
- }
- }
-}
-
-
-
-size_t drgui_text_engine_get_line_count(drgui_text_engine* pTL)
-{
- if (pTL == NULL || pTL->runCount == 0) {
- return 0;
- }
-
- return pTL->pRuns[pTL->runCount - 1].iLine + 1;
-}
-
-size_t drgui_text_engine_get_visible_line_count_starting_at(drgui_text_engine* pTL, size_t iFirstLine)
-{
- if (pTL == NULL || pTL->runCount == 0) {
- return 0;
- }
-
- unsigned int count = 0;
- float lastLineBottom = 0;
-
- // First thing we do is find the first line.
- unsigned int iLine = 0;
- drgui_text_engine_line line;
- if (drgui_text_engine__first_line(pTL, &line))
- {
- do
- {
- if (iLine >= iFirstLine) {
- break;
- }
-
- iLine += 1;
- } while (drgui_text_engine__next_line(pTL, &line));
-
-
- // At this point we are at the first line and we need to start counting.
- do
- {
- if (line.posY + pTL->innerOffsetY >= pTL->containerHeight) {
- break;
- }
-
- count += 1;
- lastLineBottom = line.posY + line.height;
-
- } while (drgui_text_engine__next_line(pTL, &line));
- }
-
-
- // At this point there may be some empty space below the last line, in which case we use the line height of the default font to fill
- // out the remaining space.
- if (lastLineBottom + pTL->innerOffsetY < pTL->containerHeight)
- {
- drgui_font_metrics defaultFontMetrics;
- if (drgui_get_font_metrics(pTL->pDefaultFont, &defaultFontMetrics)) {
- count += (unsigned int)((pTL->containerHeight - (lastLineBottom + pTL->innerOffsetY)) / defaultFontMetrics.lineHeight);
- }
- }
-
-
-
- if (count == 0) {
- return 1;
- }
-
- return count;
-}
-
-float drgui_text_engine_get_line_pos_y(drgui_text_engine* pTL, size_t iLine)
-{
- drgui_rect lineRect;
- if (!drgui_text_engine__find_line_info_by_index(pTL, iLine, &lineRect, NULL, NULL)) {
- return 0;
- }
-
- return lineRect.top;
-}
-
-size_t drgui_text_engine_get_line_at_pos_y(drgui_text_engine* pTL, float posY)
-{
- if (pTL == NULL || pTL->runCount == 0) {
- return 0;
- }
-
- drgui_rect textRect = drgui_text_engine_get_text_rect_relative_to_bounds(pTL);
-
- size_t iRun;
-
- float inputPosYRelativeToText = posY - textRect.top;
- if (!drgui_text_engine__find_closest_run_to_point(pTL, 0, inputPosYRelativeToText, &iRun)) {
- return 0;
- }
-
- return pTL->pRuns[iRun].iLine;
-}
-
-size_t drgui_text_engine_get_line_first_character(drgui_text_engine* pTL, size_t iLine)
-{
- if (pTL == NULL || pTL->runCount == 0) {
- return 0;
- }
-
- size_t firstRunIndex0;
- size_t lastRunIndexPlus1;
- if (drgui_text_engine__find_line_info_by_index(pTL, iLine, NULL, &firstRunIndex0, &lastRunIndexPlus1)) {
- return pTL->pRuns[firstRunIndex0].iChar;
- }
-
- return 0;
-}
-
-size_t drgui_text_engine_get_line_last_character(drgui_text_engine* pTL, size_t iLine)
-{
- if (pTL == NULL || pTL->runCount == 0) {
- return 0;
- }
-
- size_t firstRunIndex0;
- size_t lastRunIndexPlus1;
- if (drgui_text_engine__find_line_info_by_index(pTL, iLine, NULL, &firstRunIndex0, &lastRunIndexPlus1)) {
- size_t charEnd = pTL->pRuns[lastRunIndexPlus1 - 1].iCharEnd;
- if (charEnd > 0) {
- charEnd -= 1;
- }
-
- return charEnd;
- }
-
- return 0;
-}
-
-void drgui_text_engine_get_line_character_range(drgui_text_engine* pTL, size_t iLine, size_t* pCharStartOut, size_t* pCharEndOut)
-{
- if (pTL == NULL || pTL->runCount == 0) {
- return;
- }
-
- size_t charStart = 0;
- size_t charEnd = 0;
-
- size_t firstRunIndex0;
- size_t lastRunIndexPlus1;
- if (drgui_text_engine__find_line_info_by_index(pTL, iLine, NULL, &firstRunIndex0, &lastRunIndexPlus1)) {
- charStart = pTL->pRuns[firstRunIndex0].iChar;
- charEnd = pTL->pRuns[lastRunIndexPlus1 - 1].iCharEnd;
- if (charEnd > 0) {
- charEnd -= 1;
- }
- }
-
- if (pCharStartOut) {
- *pCharStartOut = charStart;
- }
- if (pCharEndOut) {
- *pCharEndOut = charEnd;
- }
-}
-
-
-void drgui_text_engine_set_on_paint_text(drgui_text_engine* pTL, drgui_text_engine_on_paint_text_proc proc)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->onPaintText = proc;
-}
-
-void drgui_text_engine_set_on_paint_rect(drgui_text_engine* pTL, drgui_text_engine_on_paint_rect_proc proc)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->onPaintRect = proc;
-}
-
-void drgui_text_engine_paint(drgui_text_engine* pTL, drgui_rect rect, drgui_element* pElement, void* pPaintData)
-{
- if (pTL == NULL || pTL->onPaintText == NULL || pTL->onPaintRect == NULL) {
- return;
- }
-
- if (rect.left < 0) {
- rect.left = 0;
- }
- if (rect.top < 0) {
- rect.top = 0;
- }
- if (rect.right > pTL->containerWidth) {
- rect.right = pTL->containerWidth;
- }
- if (rect.bottom > pTL->containerHeight) {
- rect.bottom = pTL->containerHeight;
- }
-
- if (rect.right <= rect.left || rect.bottom <= rect.top) {
- return;
- }
-
-
- // The position of each run will be relative to the text bounds. We want to make it relative to the container bounds.
- drgui_rect textRect = drgui_text_engine_get_text_rect_relative_to_bounds(pTL);
-
- // We draw a rectangle above and below the text rectangle. The main text rectangle will be drawn by iterating over each visible run.
- drgui_rect rectTop = drgui_make_rect(0, 0, pTL->containerWidth, textRect.top);
- drgui_rect rectBottom = drgui_make_rect(0, textRect.bottom, pTL->containerWidth, pTL->containerHeight);
-
- if (rectTop.bottom > rect.top) {
- pTL->onPaintRect(pTL, rectTop, pTL->defaultBackgroundColor, pElement, pPaintData);
- }
-
- if (rectBottom.top < rect.bottom) {
- pTL->onPaintRect(pTL, rectBottom, pTL->defaultBackgroundColor, pElement, pPaintData);
- }
-
-
- // We draw line-by-line, starting from the first visible line.
- drgui_text_engine_line line;
- if (drgui_text_engine__first_line(pTL, &line))
- {
- do
- {
- float lineTop = line.posY + textRect.top;
- float lineBottom = lineTop + line.height;
-
- if (lineTop < rect.bottom)
- {
- if (lineBottom > rect.top)
- {
- // The line is visible. We draw in 3 main parts - 1) the blank space to the left of the first run; 2) the runs themselves; 3) the blank
- // space to the right of the last run.
-
- drgui_color bgcolor = pTL->defaultBackgroundColor;
- if (line.index == drgui_text_engine_get_cursor_line(pTL)) {
- bgcolor = pTL->lineBackgroundColor;
- }
-
- float lineSelectionOverhangLeft = 0;
- float lineSelectionOverhangRight = 0;
-
- if (drgui_text_engine_is_anything_selected(pTL))
- {
- drgui_text_marker* pSelectionMarker0 = &pTL->selectionAnchor;
- drgui_text_marker* pSelectionMarker1 = &pTL->cursor;
- if (pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar > pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar)
- {
- drgui_text_marker* temp = pSelectionMarker0;
- pSelectionMarker0 = pSelectionMarker1;
- pSelectionMarker1 = temp;
- }
-
- size_t iSelectionLine0 = pTL->pRuns[pSelectionMarker0->iRun].iLine;
- size_t iSelectionLine1 = pTL->pRuns[pSelectionMarker1->iRun].iLine;
-
- if (line.index >= iSelectionLine0 && line.index < iSelectionLine1)
- {
- drgui_font_metrics defaultFontMetrics;
- drgui_get_font_metrics(pTL->pDefaultFont, &defaultFontMetrics);
-
- if (pTL->horzAlign == drgui_text_engine_alignment_right)
- {
- if (line.index > iSelectionLine0) {
- lineSelectionOverhangLeft = (float)defaultFontMetrics.spaceWidth;
- }
- }
- else if (pTL->horzAlign == drgui_text_engine_alignment_center)
- {
- lineSelectionOverhangRight = (float)defaultFontMetrics.spaceWidth;
-
- if (line.index > iSelectionLine0) {
- lineSelectionOverhangLeft = (float)defaultFontMetrics.spaceWidth;
- }
- }
- else
- {
- lineSelectionOverhangRight = (float)defaultFontMetrics.spaceWidth;
- }
- }
- }
-
-
- drgui_text_run* pFirstRun = pTL->pRuns + line.iFirstRun;
- drgui_text_run* pLastRun = pTL->pRuns + line.iLastRun;
-
- float lineLeft = pFirstRun->posX + textRect.left;
- float lineRight = pLastRun->posX + pLastRun->width + textRect.left;
-
- // 1) The blank space to the left of the first run.
- if (lineLeft > 0)
- {
- if (lineSelectionOverhangLeft > 0) {
- pTL->onPaintRect(pTL, drgui_make_rect(lineLeft - lineSelectionOverhangLeft, lineTop, lineLeft, lineBottom), pTL->selectionBackgroundColor, pElement, pPaintData);
- }
-
- pTL->onPaintRect(pTL, drgui_make_rect(0, lineTop, lineLeft - lineSelectionOverhangLeft, lineBottom), bgcolor, pElement, pPaintData);
- }
-
-
- // 2) The runs themselves.
- for (size_t iRun = line.iFirstRun; iRun <= line.iLastRun; ++iRun)
- {
- drgui_text_run* pRun = pTL->pRuns + iRun;
-
- float runLeft = pRun->posX + textRect.left;
- float runRight = runLeft + pRun->width;
-
- if (runRight > 0 && runLeft < pTL->containerWidth)
- {
- // The run is visible.
- if (!drgui_text_engine__is_text_run_whitespace(pTL, pRun) || pTL->text[pRun->iChar] == '\t')
- {
- drgui_text_run run = pTL->pRuns[iRun];
- run.pFont = pTL->pDefaultFont;
- run.textColor = pTL->defaultTextColor;
- run.backgroundColor = bgcolor;
- run.text = pTL->text + run.iChar;
- run.posX = runLeft;
- run.posY = lineTop;
-
- // We paint the run differently depending on whether or not anything is selected. If something is selected
- // we need to split the run into a maximum of 3 sub-runs so that the selection rectangle can be drawn correctly.
- if (drgui_text_engine_is_anything_selected(pTL))
- {
- drgui_text_run subruns[3];
- size_t subrunCount = drgui_text_engine__split_text_run_by_selection(pTL, &run, subruns);
- for (size_t iSubRun = 0; iSubRun < subrunCount; ++iSubRun)
- {
- drgui_text_run* pSubRun = subruns + iSubRun;
-
- if (!drgui_text_engine__is_text_run_whitespace(pTL, pRun)) {
- pTL->onPaintText(pTL, pSubRun, pElement, pPaintData);
- } else {
- pTL->onPaintRect(pTL, drgui_make_rect(pSubRun->posX, lineTop, pSubRun->posX + pSubRun->width, lineBottom), pSubRun->backgroundColor, pElement, pPaintData);
- }
- }
- }
- else
- {
- // Nothing is selected.
- if (!drgui_text_engine__is_text_run_whitespace(pTL, &run)) {
- pTL->onPaintText(pTL, &run, pElement, pPaintData);
- } else {
- pTL->onPaintRect(pTL, drgui_make_rect(run.posX, lineTop, run.posX + run.width, lineBottom), run.backgroundColor, pElement, pPaintData);
- }
- }
- }
- }
- }
-
-
- // 3) The blank space to the right of the last run.
- if (lineRight < pTL->containerWidth)
- {
- if (lineSelectionOverhangRight > 0) {
- pTL->onPaintRect(pTL, drgui_make_rect(lineRight, lineTop, lineRight + lineSelectionOverhangRight, lineBottom), pTL->selectionBackgroundColor, pElement, pPaintData);
- }
-
- pTL->onPaintRect(pTL, drgui_make_rect(lineRight + lineSelectionOverhangRight, lineTop, pTL->containerWidth, lineBottom), bgcolor, pElement, pPaintData);
- }
- }
- }
- else
- {
- // The line is below the rectangle which means no other line will be visible and we can terminate early.
- break;
- }
-
- } while (drgui_text_engine__next_line(pTL, &line));
- }
- else
- {
- // There are no lines so we do a simplified branch here.
- float lineTop = textRect.top;
- float lineBottom = textRect.bottom;
- pTL->onPaintRect(pTL, drgui_make_rect(0, lineTop, pTL->containerWidth, lineBottom), pTL->lineBackgroundColor, pElement, pPaintData);
- }
-
- // The cursor.
- if (pTL->isShowingCursor && pTL->isCursorBlinkOn) {
- pTL->onPaintRect(pTL, drgui_text_engine_get_cursor_rect(pTL), pTL->cursorColor, pElement, pPaintData);
- }
-}
-
-
-void drgui_text_engine_step(drgui_text_engine* pTL, unsigned int milliseconds)
-{
- if (pTL == NULL || milliseconds == 0) {
- return;
- }
-
- if (pTL->timeToNextCursorBlink < milliseconds)
- {
- pTL->isCursorBlinkOn = !pTL->isCursorBlinkOn;
- pTL->timeToNextCursorBlink = pTL->cursorBlinkRate;
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine_get_cursor_rect(pTL));
- }
- else
- {
- pTL->timeToNextCursorBlink -= milliseconds;
- }
-}
-
-
-
-void drgui_text_engine_paint_line_numbers(drgui_text_engine* pTL, float lineNumbersWidth, float lineNumbersHeight, drgui_color textColor, drgui_color backgroundColor, drgui_text_engine_on_paint_text_proc onPaintText, drgui_text_engine_on_paint_rect_proc onPaintRect, drgui_element* pElement, void* pPaintData)
-{
- if (pTL == NULL || onPaintText == NULL || onPaintRect == NULL) {
- return;
- }
-
-
- // The position of each run will be relative to the text bounds. We want to make it relative to the container bounds.
- drgui_rect textRect = drgui_text_engine_get_text_rect_relative_to_bounds(pTL);
-
- // We draw a rectangle above and below the text rectangle. The main text rectangle will be drawn by iterating over each visible run.
- drgui_rect rectTop = drgui_make_rect(0, 0, lineNumbersWidth, textRect.top);
- drgui_rect rectBottom = drgui_make_rect(0, textRect.bottom, lineNumbersWidth, lineNumbersHeight);
-
- if (pTL->onPaintRect)
- {
- if (rectTop.bottom > 0) {
- onPaintRect(pTL, rectTop, backgroundColor, pElement, pPaintData);
- }
-
- if (rectBottom.top < lineNumbersHeight) {
- onPaintRect(pTL, rectBottom, backgroundColor, pElement, pPaintData);
- }
- }
-
-
- // Now we draw each line.
- int iLine = 1;
- drgui_text_engine_line line;
- if (!drgui_text_engine__first_line(pTL, &line))
- {
- // We failed to retrieve the first line which is probably due to the text engine being empty. We just fake the first line to
- // ensure we get the number 1 to be drawn.
- drgui_font_metrics fontMetrics;
- drgui_get_font_metrics(pTL->pDefaultFont, &fontMetrics);
-
- line.height = (float)fontMetrics.lineHeight;
- line.posY = 0;
- }
-
- do
- {
- float lineTop = line.posY + textRect.top;
- float lineBottom = lineTop + line.height;
-
- if (lineTop < lineNumbersHeight)
- {
- if (lineBottom > 0)
- {
- char iLineStr[64];
- #ifdef _MSC_VER
- _itoa_s(iLine, iLineStr, sizeof(iLineStr), 10);
- #else
- snprintf(iLineStr, sizeof(iLineStr), "%d", iLine);
- #endif
-
- drgui_font* pFont = pTL->pDefaultFont;
-
- float textWidth;
- float textHeight;
- drgui_measure_string(pFont, iLineStr, strlen(iLineStr), &textWidth, &textHeight);
-
- drgui_text_run run = {0};
- run.pFont = pFont;
- run.textColor = textColor;
- run.backgroundColor = backgroundColor;
- run.text = iLineStr;
- run.textLength = strlen(iLineStr);
- run.posX = lineNumbersWidth - textWidth;
- run.posY = lineTop;
- onPaintText(pTL, &run, pElement, pPaintData);
- onPaintRect(pTL, drgui_make_rect(0, lineTop, run.posX, lineBottom), run.backgroundColor, pElement, pPaintData);
- }
- }
- else
- {
- // The line is below the rectangle which means no other line will be visible and we can terminate early.
- break;
- }
-
- iLine += 1;
- } while (drgui_text_engine__next_line(pTL, &line));
-}
-
-
-bool drgui_text_engine_find_next(drgui_text_engine* pTL, const char* text, size_t* pSelectionStartOut, size_t* pSelectionEndOut)
-{
- if (pTL == NULL || pTL->text == NULL || text == NULL || text[0] == '\0') {
- return false;
- }
-
- size_t cursorPos = drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor);
- char* nextOccurance = strstr(pTL->text + cursorPos, text);
- if (nextOccurance == NULL) {
- nextOccurance = strstr(pTL->text, text);
- }
-
- if (nextOccurance == NULL) {
- return false;
- }
-
- if (pSelectionStartOut) {
- *pSelectionStartOut = nextOccurance - pTL->text;
- }
- if (pSelectionEndOut) {
- *pSelectionEndOut = (nextOccurance - pTL->text) + strlen(text);
- }
-
- return true;
-}
-
-bool drgui_text_engine_find_next_no_loop(drgui_text_engine* pTL, const char* text, size_t* pSelectionStartOut, size_t* pSelectionEndOut)
-{
- if (pTL == NULL || pTL->text == NULL || text == NULL || text[0] == '\0') {
- return false;
- }
-
- size_t cursorPos = drgui_text_engine__get_marker_absolute_char_index(pTL, &pTL->cursor);
-
- char* nextOccurance = strstr(pTL->text + cursorPos, text);
- if (nextOccurance == NULL) {
- return false;
- }
-
- if (pSelectionStartOut) {
- *pSelectionStartOut = nextOccurance - pTL->text;
- }
- if (pSelectionEndOut) {
- *pSelectionEndOut = (nextOccurance - pTL->text) + strlen(text);
- }
-
- return true;
-}
-
-
-
-
-DRGUI_PRIVATE bool drgui_next_run_string(const char* runStart, const char* textEndPastNullTerminator, const char** pRunEndOut)
-{
- assert(runStart <= textEndPastNullTerminator);
-
- if (runStart == NULL || runStart == textEndPastNullTerminator)
- {
- // String is empty.
- return false;
- }
-
-
- char firstChar = runStart[0];
- if (firstChar == '\t')
- {
- // We loop until we hit anything that is not a tab character (tabs will be grouped together into a single run).
- do
- {
- runStart += 1;
- *pRunEndOut = runStart;
- } while (runStart[0] != '\0' && runStart[0] == '\t');
- }
- else if (firstChar == '\n')
- {
- runStart += 1;
- *pRunEndOut = runStart;
- }
- else if (firstChar == '\0')
- {
- assert(runStart + 1 == textEndPastNullTerminator);
- *pRunEndOut = textEndPastNullTerminator;
- }
- else
- {
- do
- {
- runStart += 1;
- *pRunEndOut = runStart;
- } while (runStart[0] != '\0' && runStart[0] != '\t' && runStart[0] != '\n');
- }
-
- return true;
-}
-
-DRGUI_PRIVATE void drgui_text_engine__refresh(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return;
- }
-
- // We split the runs based on tabs and new-lines. We want to create runs for tabs and new-line characters as well because we want
- // to have the entire string covered by runs for the sake of simplicity when it comes to editing.
- //
- // The first pass positions the runs based on a top-to-bottom, left-to-right alignment. The second pass then repositions the runs
- // based on alignment.
-
- // Runs need to be cleared first.
- drgui_text_engine__clear_text_runs(pTL);
-
- // The text bounds also need to be reset at the top.
- pTL->textBoundsWidth = 0;
- pTL->textBoundsHeight = 0;
-
- drgui_font_metrics defaultFontMetrics;
- drgui_get_font_metrics(pTL->pDefaultFont, &defaultFontMetrics);
-
- pTL->textBoundsHeight = (float)defaultFontMetrics.lineHeight;
-
- const float tabWidth = drgui_text_engine__get_tab_width(pTL);
-
- size_t iCurrentLine = 0;
- float runningPosY = 0;
- float runningLineHeight = 0;
-
- const char* nextRunStart = pTL->text;
- const char* nextRunEnd;
- while (drgui_next_run_string(nextRunStart, pTL->text + pTL->textLength + 1, OUT &nextRunEnd))
- {
- drgui_text_run run;
- run.iLine = iCurrentLine;
- run.iChar = nextRunStart - pTL->text;
- run.iCharEnd = nextRunEnd - pTL->text;
- run.textLength = nextRunEnd - nextRunStart;
- run.width = 0;
- run.height = 0;
- run.posX = 0;
- run.posY = runningPosY;
- run.pFont = pTL->pDefaultFont;
-
- // X position
- //
- // The x position depends on the previous run that's on the same line.
- if (pTL->runCount > 0)
- {
- drgui_text_run* pPrevRun = pTL->pRuns + (pTL->runCount - 1);
- if (pPrevRun->iLine == iCurrentLine)
- {
- run.posX = pPrevRun->posX + pPrevRun->width;
- }
- else
- {
- // It's the first run on the line.
- run.posX = 0;
- }
- }
-
-
- // Width and height.
- assert(nextRunEnd > nextRunStart);
- if (nextRunStart[0] == '\t')
- {
- // Tab.
- size_t tabCount = run.iCharEnd - run.iChar;
- run.width = (float)(((tabCount*(size_t)tabWidth) - ((size_t)run.posX % (size_t)tabWidth)));
- run.height = (float)defaultFontMetrics.lineHeight;
- }
- else if (nextRunStart[0] == '\n')
- {
- // New line.
- iCurrentLine += 1;
- run.width = 0;
- run.height = (float)defaultFontMetrics.lineHeight;
- }
- else if (nextRunStart[0] == '\0')
- {
- // Null terminator.
- run.width = 0;
- run.height = (float)defaultFontMetrics.lineHeight;
- run.textLength = 0;
- }
- else
- {
- // Normal run.
- drgui_measure_string(pTL->pDefaultFont, nextRunStart, run.textLength, &run.width, &run.height);
- }
-
-
- // The running line height needs to be updated by setting to the maximum size.
- runningLineHeight = (run.height > runningLineHeight) ? run.height : runningLineHeight;
-
-
- // Update the text bounds.
- if (pTL->textBoundsWidth < run.posX + run.width) {
- pTL->textBoundsWidth = run.posX + run.width;
- }
- pTL->textBoundsHeight = runningPosY + runningLineHeight;
-
-
- // A new line means we need to increment the running y position by the running line height.
- if (nextRunStart[0] == '\n')
- {
- runningPosY += runningLineHeight;
- runningLineHeight = 0;
- }
-
- // Add the run to the internal list.
- drgui_text_engine__push_text_run(pTL, &run);
-
- // Go to the next run string.
- nextRunStart = nextRunEnd;
- }
-
- // If we were to return now the text would be alignment top/left. If the alignment is not top/left we need to refresh the layout.
- if (pTL->horzAlign != drgui_text_engine_alignment_left || pTL->vertAlign != drgui_text_engine_alignment_top)
- {
- drgui_text_engine__refresh_alignment(pTL);
- }
-}
-
-DRGUI_PRIVATE void drgui_text_engine__refresh_alignment(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return;
- }
-
- float runningPosY = 0;
-
- unsigned int iCurrentLine = 0;
- for (size_t iRun = 0; iRun < pTL->runCount; /* Do Nothing*/) // iRun is incremented from within the loop.
- {
- float lineWidth = 0;
- float lineHeight = 0;
-
- // This loop does a few things. First, it defines the end point for the loop after this one (jRun). Second, it calculates
- // the line width which is needed for center and right alignment. Third it resets the position of each run to their
- // unaligned equivalents which will be offsetted in the second loop.
- size_t jRun;
- for (jRun = iRun; jRun < pTL->runCount && pTL->pRuns[jRun].iLine == iCurrentLine; ++jRun)
- {
- drgui_text_run* pRun = pTL->pRuns + jRun;
- pRun->posX = lineWidth;
- pRun->posY = runningPosY;
-
- lineWidth += pRun->width;
- lineHeight = (lineHeight > pRun->height) ? lineHeight : pRun->height;
- }
-
-
- // The actual alignment is done here.
- float lineOffsetX;
- float lineOffsetY;
- drgui_text_engine__calculate_line_alignment_offset(pTL, lineWidth, OUT &lineOffsetX, OUT &lineOffsetY);
-
- for (/* Do Nothing*/; iRun < jRun; ++iRun)
- {
- drgui_text_run* pRun = pTL->pRuns + iRun;
- pRun->posX += lineOffsetX;
- pRun->posY += lineOffsetY;
- }
-
-
- // Go to the next line.
- iCurrentLine += 1;
- runningPosY += lineHeight;
- }
-}
-
-void drgui_text_engine__calculate_line_alignment_offset(drgui_text_engine* pTL, float lineWidth, float* pOffsetXOut, float* pOffsetYOut)
-{
- if (pTL == NULL) {
- return;
- }
-
- float offsetX = 0;
- float offsetY = 0;
-
- switch (pTL->horzAlign)
- {
- case drgui_text_engine_alignment_right:
- {
- offsetX = pTL->textBoundsWidth - lineWidth;
- break;
- }
-
- case drgui_text_engine_alignment_center:
- {
- offsetX = (pTL->textBoundsWidth - lineWidth) / 2;
- break;
- }
-
- case drgui_text_engine_alignment_left:
- case drgui_text_engine_alignment_top: // Invalid for horizontal alignment.
- case drgui_text_engine_alignment_bottom: // Invalid for horizontal alignment.
- default:
- {
- offsetX = 0;
- break;
- }
- }
-
-
- switch (pTL->vertAlign)
- {
- case drgui_text_engine_alignment_bottom:
- {
- offsetY = pTL->textBoundsHeight - pTL->textBoundsHeight;
- break;
- }
-
- case drgui_text_engine_alignment_center:
- {
- offsetY = (pTL->textBoundsHeight - pTL->textBoundsHeight) / 2;
- break;
- }
-
- case drgui_text_engine_alignment_top:
- case drgui_text_engine_alignment_left: // Invalid for vertical alignment.
- case drgui_text_engine_alignment_right: // Invalid for vertical alignment.
- default:
- {
- offsetY = 0;
- break;
- }
- }
-
-
- if (pOffsetXOut) {
- *pOffsetXOut = offsetX;
- }
- if (pOffsetYOut) {
- *pOffsetYOut = offsetY;
- }
-}
-
-
-DRGUI_PRIVATE void drgui_text_engine__push_text_run(drgui_text_engine* pTL, drgui_text_run* pRun)
-{
- if (pTL == NULL) {
- return;
- }
-
- if (pTL->runBufferSize == pTL->runCount)
- {
- pTL->runBufferSize = pTL->runBufferSize*2;
- if (pTL->runBufferSize == 0) {
- pTL->runBufferSize = 1;
- }
-
- pTL->pRuns = (drgui_text_run*)realloc(pTL->pRuns, sizeof(drgui_text_run) * pTL->runBufferSize);
- if (pTL->pRuns == NULL) {
- pTL->runCount = 0;
- pTL->runBufferSize = 0;
- return;
- }
- }
-
- pTL->pRuns[pTL->runCount] = *pRun;
- pTL->runCount += 1;
-}
-
-DRGUI_PRIVATE void drgui_text_engine__clear_text_runs(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->runCount = 0;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__is_text_run_whitespace(drgui_text_engine* pTL, drgui_text_run* pRun)
-{
- if (pRun == NULL) {
- return false;
- }
-
- if (pTL->text[pRun->iChar] != '\t' && pTL->text[pRun->iChar] != '\n')
- {
- return false;
- }
-
- return true;
-}
-
-DRGUI_PRIVATE float drgui_text_engine__get_tab_width(drgui_text_engine* pTL)
-{
- drgui_font_metrics defaultFontMetrics;
- drgui_get_font_metrics(pTL->pDefaultFont, &defaultFontMetrics);
-
- return (float)(defaultFontMetrics.spaceWidth * pTL->tabSizeInSpaces);
-}
-
-
-DRGUI_PRIVATE bool drgui_text_engine__find_closest_line_to_point(drgui_text_engine* pTL, float inputPosYRelativeToText, size_t* pFirstRunIndexOnLineOut, size_t* pLastRunIndexOnLinePlus1Out)
-{
- size_t iFirstRunOnLine = 0;
- size_t iLastRunOnLinePlus1 = 0;
-
- bool result = true;
- if (pTL == NULL || pTL->runCount == 0)
- {
- result = false;
- }
- else
- {
- float runningLineTop = 0;
-
- float lineHeight;
- while (drgui_text_engine__find_line_info(pTL, iFirstRunOnLine, OUT &iLastRunOnLinePlus1, OUT &lineHeight))
- {
- const float lineTop = runningLineTop;
- const float lineBottom = lineTop + lineHeight;
-
- if (inputPosYRelativeToText < lineBottom)
- {
- // It's on this line.
- break;
- }
- else
- {
- // It's not on this line - go to the next one, unless we're already on the last line.
- if (iLastRunOnLinePlus1 == pTL->runCount) {
- break;
- }
-
- iFirstRunOnLine = iLastRunOnLinePlus1;
- runningLineTop = lineBottom;
- }
- }
- }
-
- if (pFirstRunIndexOnLineOut) {
- *pFirstRunIndexOnLineOut = iFirstRunOnLine;
- }
- if (pLastRunIndexOnLinePlus1Out) {
- *pLastRunIndexOnLinePlus1Out = iLastRunOnLinePlus1;
- }
-
- return result;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__find_closest_run_to_point(drgui_text_engine* pTL, float inputPosXRelativeToText, float inputPosYRelativeToText, size_t* pRunIndexOut)
-{
- if (pTL == NULL) {
- return false;
- }
-
- size_t iFirstRunOnLine;
- size_t iLastRunOnLinePlus1;
- if (drgui_text_engine__find_closest_line_to_point(pTL, inputPosYRelativeToText, OUT &iFirstRunOnLine, OUT &iLastRunOnLinePlus1))
- {
- size_t iRunOut = 0;
-
- const drgui_text_run* pFirstRunOnLine = pTL->pRuns + iFirstRunOnLine;
- const drgui_text_run* pLastRunOnLine = pTL->pRuns + (iLastRunOnLinePlus1 - 1);
-
- if (inputPosXRelativeToText < pFirstRunOnLine->posX)
- {
- // It's to the left of the first run.
- iRunOut = iFirstRunOnLine;
- }
- else if (inputPosXRelativeToText > pLastRunOnLine->posX + pLastRunOnLine->width)
- {
- // It's to the right of the last run.
- iRunOut = iLastRunOnLinePlus1 - 1;
- }
- else
- {
- // It's in the middle of the line. We just iterate over each run on the line and return the first one that contains the point.
- for (size_t iRun = iFirstRunOnLine; iRun < iLastRunOnLinePlus1; ++iRun)
- {
- const drgui_text_run* pRun = pTL->pRuns + iRun;
- iRunOut = iRun;
-
- if (inputPosXRelativeToText >= pRun->posX && inputPosXRelativeToText <= pRun->posX + pRun->width) {
- break;
- }
- }
- }
-
- if (pRunIndexOut) {
- *pRunIndexOut = iRunOut;
- }
-
- return true;
- }
- else
- {
- // There was an error finding the closest line.
- return false;
- }
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__find_line_info(drgui_text_engine* pTL, size_t iFirstRunOnLine, size_t* pLastRunIndexOnLinePlus1Out, float* pLineHeightOut)
-{
- if (pTL == NULL) {
- return false;
- }
-
- if (iFirstRunOnLine < pTL->runCount)
- {
- const size_t iLine = pTL->pRuns[iFirstRunOnLine].iLine;
- float lineHeight = 0;
-
- size_t iRun;
- for (iRun = iFirstRunOnLine; iRun < pTL->runCount && pTL->pRuns[iRun].iLine == iLine; ++iRun)
- {
- if (lineHeight < pTL->pRuns[iRun].height) {
- lineHeight = pTL->pRuns[iRun].height;
- }
- }
-
- assert(iRun > iFirstRunOnLine);
-
- if (pLastRunIndexOnLinePlus1Out) {
- *pLastRunIndexOnLinePlus1Out = iRun;
- }
- if (pLineHeightOut) {
- *pLineHeightOut = lineHeight;
- }
-
- return true;
- }
-
- return false;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__find_line_info_by_index(drgui_text_engine* pTL, size_t iLine, drgui_rect* pRectOut, size_t* pFirstRunIndexOut, size_t* pLastRunIndexPlus1Out)
-{
- if (pTL == NULL) {
- return false;
- }
-
- size_t iFirstRunOnLine = 0;
- size_t iLastRunOnLinePlus1 = 0;
-
- float lineTop = 0;
- float lineHeight = 0;
-
- for (size_t iCurrentLine = 0; iCurrentLine <= iLine; ++iCurrentLine)
- {
- iFirstRunOnLine = iLastRunOnLinePlus1;
- lineTop += lineHeight;
-
- if (!drgui_text_engine__find_line_info(pTL, iFirstRunOnLine, &iLastRunOnLinePlus1, &lineHeight))
- {
- // There was an error retrieving information about the line.
- return false;
- }
- }
-
-
- // At this point we have the first and last runs that make up the line and we can generate our output.
- if (iLastRunOnLinePlus1 > iFirstRunOnLine)
- {
- if (pFirstRunIndexOut) {
- *pFirstRunIndexOut = iFirstRunOnLine;
- }
- if (pLastRunIndexPlus1Out) {
- *pLastRunIndexPlus1Out = iLastRunOnLinePlus1;
- }
-
- if (pRectOut != NULL)
- {
- pRectOut->left = pTL->pRuns[iFirstRunOnLine].posX;
- pRectOut->right = pTL->pRuns[iLastRunOnLinePlus1 - 1].posX + pTL->pRuns[iLastRunOnLinePlus1 - 1].width;
- pRectOut->top = lineTop;
- pRectOut->bottom = pRectOut->top + lineHeight;
- }
-
- return true;
- }
- else
- {
- // We couldn't find any runs.
- return false;
- }
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__find_last_run_on_line_starting_from_run(drgui_text_engine* pTL, size_t iRun, size_t* pLastRunIndexOnLineOut)
-{
- if (pTL == NULL || pTL->pRuns == NULL) {
- return false;
- }
-
- size_t result = iRun;
-
- size_t iLine = pTL->pRuns[iRun].iLine;
- for (/* Do Nothing*/; iRun < pTL->runCount && pTL->pRuns[iRun].iLine == iLine; ++iRun)
- {
- result = iRun;
- }
-
- if (pLastRunIndexOnLineOut) {
- *pLastRunIndexOnLineOut = result;
- }
-
- return true;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__find_first_run_on_line_starting_from_run(drgui_text_engine* pTL, size_t iRun, size_t* pFirstRunIndexOnLineOut)
-{
- if (pTL == NULL) {
- return false;
- }
-
- size_t result = iRun;
-
- size_t iLine = pTL->pRuns[iRun].iLine;
- for (/* Do Nothing*/; iRun > 0 && pTL->pRuns[iRun - 1].iLine == iLine; --iRun)
- {
- result = iRun - 1;
- }
-
- if (pFirstRunIndexOnLineOut) {
- *pFirstRunIndexOnLineOut = result;
- }
-
- return true;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__find_run_at_character(drgui_text_engine* pTL, size_t iChar, size_t* pRunIndexOut)
-{
- if (pTL == NULL || pTL->runCount == 0) {
- return false;
- }
-
- size_t result = 0;
- if (iChar < pTL->textLength)
- {
- for (size_t iRun = 0; iRun < pTL->runCount; ++iRun)
- {
- const drgui_text_run* pRun = pTL->pRuns + iRun;
-
- if (iChar < pRun->iCharEnd)
- {
- result = iRun;
- break;
- }
- }
- }
- else
- {
- // The character index is too high. Return the last run.
- result = pTL->runCount - 1;
- }
-
- if (pRunIndexOut) {
- *pRunIndexOut = result;
- }
-
- return true;
-}
-
-
-DRGUI_PRIVATE drgui_text_marker drgui_text_engine__new_marker()
-{
- drgui_text_marker marker;
- marker.iRun = 0;
- marker.iChar = 0;
- marker.relativePosX = 0;
- marker.absoluteSickyPosX = 0;
-
- return marker;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_point_relative_to_container(drgui_text_engine* pTL, drgui_text_marker* pMarker, float inputPosX, float inputPosY)
-{
- if (pTL == NULL || pMarker == NULL) {
- return false;
- }
-
- pMarker->iRun = 0;
- pMarker->iChar = 0;
- pMarker->relativePosX = 0;
- pMarker->absoluteSickyPosX = 0;
-
- drgui_rect textRect = drgui_text_engine_get_text_rect_relative_to_bounds(pTL);
-
- float inputPosXRelativeToText = inputPosX - textRect.left;
- float inputPosYRelativeToText = inputPosY - textRect.top;
- if (drgui_text_engine__move_marker_to_point(pTL, pMarker, inputPosXRelativeToText, inputPosYRelativeToText))
- {
- drgui_text_engine__update_marker_sticky_position(pTL, pMarker);
- return true;
- }
-
- return false;
-}
-
-DRGUI_PRIVATE void drgui_text_engine__get_marker_position_relative_to_container(drgui_text_engine* pTL, drgui_text_marker* pMarker, float* pPosXOut, float* pPosYOut)
-{
- if (pTL == NULL || pMarker == NULL) {
- return;
- }
-
- float posX = 0;
- float posY = 0;
-
- if (pMarker->iRun < pTL->runCount)
- {
- posX = pTL->pRuns[pMarker->iRun].posX + pMarker->relativePosX;
- posY = pTL->pRuns[pMarker->iRun].posY;
- }
-
- drgui_rect textRect = drgui_text_engine_get_text_rect_relative_to_bounds(pTL);
- posX += textRect.left;
- posY += textRect.top;
-
-
- if (pPosXOut) {
- *pPosXOut = posX;
- }
- if (pPosYOut) {
- *pPosYOut = posY;
- }
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_point(drgui_text_engine* pTL, drgui_text_marker* pMarker, float inputPosXRelativeToText, float inputPosYRelativeToText)
-{
- if (pTL == NULL || pMarker == NULL) {
- return false;
- }
-
- size_t iClosestRunToPoint;
- if (drgui_text_engine__find_closest_run_to_point(pTL, inputPosXRelativeToText, inputPosYRelativeToText, OUT &iClosestRunToPoint))
- {
- const drgui_text_run* pRun = pTL->pRuns + iClosestRunToPoint;
-
- pMarker->iRun = iClosestRunToPoint;
-
- if (inputPosXRelativeToText < pRun->posX)
- {
- // It's to the left of the run.
- pMarker->iChar = 0;
- pMarker->relativePosX = 0;
- }
- else if (inputPosXRelativeToText > pRun->posX + pRun->width)
- {
- // It's to the right of the run. It may be a new-line run. If so, we need to move the marker to the front of it, not the back.
- pMarker->iChar = pRun->textLength;
- pMarker->relativePosX = pRun->width;
-
- if (pTL->text[pRun->iChar] == '\n') {
- assert(pMarker->iChar == 1);
- pMarker->iChar = 0;
- pMarker->relativePosX = 0;
- }
- }
- else
- {
- // It's somewhere in the middle of the run. We need to handle this a little different for tab runs since they are aligned differently.
- if (pTL->text[pRun->iChar] == '\n')
- {
- // It's a new line character. It needs to be placed at the beginning of it.
- pMarker->iChar = 0;
- pMarker->relativePosX = 0;
- }
- else if (pTL->text[pRun->iChar] == '\t')
- {
- // It's a tab run.
- pMarker->iChar = 0;
- pMarker->relativePosX = 0;
-
- const float tabWidth = drgui_text_engine__get_tab_width(pTL);
-
- float tabLeft = pRun->posX + pMarker->relativePosX;
- for (/* Do Nothing*/; pMarker->iChar < pRun->textLength; ++pMarker->iChar)
- {
- float tabRight = tabWidth * ((pRun->posX + (tabWidth*(pMarker->iChar + 1))) / tabWidth);
- if (tabRight > pRun->posX + pRun->width) {
- tabRight = pRun->posX + pRun->width;
- }
-
- if (inputPosXRelativeToText >= tabLeft && inputPosXRelativeToText <= tabRight)
- {
- // The input position is somewhere on top of this character. If it's positioned on the left side of the character, set the output
- // value to the character at iChar. Otherwise it should be set to the character at iChar + 1.
- float charBoundsRightHalf = tabLeft + ceilf(((tabRight - tabLeft) / 2.0f));
- if (inputPosXRelativeToText <= charBoundsRightHalf) {
- pMarker->relativePosX = tabLeft - pRun->posX;
- } else {
- pMarker->relativePosX = tabRight - pRun->posX;
- pMarker->iChar += 1;
- }
-
- break;
- }
-
- tabLeft = tabRight;
- }
-
- // If we're past the last character in the tab run, we want to move to the start of the next run.
- if (pMarker->iChar == pRun->textLength) {
- drgui_text_engine__move_marker_to_first_character_of_next_run(pTL, pMarker);
- }
- }
- else
- {
- // It's a standard run.
- float inputPosXRelativeToRun = inputPosXRelativeToText - pRun->posX;
- if (drgui_get_text_cursor_position_from_point(pRun->pFont, pTL->text + pRun->iChar, pRun->textLength, pRun->width, inputPosXRelativeToRun, OUT &pMarker->relativePosX, OUT &pMarker->iChar))
- {
- // If the marker is past the last character of the run it needs to be moved to the start of the next one.
- if (pMarker->iChar == pRun->textLength) {
- drgui_text_engine__move_marker_to_first_character_of_next_run(pTL, pMarker);
- }
- }
- else
- {
- // An error occured somehow.
- return false;
- }
- }
- }
-
- return true;
- }
- else
- {
- // Couldn't find a run.
- return false;
- }
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_left(drgui_text_engine* pTL, drgui_text_marker* pMarker)
-{
- if (pTL == NULL || pMarker == NULL) {
- return false;
- }
-
- if (pTL->runCount > 0)
- {
- if (pMarker->iChar > 0)
- {
- pMarker->iChar -= 1;
-
- const drgui_text_run* pRun = pTL->pRuns + pMarker->iRun;
- if (pTL->text[pRun->iChar] == '\t')
- {
- const float tabWidth = drgui_text_engine__get_tab_width(pTL);
-
- if (pMarker->iChar == 0)
- {
- // Simple case - it's the first tab character which means the relative position is just 0.
- pMarker->relativePosX = 0;
- }
- else
- {
- pMarker->relativePosX = tabWidth * ((pRun->posX + (tabWidth*(pMarker->iChar + 0))) / tabWidth);
- pMarker->relativePosX -= pRun->posX;
- }
- }
- else
- {
- if (!drgui_get_text_cursor_position_from_char(pRun->pFont, pTL->text + pTL->pRuns[pMarker->iRun].iChar, pMarker->iChar, OUT &pMarker->relativePosX)) {
- return false;
- }
- }
- }
- else
- {
- // We're at the beginning of the run which means we need to transfer the cursor to the end of the previous run.
- if (!drgui_text_engine__move_marker_to_last_character_of_prev_run(pTL, pMarker)) {
- return false;
- }
- }
-
- drgui_text_engine__update_marker_sticky_position(pTL, pMarker);
- return true;
- }
-
- return false;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_right(drgui_text_engine* pTL, drgui_text_marker* pMarker)
-{
- if (pTL == NULL || pMarker == NULL) {
- return false;
- }
-
- if (pTL->runCount > 0)
- {
- if (pMarker->iChar + 1 < pTL->pRuns[pMarker->iRun].textLength)
- {
- pMarker->iChar += 1;
-
- const drgui_text_run* pRun = pTL->pRuns + pMarker->iRun;
- if (pTL->text[pRun->iChar] == '\t')
- {
- const float tabWidth = drgui_text_engine__get_tab_width(pTL);
-
- pMarker->relativePosX = tabWidth * ((pRun->posX + (tabWidth*(pMarker->iChar + 0))) / tabWidth);
- pMarker->relativePosX -= pRun->posX;
- }
- else
- {
- if (!drgui_get_text_cursor_position_from_char(pRun->pFont, pTL->text + pTL->pRuns[pMarker->iRun].iChar, pMarker->iChar, OUT &pMarker->relativePosX)) {
- return false;
- }
- }
- }
- else
- {
- // We're at the end of the run which means we need to transfer the cursor to the beginning of the next run.
- if (!drgui_text_engine__move_marker_to_first_character_of_next_run(pTL, pMarker)) {
- return false;
- }
- }
-
- drgui_text_engine__update_marker_sticky_position(pTL, pMarker);
- return true;
- }
-
- return false;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_up(drgui_text_engine* pTL, drgui_text_marker* pMarker)
-{
- if (pTL == NULL || pMarker == NULL || pTL->runCount == 0) {
- return false;
- }
-
- return drgui_text_engine__move_marker_y(pTL, pMarker, -1);
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_down(drgui_text_engine* pTL, drgui_text_marker* pMarker)
-{
- if (pTL == NULL || pMarker == NULL || pTL->runCount == 0) {
- return false;
- }
-
- return drgui_text_engine__move_marker_y(pTL, pMarker, 1);
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_y(drgui_text_engine* pTL, drgui_text_marker* pMarker, int amount)
-{
- if (pTL == NULL || pMarker == NULL || pTL->runCount == 0) {
- return false;
- }
-
- const drgui_text_run* pOldRun = pTL->pRuns + pMarker->iRun;
-
- int iNewLine = (int)pOldRun->iLine + amount;
- if (iNewLine >= (int)drgui_text_engine_get_line_count(pTL)) {
- iNewLine = (int)drgui_text_engine_get_line_count(pTL) - 1;
- }
- if (iNewLine < 0) {
- iNewLine = 0;
- }
-
- if ((int)pOldRun->iLine == iNewLine) {
- return false; // The lines are the same.
- }
-
- drgui_rect lineRect;
- size_t iFirstRunOnLine;
- size_t iLastRunOnLinePlus1;
- if (drgui_text_engine__find_line_info_by_index(pTL, iNewLine, OUT &lineRect, OUT &iFirstRunOnLine, OUT &iLastRunOnLinePlus1))
- {
- float newMarkerPosX = pMarker->absoluteSickyPosX;
- float newMarkerPosY = lineRect.top;
- drgui_text_engine__move_marker_to_point(pTL, pMarker, newMarkerPosX, newMarkerPosY);
-
- return true;
- }
- else
- {
- // An error occured while finding information about the line above.
- return false;
- }
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_end_of_line(drgui_text_engine* pTL, drgui_text_marker* pMarker)
-{
- if (pTL == NULL || pMarker == NULL) {
- return false;
- }
-
- size_t iLastRunOnLine;
- if (drgui_text_engine__find_last_run_on_line_starting_from_run(pTL, pMarker->iRun, &iLastRunOnLine))
- {
- return drgui_text_engine__move_marker_to_last_character_of_run(pTL, pMarker, iLastRunOnLine);
- }
-
- return false;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_start_of_line(drgui_text_engine* pTL, drgui_text_marker* pMarker)
-{
- if (pTL == NULL || pMarker == NULL) {
- return false;
- }
-
- size_t iFirstRunOnLine;
- if (drgui_text_engine__find_first_run_on_line_starting_from_run(pTL, pMarker->iRun, &iFirstRunOnLine))
- {
- return drgui_text_engine__move_marker_to_first_character_of_run(pTL, pMarker, iFirstRunOnLine);
- }
-
- return false;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_end_of_line_by_index(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iLine)
-{
- if (pTL == NULL || pMarker == NULL) {
- return false;
- }
-
- size_t iFirstRun;
- size_t iLastRunPlus1;
- if (drgui_text_engine__find_line_info_by_index(pTL, iLine, NULL, &iFirstRun, &iLastRunPlus1))
- {
- return drgui_text_engine__move_marker_to_last_character_of_run(pTL, pMarker, iLastRunPlus1 - 1);
- }
-
- return false;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_start_of_line_by_index(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iLine)
-{
- if (pTL == NULL || pMarker == NULL) {
- return false;
- }
-
- size_t iFirstRun;
- size_t iLastRunPlus1;
- if (drgui_text_engine__find_line_info_by_index(pTL, iLine, NULL, &iFirstRun, &iLastRunPlus1))
- {
- return drgui_text_engine__move_marker_to_first_character_of_run(pTL, pMarker, iFirstRun);
- }
-
- return false;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_end_of_text(drgui_text_engine* pTL, drgui_text_marker* pMarker)
-{
- if (pTL == NULL || pMarker == NULL) {
- return false;
- }
-
- if (pTL->runCount > 0) {
- return drgui_text_engine__move_marker_to_last_character_of_run(pTL, pMarker, pTL->runCount - 1);
- }
-
- return false;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_start_of_text(drgui_text_engine* pTL, drgui_text_marker* pMarker)
-{
- if (pTL == NULL || pMarker == NULL) {
- return false;
- }
-
- return drgui_text_engine__move_marker_to_first_character_of_run(pTL, pMarker, 0);
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_last_character_of_run(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iRun)
-{
- if (pTL == NULL || pMarker == NULL) {
- return false;
- }
-
- if (iRun < pTL->runCount)
- {
- pMarker->iRun = iRun;
- pMarker->iChar = pTL->pRuns[pMarker->iRun].textLength;
- pMarker->relativePosX = pTL->pRuns[pMarker->iRun].width;
-
- if (pMarker->iChar > 0)
- {
- // At this point we are located one character past the last character - we need to move it left.
- return drgui_text_engine__move_marker_left(pTL, pMarker);
- }
-
- return true;
- }
-
- return false;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_first_character_of_run(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iRun)
-{
- if (pTL == NULL || pMarker == NULL) {
- return false;
- }
-
- if (iRun < pTL->runCount)
- {
- pMarker->iRun = iRun;
- pMarker->iChar = 0;
- pMarker->relativePosX = 0;
-
- drgui_text_engine__update_marker_sticky_position(pTL, pMarker);
-
- return true;
- }
-
- return false;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_last_character_of_prev_run(drgui_text_engine* pTL, drgui_text_marker* pMarker)
-{
- if (pTL == NULL || pMarker == NULL) {
- return false;
- }
-
- if (pMarker->iRun > 0) {
- return drgui_text_engine__move_marker_to_last_character_of_run(pTL, pMarker, pMarker->iRun - 1);
- }
-
- return false;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_first_character_of_next_run(drgui_text_engine* pTL, drgui_text_marker* pMarker)
-{
- if (pTL == NULL || pMarker == NULL) {
- return false;
- }
-
- if (pTL->runCount > 0 && pMarker->iRun < pTL->runCount - 1) {
- return drgui_text_engine__move_marker_to_first_character_of_run(pTL, pMarker, pMarker->iRun + 1);
- }
-
- return false;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__move_marker_to_character(drgui_text_engine* pTL, drgui_text_marker* pMarker, size_t iChar)
-{
- if (pTL == NULL || pMarker == NULL || pTL->runCount == 0) {
- return false;
- }
-
- // Clamp the character to the end of the string.
- if (iChar > pTL->textLength) {
- iChar = pTL->textLength;
- }
-
- drgui_text_engine__find_run_at_character(pTL, iChar, &pMarker->iRun);
-
- assert(pMarker->iRun < pTL->runCount);
- pMarker->iChar = iChar - pTL->pRuns[pMarker->iRun].iChar;
-
-
- // The relative position depends on whether or not the run is a tab character.
- return drgui_text_engine__update_marker_relative_position(pTL, pMarker);
-}
-
-
-DRGUI_PRIVATE bool drgui_text_engine__update_marker_relative_position(drgui_text_engine* pTL, drgui_text_marker* pMarker)
-{
- if (pTL == NULL || pMarker == NULL || pTL->runCount == 0) {
- return false;
- }
-
- const drgui_text_run* pRun = pTL->pRuns + pMarker->iRun;
- if (pTL->text[pRun->iChar] == '\t')
- {
- const float tabWidth = drgui_text_engine__get_tab_width(pTL);
-
- if (pMarker->iChar == 0)
- {
- // Simple case - it's the first tab character which means the relative position is just 0.
- pMarker->relativePosX = 0;
- }
- else
- {
- pMarker->relativePosX = tabWidth * ((pRun->posX + (tabWidth*(pMarker->iChar + 0))) / tabWidth);
- pMarker->relativePosX -= pRun->posX;
- }
-
- return true;
- }
- else
- {
- return drgui_get_text_cursor_position_from_char(pRun->pFont, pTL->text + pTL->pRuns[pMarker->iRun].iChar, pMarker->iChar, OUT &pMarker->relativePosX);
- }
-}
-
-DRGUI_PRIVATE void drgui_text_engine__update_marker_sticky_position(drgui_text_engine* pTL, drgui_text_marker* pMarker)
-{
- if (pTL == NULL || pMarker == NULL) {
- return;
- }
-
- pMarker->absoluteSickyPosX = pTL->pRuns[pMarker->iRun].posX + pMarker->relativePosX;
-}
-
-DRGUI_PRIVATE size_t drgui_text_engine__get_marker_absolute_char_index(drgui_text_engine* pTL, drgui_text_marker* pMarker)
-{
- if (pTL == NULL || pMarker == NULL || pTL->runCount == 0) {
- return 0;
- }
-
- return pTL->pRuns[pMarker->iRun].iChar + pMarker->iChar;
-}
-
-
-DRGUI_PRIVATE bool drgui_text_engine__has_spacing_between_selection_markers(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return false;
- }
-
- return (pTL->cursor.iRun != pTL->selectionAnchor.iRun || pTL->cursor.iChar != pTL->selectionAnchor.iChar);
-}
-
-DRGUI_PRIVATE size_t drgui_text_engine__split_text_run_by_selection(drgui_text_engine* pTL, drgui_text_run* pRunToSplit, drgui_text_run pSubRunsOut[3])
-{
- if (pTL == NULL || pRunToSplit == NULL || pSubRunsOut == NULL) {
- return 0;
- }
-
- drgui_text_marker* pSelectionMarker0 = &pTL->selectionAnchor;
- drgui_text_marker* pSelectionMarker1 = &pTL->cursor;
- if (pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar > pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar)
- {
- drgui_text_marker* temp = pSelectionMarker0;
- pSelectionMarker0 = pSelectionMarker1;
- pSelectionMarker1 = temp;
- }
-
- drgui_text_run* pSelectionRun0 = pTL->pRuns + pSelectionMarker0->iRun;
- drgui_text_run* pSelectionRun1 = pTL->pRuns + pSelectionMarker1->iRun;
-
- size_t iSelectionChar0 = pSelectionRun0->iChar + pSelectionMarker0->iChar;
- size_t iSelectionChar1 = pSelectionRun1->iChar + pSelectionMarker1->iChar;
-
- if (drgui_text_engine_is_anything_selected(pTL))
- {
- if (pRunToSplit->iChar < iSelectionChar1 && pRunToSplit->iCharEnd > iSelectionChar0)
- {
- // The run is somewhere inside the selection region.
- for (int i = 0; i < 3; ++i) {
- pSubRunsOut[i] = *pRunToSplit;
- }
-
- if (pRunToSplit->iChar >= iSelectionChar0)
- {
- // The first part of the run is selected.
- if (pRunToSplit->iCharEnd <= iSelectionChar1)
- {
- // The entire run is selected.
- pSubRunsOut[0].backgroundColor = pTL->selectionBackgroundColor;
- return 1;
- }
- else
- {
- // The head part of the run is selected, and the tail is deselected.
-
- // Head.
- pSubRunsOut[0].backgroundColor = pTL->selectionBackgroundColor;
- pSubRunsOut[0].iCharEnd = iSelectionChar1;
- pSubRunsOut[0].width = pSelectionMarker1->relativePosX;
- pSubRunsOut[0].text = pTL->text + pSubRunsOut[0].iChar;
- pSubRunsOut[0].textLength = pSubRunsOut[0].iCharEnd - pSubRunsOut[0].iChar;
-
- // Tail.
- pSubRunsOut[1].iChar = iSelectionChar1;
- pSubRunsOut[1].width = pRunToSplit->width - pSelectionMarker1->relativePosX;
- pSubRunsOut[1].posX = pSubRunsOut[0].posX + pSubRunsOut[0].width;
- pSubRunsOut[1].text = pTL->text + pSubRunsOut[1].iChar;
- pSubRunsOut[1].textLength = pSubRunsOut[1].iCharEnd - pSubRunsOut[1].iChar;
-
- return 2;
- }
- }
- else
- {
- // The first part of the run is deselected. There will be at least 2, but possibly 3 sub-runs in this case.
- if (pRunToSplit->iCharEnd <= iSelectionChar1)
- {
- // The head of the run is deselected and the tail is selected.
-
- // Head.
- pSubRunsOut[0].iCharEnd = iSelectionChar0;
- pSubRunsOut[0].width = pSelectionMarker0->relativePosX;
- pSubRunsOut[0].text = pTL->text + pSubRunsOut[0].iChar;
- pSubRunsOut[0].textLength = pSubRunsOut[0].iCharEnd - pSubRunsOut[0].iChar;
-
- // Tail.
- pSubRunsOut[1].backgroundColor = pTL->selectionBackgroundColor;
- pSubRunsOut[1].iChar = iSelectionChar0;
- pSubRunsOut[1].width = pRunToSplit->width - pSubRunsOut[0].width;
- pSubRunsOut[1].posX = pSubRunsOut[0].posX + pSubRunsOut[0].width;
- pSubRunsOut[1].text = pTL->text + pSubRunsOut[1].iChar;
- pSubRunsOut[1].textLength = pSubRunsOut[1].iCharEnd - pSubRunsOut[1].iChar;
-
- return 2;
- }
- else
- {
- // The head and tail are both deselected, and the middle section is selected.
-
- // Head.
- pSubRunsOut[0].iCharEnd = iSelectionChar0;
- pSubRunsOut[0].width = pSelectionMarker0->relativePosX;
- pSubRunsOut[0].text = pTL->text + pSubRunsOut[0].iChar;
- pSubRunsOut[0].textLength = pSubRunsOut[0].iCharEnd - pSubRunsOut[0].iChar;
-
- // Mid.
- pSubRunsOut[1].iChar = iSelectionChar0;
- pSubRunsOut[1].iCharEnd = iSelectionChar1;
- pSubRunsOut[1].backgroundColor = pTL->selectionBackgroundColor;
- pSubRunsOut[1].width = pSelectionMarker1->relativePosX - pSelectionMarker0->relativePosX;
- pSubRunsOut[1].posX = pSubRunsOut[0].posX + pSubRunsOut[0].width;
- pSubRunsOut[1].text = pTL->text + pSubRunsOut[1].iChar;
- pSubRunsOut[1].textLength = pSubRunsOut[1].iCharEnd - pSubRunsOut[1].iChar;
-
- // Tail.
- pSubRunsOut[2].iChar = iSelectionChar1;
- pSubRunsOut[2].width = pRunToSplit->width - pSelectionMarker1->relativePosX;
- pSubRunsOut[2].posX = pSubRunsOut[1].posX + pSubRunsOut[1].width;
- pSubRunsOut[2].text = pTL->text + pSubRunsOut[2].iChar;
- pSubRunsOut[2].textLength = pSubRunsOut[2].iCharEnd - pSubRunsOut[2].iChar;
-
- return 3;
- }
- }
- }
- }
-
- // If we get here it means the run is not within the selected region.
- pSubRunsOut[0] = *pRunToSplit;
- return 1;
-}
-
-#if 0
-DRGUI_PRIVATE bool drgui_text_engine__is_run_selected(drgui_text_engine* pTL, unsigned int iRun)
-{
- if (drgui_text_engine_is_anything_selected(pTL))
- {
- drgui_text_marker* pSelectionMarker0;
- drgui_text_marker* pSelectionMarker1;
- if (!drgui_text_engine__get_selection_markers(pTL, &pSelectionMarker0, &pSelectionMarker1)) {
- return false;
- }
-
- unsigned int iSelectionChar0 = pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar;
- unsigned int iSelectionChar1 = pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar;
-
- return pTL->pRuns[iRun].iChar < iSelectionChar1 && pTL->pRuns[iRun].iCharEnd > iSelectionChar0;
- }
-
- return false;
-}
-#endif
-
-DRGUI_PRIVATE bool drgui_text_engine__get_selection_markers(drgui_text_engine* pTL, drgui_text_marker** ppSelectionMarker0Out, drgui_text_marker** ppSelectionMarker1Out)
-{
- bool result = false;
-
- drgui_text_marker* pSelectionMarker0 = NULL;
- drgui_text_marker* pSelectionMarker1 = NULL;
- if (pTL != NULL && drgui_text_engine_is_anything_selected(pTL))
- {
- pSelectionMarker0 = &pTL->selectionAnchor;
- pSelectionMarker1 = &pTL->cursor;
- if (pTL->pRuns[pSelectionMarker0->iRun].iChar + pSelectionMarker0->iChar > pTL->pRuns[pSelectionMarker1->iRun].iChar + pSelectionMarker1->iChar)
- {
- drgui_text_marker* temp = pSelectionMarker0;
- pSelectionMarker0 = pSelectionMarker1;
- pSelectionMarker1 = temp;
- }
-
- result = true;
- }
-
- if (ppSelectionMarker0Out) {
- *ppSelectionMarker0Out = pSelectionMarker0;
- }
- if (ppSelectionMarker1Out) {
- *ppSelectionMarker1Out = pSelectionMarker1;
- }
-
- return result;
-}
-
-
-DRGUI_PRIVATE bool drgui_text_engine__first_line(drgui_text_engine* pTL, drgui_text_engine_line* pLine)
-{
- if (pTL == NULL || pLine == NULL || pTL->runCount == 0) {
- return false;
- }
-
- pLine->index = 0;
- pLine->posY = 0;
- pLine->height = 0;
- pLine->iFirstRun = 0;
- pLine->iLastRun = 0;
-
- // We need to find the last run in the line and the height. The height is determined by the run with the largest height.
- while (pLine->iLastRun < pTL->runCount)
- {
- if (pLine->height < pTL->pRuns[pLine->iLastRun].height) {
- pLine->height = pTL->pRuns[pLine->iLastRun].height;
- }
-
- pLine->iLastRun += 1;
-
- if (pTL->pRuns[pLine->iLastRun].iLine != pLine->index) {
- break;
- }
- }
-
- if (pLine->iLastRun > 0) {
- pLine->iLastRun -= 1;
- }
-
- return true;
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__next_line(drgui_text_engine* pTL, drgui_text_engine_line* pLine)
-{
- if (pTL == NULL || pLine == NULL || pTL->runCount == 0) {
- return false;
- }
-
- // If there's no more runs, there is no next line.
- if (pLine->iLastRun == pTL->runCount - 1) {
- return false;
- }
-
- pLine->index += 1;
- pLine->posY += pLine->height;
- pLine->height = 0;
- pLine->iFirstRun = pLine->iLastRun + 1;
- pLine->iLastRun = pLine->iFirstRun;
-
- while (pLine->iLastRun < pTL->runCount)
- {
- if (pLine->height < pTL->pRuns[pLine->iLastRun].height) {
- pLine->height = pTL->pRuns[pLine->iLastRun].height;
- }
-
- pLine->iLastRun += 1;
-
- if (pTL->pRuns[pLine->iLastRun].iLine != pLine->index) {
- break;
- }
- }
-
- if (pLine->iLastRun > 0) {
- pLine->iLastRun -= 1;
- }
-
- return true;
-}
-
-DRGUI_PRIVATE void drgui_text_engine__trim_undo_stack(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return;
- }
-
- while (pTL->undoStackCount > pTL->iUndoState)
- {
- unsigned int iLastItem = pTL->undoStackCount - 1;
-
- drgui_text_engine__uninit_undo_state(pTL->pUndoStack + iLastItem);
- pTL->undoStackCount -= 1;
- }
-}
-
-DRGUI_PRIVATE bool drgui_text_engine__diff_states(drgui_text_engine_state* pPrevState, drgui_text_engine_state* pCurrentState, drgui_text_engine_undo_state* pUndoStateOut)
-{
- if (pPrevState == NULL || pCurrentState == NULL || pUndoStateOut == NULL) {
- return false;
- }
-
- if (pPrevState->text == NULL || pCurrentState->text == NULL) {
- return false;
- }
-
- const char* prevText = pPrevState->text;
- const char* currText = pCurrentState->text;
-
- const size_t prevLen = strlen(prevText);
- const size_t currLen = strlen(currText);
-
-
- // The first step is to find the position of the first differing character.
- size_t sameChCountStart;
- for (sameChCountStart = 0; sameChCountStart < prevLen && sameChCountStart < currLen; ++sameChCountStart)
- {
- char prevCh = prevText[sameChCountStart];
- char currCh = currText[sameChCountStart];
-
- if (prevCh != currCh) {
- break;
- }
- }
-
- // The next step is to find the position of the last differing character.
- size_t sameChCountEnd;
- for (sameChCountEnd = 0; sameChCountEnd < prevLen && sameChCountEnd < currLen; ++sameChCountEnd)
- {
- // Don't move beyond the first differing character.
- if (prevLen - sameChCountEnd <= sameChCountStart ||
- currLen - sameChCountEnd <= sameChCountStart)
- {
- break;
- }
-
- char prevCh = prevText[prevLen - sameChCountEnd - 1];
- char currCh = currText[currLen - sameChCountEnd - 1];
-
- if (prevCh != currCh) {
- break;
- }
- }
-
-
- // At this point we know which section of the text is different. We now need to initialize the undo state object.
- pUndoStateOut->diffPos = sameChCountStart;
- pUndoStateOut->newState = *pCurrentState;
- pUndoStateOut->newState.text = NULL;
- pUndoStateOut->oldState = *pPrevState;
- pUndoStateOut->oldState.text = NULL;
-
- size_t oldTextLen = prevLen - sameChCountStart - sameChCountEnd;
- pUndoStateOut->oldText = (char*)malloc(oldTextLen + 1);
- drgui__strncpy_s(pUndoStateOut->oldText, oldTextLen + 1, prevText + sameChCountStart, oldTextLen);
-
- size_t newTextLen = currLen - sameChCountStart - sameChCountEnd;
- pUndoStateOut->newText = (char*)malloc(newTextLen + 1);
- drgui__strncpy_s(pUndoStateOut->newText, newTextLen + 1, currText + sameChCountStart, newTextLen);
-
- return true;
-}
-
-DRGUI_PRIVATE void drgui_text_engine__uninit_undo_state(drgui_text_engine_undo_state* pUndoState)
-{
- if (pUndoState == NULL) {
- return;
- }
-
- free(pUndoState->oldText);
- pUndoState->oldText = NULL;
-
- free(pUndoState->newText);
- pUndoState->newText = NULL;
-}
-
-DRGUI_PRIVATE void drgui_text_engine__push_undo_state(drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState)
-{
- if (pTL == NULL || pUndoState == NULL) {
- return;
- }
-
- assert(pTL->iUndoState == pTL->undoStackCount);
-
-
- drgui_text_engine_undo_state* pOldStack = pTL->pUndoStack;
- drgui_text_engine_undo_state* pNewStack = (drgui_text_engine_undo_state*)malloc(sizeof(*pNewStack) * (pTL->undoStackCount + 1));
-
- if (pTL->undoStackCount > 0) {
- memcpy(pNewStack, pOldStack, sizeof(*pNewStack) * (pTL->undoStackCount));
- }
-
- pNewStack[pTL->undoStackCount] = *pUndoState;
- pTL->pUndoStack = pNewStack;
- pTL->undoStackCount += 1;
- pTL->iUndoState += 1;
-
- if (pTL->onUndoPointChanged) {
- pTL->onUndoPointChanged(pTL, pTL->iUndoState);
- }
-
- free(pOldStack);
-}
-
-DRGUI_PRIVATE void drgui_text_engine__apply_undo_state(drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState)
-{
- if (pTL == NULL || pUndoState == NULL) {
- return;
- }
-
- // When undoing we want to remove the new text and replace it with the old text.
-
- size_t iFirstCh = pUndoState->diffPos;
- size_t iLastChPlus1 = pUndoState->diffPos + strlen(pUndoState->newText);
- size_t bytesToRemove = iLastChPlus1 - iFirstCh;
- if (bytesToRemove > 0)
- {
- memmove(pTL->text + iFirstCh, pTL->text + iLastChPlus1, pTL->textLength - iLastChPlus1);
- pTL->textLength -= bytesToRemove;
- pTL->text[pTL->textLength] = '\0';
- }
-
- // TODO: This needs improving because it results in multiple onTextChanged and onDirty events being posted.
-
- // Insert the old text.
- drgui_text_engine_insert_text(pTL, pUndoState->oldText, pUndoState->diffPos);
-
-
- // The layout will have changed so it needs to be refreshed.
- drgui_text_engine__refresh(pTL);
-
-
- // Markers needs to be updated after refreshing the layout.
- drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, pUndoState->oldState.cursorPos);
- drgui_text_engine__move_marker_to_character(pTL, &pTL->selectionAnchor, pUndoState->oldState.selectionAnchorPos);
-
- // The cursor's sticky position needs to be updated whenever the text is edited.
- drgui_text_engine__update_marker_sticky_position(pTL, &pTL->cursor);
-
- // Ensure we mark the text as selected if appropriate.
- pTL->isAnythingSelected = pUndoState->oldState.isAnythingSelected;
-
-
- if (pTL->onTextChanged) {
- pTL->onTextChanged(pTL);
- }
-
- drgui_text_engine__on_cursor_move(pTL);
-
- if (pTL->onDirty) {
- pTL->onDirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-}
-
-DRGUI_PRIVATE void drgui_text_engine__apply_redo_state(drgui_text_engine* pTL, drgui_text_engine_undo_state* pUndoState)
-{
- if (pTL == NULL || pUndoState == NULL) {
- return;
- }
-
- // An redo is just the opposite of an undo. We want to remove the old text and replace it with the new text.
-
- size_t iFirstCh = pUndoState->diffPos;
- size_t iLastChPlus1 = pUndoState->diffPos + strlen(pUndoState->oldText);
- size_t bytesToRemove = iLastChPlus1 - iFirstCh;
- if (bytesToRemove > 0)
- {
- memmove(pTL->text + iFirstCh, pTL->text + iLastChPlus1, pTL->textLength - iLastChPlus1);
- pTL->textLength -= bytesToRemove;
- pTL->text[pTL->textLength] = '\0';
- }
-
- // TODO: This needs improving because it results in multiple onTextChanged and onDirty events being posted.
-
- // Insert the new text.
- drgui_text_engine_insert_text(pTL, pUndoState->newText, pUndoState->diffPos);
-
-
- // The layout will have changed so it needs to be refreshed.
- drgui_text_engine__refresh(pTL);
-
-
- // Markers needs to be updated after refreshing the layout.
- drgui_text_engine__move_marker_to_character(pTL, &pTL->cursor, pUndoState->newState.cursorPos);
- drgui_text_engine__move_marker_to_character(pTL, &pTL->selectionAnchor, pUndoState->newState.selectionAnchorPos);
-
- // The cursor's sticky position needs to be updated whenever the text is edited.
- drgui_text_engine__update_marker_sticky_position(pTL, &pTL->cursor);
-
- // Ensure we mark the text as selected if appropriate.
- pTL->isAnythingSelected = pUndoState->newState.isAnythingSelected;
-
-
- if (pTL->onTextChanged) {
- pTL->onTextChanged(pTL);
- }
-
- drgui_text_engine__on_cursor_move(pTL);
-
- if (pTL->onDirty) {
- pTL->onDirty(pTL, drgui_text_engine__local_rect(pTL));
- }
-}
-
-
-DRGUI_PRIVATE drgui_rect drgui_text_engine__local_rect(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return drgui_make_rect(0, 0, 0, 0);
- }
-
- return drgui_make_rect(0, 0, pTL->containerWidth, pTL->containerHeight);
-}
-
-
-DRGUI_PRIVATE void drgui_text_engine__on_cursor_move(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return;
- }
-
- // When the cursor moves we want to reset the cursor's blink state.
- pTL->timeToNextCursorBlink = pTL->cursorBlinkRate;
- pTL->isCursorBlinkOn = true;
-
- if (pTL->onCursorMove) {
- pTL->onCursorMove(pTL);
- }
-
- drgui_text_engine__on_dirty(pTL, drgui_text_engine_get_cursor_rect(pTL));
-}
-
-DRGUI_PRIVATE void drgui_text_engine__on_dirty(drgui_text_engine* pTL, drgui_rect rect)
-{
- drgui_text_engine__begin_dirty(pTL);
- {
- pTL->accumulatedDirtyRect = drgui_rect_union(pTL->accumulatedDirtyRect, rect);
- }
- drgui_text_engine__end_dirty(pTL);
-}
-
-DRGUI_PRIVATE void drgui_text_engine__begin_dirty(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return;
- }
-
- pTL->dirtyCounter += 1;
-}
-
-DRGUI_PRIVATE void drgui_text_engine__end_dirty(drgui_text_engine* pTL)
-{
- if (pTL == NULL) {
- return;
- }
-
- assert(pTL->dirtyCounter > 0);
-
- pTL->dirtyCounter -= 1;
-
- if (pTL->dirtyCounter == 0) {
- if (pTL->onDirty) {
- pTL->onDirty(pTL, pTL->accumulatedDirtyRect);
- }
-
- pTL->accumulatedDirtyRect = drgui_make_inside_out_rect();
- }
-}
-#endif //DR_GUI_IMPLEMENTATION
-#endif
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-//
-// Scrollbar
-//
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-#ifndef drgui_scrollbar_h
-#define drgui_scrollbar_h
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum
-{
- drgui_sb_orientation_none,
- drgui_sb_orientation_vertical,
- drgui_sb_orientation_horizontal
-
-} drgui_sb_orientation;
-
-typedef void (* drgui_sb_on_scroll_proc)(drgui_element* pSBElement, int scrollPos);
-
-
-/// Creates a scrollbar element.
-drgui_element* drgui_create_scrollbar(drgui_context* pContext, drgui_element* pParent, drgui_sb_orientation orientation, size_t extraDataSize, const void* pExtraData);
-
-/// Deletes the given scrollbar element.
-void drgui_delete_scrollbar(drgui_element* pSBElement);
-
-
-/// Retrieves the size of the extra data associated with the scrollbar.
-size_t drgui_sb_get_extra_data_size(drgui_element* pSBElement);
-
-/// Retrieves a pointer to the extra data associated with the scrollbar.
-void* drgui_sb_get_extra_data(drgui_element* pSBElement);
-
-
-/// Retrieves the orientation of the given scrollbar.
-drgui_sb_orientation drgui_sb_get_orientation(drgui_element* pSBElement);
-
-
-/// Sets the given scrollbar's range.
-void drgui_sb_set_range(drgui_element* pSBElement, int rangeMin, int rangeMax);
-
-/// Retrieves the given scrollbar's range.
-void drgui_sb_get_range(drgui_element* pSBElement, int* pRangeMinOut, int* pRangeMaxOut);
-
-
-/// Sets the page size of the given scrollbar's page.
-void drgui_sb_set_page_size(drgui_element* pSBElement, int pageSize);
-
-/// Retrieves the page size of the given scrollbar's page.
-int drgui_sb_get_page_size(drgui_element* pSBElement);
-
-
-/// Sets the range and page size.
-///
-/// @remarks
-/// Use this when both the range and page size need to be updated at the same time.
-void drgui_sb_set_range_and_page_size(drgui_element* pSBElement, int rangeMin, int rangeMax, int pageSize);
-
-
-/// Explicitly sets the scroll position.
-///
-/// @remarks
-/// This will move the thumb, but not post the on_scroll event.
-/// @par
-/// The scroll position will be clamped to the current range, minus the page size.
-void drgui_sb_set_scroll_position(drgui_element* pSBElement, int position);
-
-/// Retrieves the scroll position.
-int drgui_sb_get_scroll_position(drgui_element* pSBElement);
-
-
-/// Scrolls by the given amount.
-///
-/// @remarks
-/// If the resulting scroll position differs from the old one, the on on_scroll event will be posted.
-void drgui_sb_scroll(drgui_element* pSBElement, int offset);
-
-/// Scrolls to the given position.
-///
-/// @remarks
-/// This differs from drgui_sb_set_scroll_position in that it will post the on_scroll event.
-/// @par
-/// Note that the actual maximum scrollable position is equal to the maximum range value minus the page size.
-void drgui_sb_scroll_to(drgui_element* pSBElement, int newScrollPos);
-
-
-/// Enables auto-hiding of the thumb.
-void drgui_sb_enable_thumb_auto_hide(drgui_element* pSBElement);
-
-/// Disables auto-hiding of the thumb.
-void drgui_sb_disable_thumb_auto_hide(drgui_element* pSBElement);
-
-/// Determines whether or not thumb auto-hiding is enabled.
-bool drgui_sb_is_thumb_auto_hide_enabled(drgui_element* pSBElement);
-
-/// Determines whether or not the thumb is visible.
-///
-/// @remarks
-/// This is determined by whether or not the thumb is set to auto-hide and the current range and page size.
-bool drgui_sb_is_thumb_visible(drgui_element* pSBElement);
-
-
-/// Sets the mouse wheel scale.
-///
-/// @remarks
-/// Set this to a negative value to reverse the direction.
-void drgui_sb_set_mouse_wheel_scele(drgui_element* pSBElement, int scale);
-
-/// Retrieves the mouse wheel scale.
-int drgui_sb_get_mouse_wheel_scale(drgui_element* pSBElement);
-
-
-/// Sets the color of the track.
-void drgui_sb_set_track_color(drgui_element* pSBElement, drgui_color color);
-
-/// Sets the default color of the thumb.
-void drgui_sb_set_default_thumb_color(drgui_element* pSBElement, drgui_color color);
-
-/// Sets the hovered color of the thumb.
-void drgui_sb_set_hovered_thumb_color(drgui_element* pSBElement, drgui_color color);
-
-/// Sets the pressed color of the thumb.
-void drgui_sb_set_pressed_thumb_color(drgui_element* pSBElement, drgui_color color);
-
-
-/// Sets the function to call when the given scrollbar is scrolled.
-void drgui_sb_set_on_scroll(drgui_element* pSBElement, drgui_sb_on_scroll_proc onScroll);
-
-/// Retrieves the function call when the given scrollbar is scrolled.
-drgui_sb_on_scroll_proc drgui_sb_get_on_scroll(drgui_element* pSBElement);
-
-
-/// Calculates the relative rectangle of the given scrollbar's thumb.
-drgui_rect drgui_sb_get_thumb_rect(drgui_element* pSBElement);
-
-
-/// Called when the size event needs to be processed for the given scrollbar.
-void drgui_sb_on_size(drgui_element* pSBElement, float newWidth, float newHeight);
-
-/// Called when the mouse leave event needs to be processed for the given scrollbar.
-void drgui_sb_on_mouse_leave(drgui_element* pSBElement);
-
-/// Called when the mouse move event needs to be processed for the given scrollbar.
-void drgui_sb_on_mouse_move(drgui_element* pSBElement, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// Called when the mouse button down event needs to be processed for the given scrollbar.
-void drgui_sb_on_mouse_button_down(drgui_element* pSBElement, int button, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// Called when the mouse button up event needs to be processed for the given scrollbar.
-void drgui_sb_on_mouse_button_up(drgui_element* pSBElement, int button, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// Called when the mouse wheel event needs to be processed for the given scrollbar.
-void drgui_sb_on_mouse_wheel(drgui_element* pSBElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// Called when the paint event needs to be processed.
-void drgui_sb_on_paint(drgui_element* pSBElement, drgui_rect relativeClippingRect, void* pPaintData);
-
-
-#ifdef __cplusplus
-}
-#endif
-#endif //drgui_scrollbar_h
-
-
-#ifdef DR_GUI_IMPLEMENTATION
-
-#define DRGUI_MIN_SCROLLBAR_THUMB_SIZE 16
-
-typedef struct
-{
- /// The orientation.
- drgui_sb_orientation orientation;
-
- /// The minimum scroll range.
- int rangeMin;
-
- /// The maximum scroll range.
- int rangeMax;
-
- /// The page size.
- int pageSize;
-
- /// The current scroll position.
- int scrollPos;
-
- /// Whether or not to auto-hide the thumb.
- bool autoHideThumb;
-
- /// The mouse wheel scale.
- int mouseWheelScale;
-
- /// The color of the track.
- drgui_color trackColor;
-
- /// The color of the thumb while not hovered or pressed.
- drgui_color thumbColor;
-
- /// The color of the thumb while hovered.
- drgui_color thumbColorHovered;
-
- /// The color of the thumb while pressed.
- drgui_color thumbColorPressed;
-
- /// The function to call when the scroll position changes.
- drgui_sb_on_scroll_proc onScroll;
-
-
- /// The current size of the thumb.
- float thumbSize;
-
- /// The current position of the thumb.
- float thumbPos;
-
- /// The amount of padding between the edge of the scrollbar and the thumb.
- float thumbPadding;
-
- /// Whether or not we are hovered over the thumb.
- bool thumbHovered;
-
- /// Whether or not the thumb is pressed.
- bool thumbPressed;
-
- /// The relative position of the mouse on the x axis at the time the thumb was pressed with the mouse.
- float thumbClickPosX;
-
- /// The relative position of the mouse on the y axis at the time the thumb was pressed with the mouse.
- float thumbClickPosY;
-
-
- /// The size of the extra data.
- size_t extraDataSize;
-
- /// A pointer to the extra data.
- char pExtraData[1];
-
-} drgui_scrollbar;
-
-
-/// Refreshes the given scrollbar's thumb layout and redraws it.
-DRGUI_PRIVATE void drgui_sb_refresh_thumb(drgui_element* pSBElement);
-
-/// Calculates the size of the thumb. This does not change the state of the thumb.
-DRGUI_PRIVATE float drgui_sb_calculate_thumb_size(drgui_element* pSBElement);
-
-/// Calculates the position of the thumb. This does not change the state of the thumb.
-DRGUI_PRIVATE float drgui_sb_calculate_thumb_position(drgui_element* pSBElement);
-
-/// Retrieves the size of the given scrollbar's track. For vertical alignments, it's the height of the element, otherwise it's the width.
-DRGUI_PRIVATE float drgui_sb_get_track_size(drgui_element* pSBElement);
-
-/// Makes the given point that's relative to the given scrollbar relative to it's thumb.
-DRGUI_PRIVATE void drgui_sb_make_relative_to_thumb(drgui_element* pSBElement, float* pPosX, float* pPosY);
-
-/// Calculates the scroll position based on the current position of the thumb. This is used for scrolling while dragging the thumb.
-DRGUI_PRIVATE int drgui_sb_calculate_scroll_pos_from_thumb_pos(drgui_element* pScrollba, float thumbPosr);
-
-/// Simple clamp function.
-DRGUI_PRIVATE float drgui_sb_clampf(float n, float lower, float upper)
-{
- return n <= lower ? lower : n >= upper ? upper : n;
-}
-
-/// Simple clamp function.
-DRGUI_PRIVATE int drgui_sb_clampi(int n, int lower, int upper)
-{
- return n <= lower ? lower : n >= upper ? upper : n;
-}
-
-/// Simple max function.
-DRGUI_PRIVATE int drgui_sb_maxi(int x, int y)
-{
- return (x > y) ? x : y;
-}
-
-
-drgui_element* drgui_create_scrollbar(drgui_context* pContext, drgui_element* pParent, drgui_sb_orientation orientation, size_t extraDataSize, const void* pExtraData)
-{
- if (pContext == NULL || orientation == drgui_sb_orientation_none) {
- return NULL;
- }
-
- drgui_element* pSBElement = drgui_create_element(pContext, pParent, sizeof(drgui_scrollbar) + extraDataSize, NULL);
- if (pSBElement == NULL) {
- return NULL;
- }
-
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- assert(pSB != NULL);
-
- pSB->orientation = orientation;
- pSB->rangeMin = 0;
- pSB->rangeMax = 0;
- pSB->pageSize = 0;
- pSB->scrollPos = 0;
- pSB->autoHideThumb = true;
- pSB->mouseWheelScale = 1;
- pSB->trackColor = drgui_rgb(80, 80, 80);
- pSB->thumbColor = drgui_rgb(112, 112, 112);
- pSB->thumbColorHovered = drgui_rgb(144, 144, 144);
- pSB->thumbColorPressed = drgui_rgb(180, 180, 180);
- pSB->onScroll = NULL;
-
- pSB->thumbSize = DRGUI_MIN_SCROLLBAR_THUMB_SIZE;
- pSB->thumbPos = 0;
- pSB->thumbPadding = 2;
- pSB->thumbHovered = false;
- pSB->thumbPressed = false;
- pSB->thumbClickPosX = 0;
- pSB->thumbClickPosY = 0;
-
- pSB->extraDataSize = extraDataSize;
- if (pExtraData != NULL) {
- memcpy(pSB->pExtraData, pExtraData, extraDataSize);
- }
-
-
- // Default event handlers.
- drgui_set_on_size(pSBElement, drgui_sb_on_size);
- drgui_set_on_mouse_leave(pSBElement, drgui_sb_on_mouse_leave);
- drgui_set_on_mouse_move(pSBElement, drgui_sb_on_mouse_move);
- drgui_set_on_mouse_button_down(pSBElement, drgui_sb_on_mouse_button_down);
- drgui_set_on_mouse_button_up(pSBElement, drgui_sb_on_mouse_button_up);
- drgui_set_on_mouse_wheel(pSBElement, drgui_sb_on_mouse_wheel);
- drgui_set_on_paint(pSBElement, drgui_sb_on_paint);
-
-
- return pSBElement;
-}
-
-void drgui_delete_scrollbar(drgui_element* pSBElement)
-{
- if (pSBElement == NULL) {
- return;
- }
-
- drgui_delete_element(pSBElement);
-}
-
-
-size_t drgui_sb_get_extra_data_size(drgui_element* pSBElement)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return 0;
- }
-
- return pSB->extraDataSize;
-}
-
-void* drgui_sb_get_extra_data(drgui_element* pSBElement)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return NULL;
- }
-
- return pSB->pExtraData;
-}
-
-
-drgui_sb_orientation drgui_sb_get_orientation(drgui_element* pSBElement)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return drgui_sb_orientation_none;
- }
-
- return pSB->orientation;
-}
-
-
-void drgui_sb_set_range(drgui_element* pSBElement, int rangeMin, int rangeMax)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- pSB->rangeMin = rangeMin;
- pSB->rangeMax = rangeMax;
-
-
- // Make sure the scroll position is still valid.
- drgui_sb_scroll_to(pSBElement, drgui_sb_get_scroll_position(pSBElement));
-
- // The thumb may have changed, so refresh it.
- drgui_sb_refresh_thumb(pSBElement);
-}
-
-void drgui_sb_get_range(drgui_element* pSBElement, int* pRangeMinOut, int* pRangeMaxOut)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- if (pRangeMinOut != NULL) {
- *pRangeMinOut = pSB->rangeMin;
- }
-
- if (pRangeMaxOut != NULL) {
- *pRangeMaxOut = pSB->rangeMax;
- }
-}
-
-
-void drgui_sb_set_page_size(drgui_element* pSBElement, int pageSize)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- pSB->pageSize = pageSize;
-
-
- // Make sure the scroll position is still valid.
- drgui_sb_scroll_to(pSBElement, drgui_sb_get_scroll_position(pSBElement));
-
- // The thumb may have changed, so refresh it.
- drgui_sb_refresh_thumb(pSBElement);
-}
-
-int drgui_sb_get_page_size(drgui_element* pSBElement)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return 0;
- }
-
- return pSB->pageSize;
-}
-
-
-void drgui_sb_set_range_and_page_size(drgui_element* pSBElement, int rangeMin, int rangeMax, int pageSize)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- pSB->rangeMin = rangeMin;
- pSB->rangeMax = rangeMax;
- pSB->pageSize = pageSize;
-
-
- // Make sure the scroll position is still valid.
- drgui_sb_scroll_to(pSBElement, drgui_sb_get_scroll_position(pSBElement));
-
- // The thumb may have changed, so refresh it.
- drgui_sb_refresh_thumb(pSBElement);
-}
-
-
-void drgui_sb_set_scroll_position(drgui_element* pSBElement, int position)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- int newScrollPos = drgui_sb_clampi(position, pSB->rangeMin, drgui_sb_maxi(0, pSB->rangeMax - pSB->pageSize + 1));
- if (newScrollPos != pSB->scrollPos)
- {
- pSB->scrollPos = newScrollPos;
-
- // The position of the thumb has changed, so refresh it.
- drgui_sb_refresh_thumb(pSBElement);
- }
-}
-
-int drgui_sb_get_scroll_position(drgui_element* pSBElement)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return 0;
- }
-
- return pSB->scrollPos;
-}
-
-
-void drgui_sb_scroll(drgui_element* pSBElement, int offset)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- drgui_sb_scroll_to(pSBElement, pSB->scrollPos + offset);
-}
-
-void drgui_sb_scroll_to(drgui_element* pSBElement, int newScrollPos)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- int oldScrollPos = pSB->scrollPos;
- drgui_sb_set_scroll_position(pSBElement, newScrollPos);
-
- if (oldScrollPos != pSB->scrollPos)
- {
- if (pSB->onScroll) {
- pSB->onScroll(pSBElement, pSB->scrollPos);
- }
- }
-}
-
-
-void drgui_sb_enable_thumb_auto_hide(drgui_element* pSBElement)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- if (pSB->autoHideThumb != true)
- {
- pSB->autoHideThumb = true;
-
- // The thumb needs to be refreshed in order to show the correct state.
- drgui_sb_refresh_thumb(pSBElement);
- }
-}
-
-void drgui_sb_disable_thumb_auto_hide(drgui_element* pSBElement)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- if (pSB->autoHideThumb != false)
- {
- pSB->autoHideThumb = false;
-
- // The thumb needs to be refreshed in order to show the correct state.
- drgui_sb_refresh_thumb(pSBElement);
- }
-}
-
-bool drgui_sb_is_thumb_auto_hide_enabled(drgui_element* pSBElement)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return false;
- }
-
- return pSB->autoHideThumb;
-}
-
-bool drgui_sb_is_thumb_visible(drgui_element* pSBElement)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return false;
- }
-
- // Always visible if auto-hiding is disabled.
- if (!pSB->autoHideThumb) {
- return true;
- }
-
- return pSB->pageSize < (pSB->rangeMax - pSB->rangeMin + 1) && pSB->pageSize > 0;
-}
-
-
-void drgui_sb_set_mouse_wheel_scele(drgui_element* pSBElement, int scale)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- pSB->mouseWheelScale = scale;
-}
-
-int drgui_sb_get_mouse_wheel_scale(drgui_element* pSBElement)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return 1;
- }
-
- return pSB->mouseWheelScale;
-}
-
-
-void drgui_sb_set_track_color(drgui_element* pSBElement, drgui_color color)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- pSB->trackColor = color;
-}
-
-void drgui_sb_set_default_thumb_color(drgui_element* pSBElement, drgui_color color)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- pSB->thumbColor = color;
-}
-
-void drgui_sb_set_hovered_thumb_color(drgui_element* pSBElement, drgui_color color)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- pSB->thumbColorHovered = color;
-}
-
-void drgui_sb_set_pressed_thumb_color(drgui_element* pSBElement, drgui_color color)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- pSB->thumbColorPressed = color;
-}
-
-
-void drgui_sb_set_on_scroll(drgui_element* pSBElement, drgui_sb_on_scroll_proc onScroll)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- pSB->onScroll = onScroll;
-}
-
-drgui_sb_on_scroll_proc drgui_sb_get_on_scroll(drgui_element* pSBElement)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return NULL;
- }
-
- return pSB->onScroll;
-}
-
-
-drgui_rect drgui_sb_get_thumb_rect(drgui_element* pSBElement)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return drgui_make_rect(0, 0, 0, 0);
- }
-
- drgui_rect rect = {0, 0, 0, 0};
- rect.left = pSB->thumbPadding;
- rect.top = pSB->thumbPadding;
-
- if (pSB->orientation == drgui_sb_orientation_vertical)
- {
- // Vertical.
- rect.left = pSB->thumbPadding;
- rect.right = drgui_get_width(pSBElement) - pSB->thumbPadding;
- rect.top = pSB->thumbPadding + pSB->thumbPos;
- rect.bottom = rect.top + pSB->thumbSize;
- }
- else
- {
- // Horizontal.
- rect.left = pSB->thumbPadding + pSB->thumbPos;
- rect.right = rect.left + pSB->thumbSize;
- rect.top = pSB->thumbPadding;
- rect.bottom = drgui_get_height(pSBElement) - pSB->thumbPadding;
- }
-
- return rect;
-}
-
-
-void drgui_sb_on_size(drgui_element* pSBElement, float newWidth, float newHeight)
-{
- (void)newWidth;
- (void)newHeight;
-
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- drgui_sb_refresh_thumb(pSBElement);
-}
-
-void drgui_sb_on_mouse_leave(drgui_element* pSBElement)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- bool needsRedraw = false;
- if (pSB->thumbHovered)
- {
- needsRedraw = true;
- pSB->thumbHovered = false;
- }
-
- if (pSB->thumbPressed)
- {
- needsRedraw = true;
- pSB->thumbPressed = false;
- }
-
- if (needsRedraw) {
- drgui_dirty(pSBElement, drgui_sb_get_thumb_rect(pSBElement));
- }
-}
-
-void drgui_sb_on_mouse_move(drgui_element* pSBElement, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)stateFlags;
-
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- if (pSB->thumbPressed)
- {
- // The thumb is pressed. Drag it.
- float thumbRelativeMousePosX = (float)relativeMousePosX;
- float thumbRelativeMousePosY = (float)relativeMousePosY;
- drgui_sb_make_relative_to_thumb(pSBElement, &thumbRelativeMousePosX, &thumbRelativeMousePosY);
-
- float dragOffsetX = thumbRelativeMousePosX - pSB->thumbClickPosX;
- float dragOffsetY = thumbRelativeMousePosY - pSB->thumbClickPosY;
-
- float destTrackPos = pSB->thumbPos;
- if (pSB->orientation == drgui_sb_orientation_vertical) {
- destTrackPos += dragOffsetY;
- } else {
- destTrackPos += dragOffsetX;
- }
-
- int destScrollPos = drgui_sb_calculate_scroll_pos_from_thumb_pos(pSBElement, destTrackPos);
- if (destScrollPos != pSB->scrollPos)
- {
- drgui_sb_scroll_to(pSBElement, destScrollPos);
- }
- }
- else
- {
- // The thumb is not pressed. We just need to check if the hovered state has change and redraw if required.
- if (drgui_sb_is_thumb_visible(pSBElement))
- {
- bool wasThumbHovered = pSB->thumbHovered;
-
- drgui_rect thumbRect = drgui_sb_get_thumb_rect(pSBElement);
- pSB->thumbHovered = drgui_rect_contains_point(thumbRect, (float)relativeMousePosX, (float)relativeMousePosY);
-
- if (wasThumbHovered != pSB->thumbHovered) {
- drgui_dirty(pSBElement, thumbRect);
- }
- }
- }
-}
-
-void drgui_sb_on_mouse_button_down(drgui_element* pSBElement, int button, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)stateFlags;
-
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- if (button == DRGUI_MOUSE_BUTTON_LEFT)
- {
- if (drgui_sb_is_thumb_visible(pSBElement))
- {
- drgui_rect thumbRect = drgui_sb_get_thumb_rect(pSBElement);
- if (drgui_rect_contains_point(thumbRect, (float)relativeMousePosX, (float)relativeMousePosY))
- {
- if (!pSB->thumbPressed)
- {
- drgui_capture_mouse(pSBElement);
- pSB->thumbPressed = true;
-
- pSB->thumbClickPosX = (float)relativeMousePosX;
- pSB->thumbClickPosY = (float)relativeMousePosY;
- drgui_sb_make_relative_to_thumb(pSBElement, &pSB->thumbClickPosX, &pSB->thumbClickPosY);
-
- drgui_dirty(pSBElement, drgui_sb_get_thumb_rect(pSBElement));
- }
- }
- else
- {
- // If the click position is above the thumb we want to scroll up by a page. If it's below the thumb, we scroll down by a page.
- if (relativeMousePosY < thumbRect.top) {
- drgui_sb_scroll(pSBElement, -drgui_sb_get_page_size(pSBElement));
- } else if (relativeMousePosY >= thumbRect.bottom) {
- drgui_sb_scroll(pSBElement, drgui_sb_get_page_size(pSBElement));
- }
- }
- }
- }
-}
-
-void drgui_sb_on_mouse_button_up(drgui_element* pSBElement, int button, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)relativeMousePosX;
- (void)relativeMousePosY;
- (void)stateFlags;
-
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- if (button == DRGUI_MOUSE_BUTTON_LEFT)
- {
- if (pSB->thumbPressed && drgui_get_element_with_mouse_capture(pSBElement->pContext) == pSBElement)
- {
- drgui_release_mouse(pSBElement->pContext);
- pSB->thumbPressed = false;
-
- drgui_dirty(pSBElement, drgui_sb_get_thumb_rect(pSBElement));
- }
- }
-}
-
-void drgui_sb_on_mouse_wheel(drgui_element* pSBElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)relativeMousePosX;
- (void)relativeMousePosY;
- (void)stateFlags;
-
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- drgui_sb_scroll(pSBElement, -delta * drgui_sb_get_mouse_wheel_scale(pSBElement));
-}
-
-void drgui_sb_on_paint(drgui_element* pSBElement, drgui_rect relativeClippingRect, void* pPaintData)
-{
- (void)relativeClippingRect;
-
- const drgui_scrollbar* pSB = (const drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- drgui_rect thumbRect = drgui_sb_get_thumb_rect(pSBElement);
-
- if (drgui_sb_is_thumb_visible(pSBElement))
- {
- // The thumb is visible.
-
- // Track. We draw this in 4 seperate pieces so we can avoid overdraw with the thumb.
- drgui_draw_rect(pSBElement, drgui_make_rect(0, 0, drgui_get_width(pSBElement), thumbRect.top), pSB->trackColor, pPaintData); // Top
- drgui_draw_rect(pSBElement, drgui_make_rect(0, thumbRect.bottom, drgui_get_width(pSBElement), drgui_get_height(pSBElement)), pSB->trackColor, pPaintData); // Bottom
- drgui_draw_rect(pSBElement, drgui_make_rect(0, thumbRect.top, thumbRect.left, thumbRect.bottom), pSB->trackColor, pPaintData); // Left
- drgui_draw_rect(pSBElement, drgui_make_rect(thumbRect.right, thumbRect.top, drgui_get_width(pSBElement), thumbRect.bottom), pSB->trackColor, pPaintData); // Right
-
- // Thumb.
- drgui_color thumbColor;
- if (pSB->thumbPressed) {
- thumbColor = pSB->thumbColorPressed;
- } else if (pSB->thumbHovered) {
- thumbColor = pSB->thumbColorHovered;
- } else {
- thumbColor = pSB->thumbColor;
- }
-
- drgui_draw_rect(pSBElement, thumbRect, thumbColor, pPaintData);
- }
- else
- {
- // The thumb is not visible - just draw the track as one quad.
- drgui_draw_rect(pSBElement, drgui_get_local_rect(pSBElement), pSB->trackColor, pPaintData);
- }
-}
-
-
-
-DRGUI_PRIVATE void drgui_sb_refresh_thumb(drgui_element* pSBElement)
-{
- drgui_scrollbar* pSB = (drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- assert(pSB != NULL);
-
- drgui_rect oldThumbRect = drgui_sb_get_thumb_rect(pSBElement);
-
- pSB->thumbSize = drgui_sb_calculate_thumb_size(pSBElement);
- pSB->thumbPos = drgui_sb_calculate_thumb_position(pSBElement);
-
- drgui_rect newThumbRect = drgui_sb_get_thumb_rect(pSBElement);
- if (!drgui_rect_equal(oldThumbRect, newThumbRect))
- {
- drgui_dirty(pSBElement, drgui_rect_union(oldThumbRect, newThumbRect));
- }
-}
-
-DRGUI_PRIVATE float drgui_sb_calculate_thumb_size(drgui_element* pSBElement)
-{
- const drgui_scrollbar* pSB = (const drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- assert(pSB != NULL);
-
- float trackSize = drgui_sb_get_track_size(pSBElement);
- float range = (float)(pSB->rangeMax - pSB->rangeMin + 1);
-
- float thumbSize = DRGUI_MIN_SCROLLBAR_THUMB_SIZE;
- if (range > 0)
- {
- thumbSize = roundf((trackSize / range) * pSB->pageSize);
- thumbSize = drgui_sb_clampf(thumbSize, DRGUI_MIN_SCROLLBAR_THUMB_SIZE, trackSize);
- }
-
- return thumbSize;
-}
-
-DRGUI_PRIVATE float drgui_sb_calculate_thumb_position(drgui_element* pSBElement)
-{
- const drgui_scrollbar* pSB = (const drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- assert(pSB != NULL);
-
- float trackSize = drgui_sb_get_track_size(pSBElement);
- float thumbSize = drgui_sb_calculate_thumb_size(pSBElement);
- float range = (float)(pSB->rangeMax - pSB->rangeMin + 1);
-
- float thumbPos = 0;
- if (range > pSB->pageSize)
- {
- thumbPos = roundf((trackSize / range) * pSB->scrollPos);
- thumbPos = drgui_sb_clampf(thumbPos, 0, trackSize - thumbSize);
- }
-
- return thumbPos;
-}
-
-DRGUI_PRIVATE float drgui_sb_get_track_size(drgui_element* pSBElement)
-{
- const drgui_scrollbar* pSB = (const drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- assert(pSB != NULL);
-
- if (pSB->orientation == drgui_sb_orientation_vertical) {
- return drgui_get_height(pSBElement) - (pSB->thumbPadding*2);
- } else {
- return drgui_get_width(pSBElement) - (pSB->thumbPadding*2);
- }
-}
-
-DRGUI_PRIVATE void drgui_sb_make_relative_to_thumb(drgui_element* pSBElement, float* pPosX, float* pPosY)
-{
- drgui_rect thumbRect = drgui_sb_get_thumb_rect(pSBElement);
-
- if (pPosX != NULL) {
- *pPosX -= thumbRect.left;
- }
-
- if (pPosY != NULL) {
- *pPosY -= thumbRect.top;
- }
-}
-
-DRGUI_PRIVATE int drgui_sb_calculate_scroll_pos_from_thumb_pos(drgui_element* pSBElement, float thumbPos)
-{
- const drgui_scrollbar* pSB = (const drgui_scrollbar*)drgui_get_extra_data(pSBElement);
- assert(pSB != NULL);
-
- float trackSize = drgui_sb_get_track_size(pSBElement);
- float range = (float)(pSB->rangeMax - pSB->rangeMin + 1);
-
- return (int)roundf(thumbPos / (trackSize / range));
-}
-#endif //DR_GUI_IMPLEMENTATION
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-//
-// Tab Bar
-//
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-// QUICK NOTES
-//
-// - This control is only the tab bar itself - this does not handle tab pages and content switching and whatnot.
-
-#ifndef drgui_tab_bar_h
-#define drgui_tab_bar_h
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define DRGUI_MAX_TAB_TEXT_LENGTH 256
-
-typedef enum
-{
- drgui_tabbar_orientation_top,
- drgui_tabbar_orientation_bottom,
- drgui_tabbar_orientation_left,
- drgui_tabbar_orientation_right
-} drgui_tabbar_orientation;
-
-typedef struct drgui_tab drgui_tab;
-
-typedef void (* drgui_tabbar_on_measure_tab_proc) (drgui_element* pTBElement, drgui_tab* pTab, float* pWidthOut, float* pHeightOut);
-typedef void (* drgui_tabbar_on_paint_tab_proc) (drgui_element* pTBElement, drgui_tab* pTab, drgui_rect relativeClippingRect, float offsetX, float offsetY, float width, float height, void* pPaintData);
-typedef void (* drgui_tabbar_on_tab_activated_proc) (drgui_element* pTBElement, drgui_tab* pTab, drgui_tab* pOldActiveTab);
-typedef void (* drgui_tabbar_on_tab_deactivated_proc) (drgui_element* pTBElement, drgui_tab* pTab, drgui_tab* pNewActiveTab);
-typedef void (* drgui_tabbar_on_tab_close_proc) (drgui_element* pTBElement, drgui_tab* pTab);
-typedef void (* drgui_tabbar_on_tab_mouse_button_up_proc)(drgui_element* pTBElement, drgui_tab* pTab, int mouseButton, int mouseRelativePosX, int mouseRelativePosY, int stateFlags);
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// Tab Bar
-//
-///////////////////////////////////////////////////////////////////////////////
-
-/// Creates a new tab bar control.
-drgui_element* drgui_create_tab_bar(drgui_context* pContext, drgui_element* pParent, drgui_tabbar_orientation orientation, size_t extraDataSize, const void* pExtraData);
-
-/// Deletes the given tab bar control.
-void drgui_delete_tab_bar(drgui_element* pTBElement);
-
-
-/// Retrieves the size of the extra data associated with the scrollbar.
-size_t drgui_tabbar_get_extra_data_size(drgui_element* pTBElement);
-
-/// Retrieves a pointer to the extra data associated with the scrollbar.
-void* drgui_tabbar_get_extra_data(drgui_element* pTBElement);
-
-/// Retrieves the orientation of the given scrollbar.
-drgui_tabbar_orientation drgui_tabbar_get_orientation(drgui_element* pTBElement);
-
-
-/// Sets the default font to use for tabs.
-void drgui_tabbar_set_font(drgui_element* pTBElement, drgui_font* pFont);
-
-/// Retrieves the default font to use for tabs.
-drgui_font* drgui_tabbar_get_font(drgui_element* pTBElement);
-
-// Sets the color of the text to use on tabs.
-void drgui_tabbar_set_text_color(drgui_element* pTBElement, drgui_color color);
-
-// Retrieves the color of the text to use on tabs.
-drgui_color drgui_tabbar_get_text_color(drgui_element* pTBElement);
-
-// Sets the color of the text to use on active tabs.
-void drgui_tabbar_set_text_color_active(drgui_element* pTBElement, drgui_color color);
-
-// Sets the color of the text to use on hovered tabs.
-void drgui_tabbar_set_text_color_hovered(drgui_element* pTBElement, drgui_color color);
-
-/// Sets the image to use for close buttons.
-void drgui_tabbar_set_close_button_image(drgui_element* pTBElement, drgui_image* pImage);
-
-/// Retrieves the image being used for the close buttons.
-drgui_image* drgui_tabbar_get_close_button_image(drgui_element* pTBElement);
-
-// Sets the default color of the close button.
-void drgui_tabbar_set_close_button_color(drgui_element* pTBElement, drgui_color color);
-
-// Sets the padding to apply the the text of each tab.
-void drgui_tabbar_set_tab_padding(drgui_element* pTBElement, float padding);
-
-// Retrieves the padding to apply to the text of each tab.
-float drgui_tabbar_get_tab_padding(drgui_element* pTBElement);
-
-// Sets the padding to apply the the left of the close button.
-void drgui_tabbar_set_close_button_left_padding(drgui_element* pTBElement, float padding);
-
-// Retrieves the padding to apply to the left of the close button.
-float drgui_tabbar_get_close_button_left_padding(drgui_element* pTBElement);
-
-// Sets the default background color of tabs. This is the color of inactive tabs.
-void drgui_tabbar_set_tab_background_color(drgui_element* pTBElement, drgui_color color);
-
-// Retrieves the default background color of tabs while inactive.
-drgui_color drgui_tabbar_get_tab_background_color(drgui_element* pTBElement);
-
-// Sets the background color of tabs while hovered.
-void drgui_tabbar_set_tab_background_color_hovered(drgui_element* pTBElement, drgui_color color);
-
-// Retrieves the background color of tabs while hovered.
-drgui_color drgui_tabbar_get_tab_background_color_hovered(drgui_element* pTBElement);
-
-// Sets the background color of tabs while activated.
-void drgui_tabbar_set_tab_background_color_active(drgui_element* pTBElement, drgui_color color);
-
-// Retrieves the background color of tabs while activated.
-drgui_color drgui_tabbar_get_tab_background_color_actived(drgui_element* pTBElement);
-
-
-/// Sets the function to call when a tab needs to be measured.
-void drgui_tabbar_set_on_measure_tab(drgui_element* pTBElement, drgui_tabbar_on_measure_tab_proc proc);
-
-/// Sets the function to call when a tab needs to be painted.
-void drgui_tabbar_set_on_paint_tab(drgui_element* pTBElement, drgui_tabbar_on_paint_tab_proc proc);
-
-/// Sets the function to call when a tab is activated.
-void drgui_tabbar_set_on_tab_activated(drgui_element* pTBElement, drgui_tabbar_on_tab_activated_proc proc);
-
-/// Sets the function to call when a tab is deactivated.
-void drgui_tabbar_set_on_tab_deactivated(drgui_element* pTBElement, drgui_tabbar_on_tab_deactivated_proc proc);
-
-/// Sets the function to call when a tab is closed with the close button.
-void drgui_tabbar_set_on_tab_closed(drgui_element* pTBElement, drgui_tabbar_on_tab_close_proc proc);
-
-// Sets the function to call when a tab has a mouse button released on it.
-void drgui_tabbar_set_on_tab_mouse_button_up(drgui_element* pTBElement, drgui_tabbar_on_tab_mouse_button_up_proc proc);
-
-
-/// Measures the given tab.
-void drgui_tabbar_measure_tab(drgui_element* pTBElement, drgui_tab* pTab, float* pWidthOut, float* pHeightOut);
-
-/// Paints the given tab.
-void drgui_tabbar_paint_tab(drgui_element* pTBElement, drgui_tab* pTab, drgui_rect relativeClippingRect, float offsetX, float offsetY, float width, float height, void* pPaintData);
-
-
-/// Sets the width or height of the tab bar to that of it's tabs based on it's orientation.
-///
-/// @remarks
-/// If the orientation is set to top or bottom, the height will be resized and the width will be left alone. If the orientation
-/// is left or right, the width will be resized and the height will be left alone.
-/// @par
-/// If there is no tab measuring callback set, this will do nothing.
-void drgui_tabbar_resize_by_tabs(drgui_element* pTBElement);
-
-/// Enables auto-resizing based on tabs.
-///
-/// @remarks
-/// This follows the same resizing rules as per drgui_tabbar_resize_by_tabs().
-///
-/// @see
-/// drgui_tabbar_resize_by_tabs()
-void drgui_tabbar_enable_auto_size(drgui_element* pTBElement);
-
-/// Disables auto-resizing based on tabs.
-void drgui_tabbar_disable_auto_size(drgui_element* pTBElement);
-
-/// Determines whether or not auto-sizing is enabled.
-bool drgui_tabbar_is_auto_size_enabled(drgui_element* pTBElement);
-
-
-// Retrieves a pointer to the first tab in the given tab bar.
-drgui_tab* drgui_tabbar_get_first_tab(drgui_element* pTBElement);
-
-// Retrieves a pointer to the last tab in the given tab bar.
-drgui_tab* drgui_tabbar_get_last_tab(drgui_element* pTBElement);
-
-// Retrieves a pointer to the next tab in the given tab bar.
-drgui_tab* drgui_tabbar_get_next_tab(drgui_element* pTBElement, drgui_tab* pTab);
-
-// Retrieves a pointer to the previous tab in the given tab bar.
-drgui_tab* drgui_tabbar_get_prev_tab(drgui_element* pTBElement, drgui_tab* pTab);
-
-
-/// Activates the given tab.
-void drgui_tabbar_activate_tab(drgui_element* pTBElement, drgui_tab* pTab);
-
-// Activates the tab to the right of the currently active tab, looping back to the start if necessary.
-void drgui_tabbar_activate_next_tab(drgui_element* pTBElement);
-
-// Activates the tab to the left of the currently active tab, looping back to the end if necessary.
-void drgui_tabbar_activate_prev_tab(drgui_element* pTBElement);
-
-/// Retrieves a pointer to the currently active tab.
-drgui_tab* drgui_tabbar_get_active_tab(drgui_element* pTBElement);
-
-
-/// Determines whether or not the given tab is in view.
-bool drgui_tabbar_is_tab_in_view(drgui_element* pTBElement, drgui_tab* pTab);
-
-
-/// Shows the close buttons on each tab.
-void drgui_tabbar_show_close_buttons(drgui_element* pTBElement);
-
-/// Hides the close buttons on each tab.
-void drgui_tabbar_hide_close_buttons(drgui_element* pTBElement);
-
-/// Enables the on_close event on middle click.
-void drgui_tabbar_enable_close_on_middle_click(drgui_element* pTBElement);
-
-/// Disables the on_close event on middle click.
-void drgui_tabbar_disable_close_on_middle_click(drgui_element* pTBElement);
-
-/// Determines whether or not close-on-middle-click is enabled.
-bool drgui_tabbar_is_close_on_middle_click_enabled(drgui_element* pTBElement);
-
-
-/// Called when the mouse leave event needs to be processed for the given tab bar control.
-void drgui_tabbar_on_mouse_leave(drgui_element* pTBElement);
-
-/// Called when the mouse move event needs to be processed for the given tab bar control.
-void drgui_tabbar_on_mouse_move(drgui_element* pTBElement, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// Called when the mouse button down event needs to be processed for the given tab bar control.
-void drgui_tabbar_on_mouse_button_down(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// Called when the mouse button up event needs to be processed for the given tab bar control.
-void drgui_tabbar_on_mouse_button_up(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// Called when the paint event needs to be processed for the given tab control.
-void drgui_tabbar_on_paint(drgui_element* pTBElement, drgui_rect relativeClippingRect, void* pPaintData);
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// Tab
-//
-///////////////////////////////////////////////////////////////////////////////
-
-/// Creates and appends a tab
-drgui_tab* drgui_tabbar_create_and_append_tab(drgui_element* pTBElement, const char* text, size_t extraDataSize, const void* pExtraData);
-
-/// Creates and prepends a tab.
-drgui_tab* drgui_tabbar_create_and_prepend_tab(drgui_element* pTBElement, const char* text, size_t extraDataSize, const void* pExtraData);
-
-/// Recursively deletes a tree view item.
-void drgui_tab_delete(drgui_tab* pTab);
-
-/// Retrieves the tab bar GUI element that owns the given item.
-drgui_element* drgui_tab_get_tab_bar_element(drgui_tab* pTab);
-
-/// Retrieves the size of the extra data associated with the given tree-view item.
-size_t drgui_tab_get_extra_data_size(drgui_tab* pTab);
-
-/// Retrieves a pointer to the extra data associated with the given tree-view item.
-void* drgui_tab_get_extra_data(drgui_tab* pTab);
-
-
-/// Sets the text of the given tab bar item.
-void drgui_tab_set_text(drgui_tab* pTab, const char* text);
-
-/// Retrieves the text of the given tab bar item.
-const char* drgui_tab_get_text(drgui_tab* pTab);
-
-
-/// Retrieves a pointer to the next tab in the tab bar.
-drgui_tab* drgui_tab_get_next_tab(drgui_tab* pTab);
-
-/// Retrieves a pointer to the previous tab in the tab bar.
-drgui_tab* drgui_tab_get_prev_tab(drgui_tab* pTab);
-
-
-/// Moves the given tab to the front of the tab bar that owns it.
-void drgui_tab_move_to_front(drgui_tab* pTab);
-
-/// Determines whether or not the given tab is in view.
-bool drgui_tab_is_in_view(drgui_tab* pTab);
-
-/// Moves the given tab into view, if it's not already.
-///
-/// If the tab is out of view, it will be repositioned to the front of the tab bar.
-void drgui_tab_move_into_view(drgui_tab* pTab);
-
-
-#ifdef __cplusplus
-}
-#endif
-#endif //drgui_tab_bar_h
-
-
-#ifdef DR_GUI_IMPLEMENTATION
-typedef struct drgui_tab_bar drgui_tab_bar;
-
-struct drgui_tab_bar
-{
- /// The orientation.
- drgui_tabbar_orientation orientation;
-
-
- /// A pointer to the first tab.
- drgui_tab* pFirstTab;
-
- /// A pointer to the last tab.
- drgui_tab* pLastTab;
-
-
- /// A pointer to the hovered tab.
- drgui_tab* pHoveredTab;
-
- /// A pointer to the active tab.
- drgui_tab* pActiveTab;
-
- /// The tab whose close button is currently pressed, if any.
- drgui_tab* pTabWithCloseButtonPressed;
-
-
- /// The default font to use for tab bar items.
- drgui_font* pFont;
-
- /// The default color to use for tab bar item text.
- drgui_color tabTextColor;
-
- /// The default color to use for tab bar item text while active.
- drgui_color tabTextColorActivated;
-
- /// The default color to use for tab bar item text while hovered.
- drgui_color tabTextColorHovered;
-
- /// The default background color of tab bar items.
- drgui_color tabBackgroundColor;
-
- /// The background color of tab bar items while hovered.
- drgui_color tabBackgroundColorHovered;
-
- /// The background color of tab bar items while selected.
- drgui_color tabBackbroundColorActivated;
-
- /// The padding to apply to the text of tabs.
- float tabPadding;
-
- /// The image to use for the close button.
- drgui_image* pCloseButtonImage;
-
- /// The padding to the left of the close button.
- float closeButtonPaddingLeft;
-
- /// The default color of the close button.
- drgui_color closeButtonColorDefault;
-
- /// The color of the close button when the tab is hovered, but not the close button itself.
- drgui_color closeButtonColorTabHovered;
-
- /// The color of the close button when it is hovered.
- drgui_color closeButtonColorHovered;
-
- /// The color of the close button when it is pressed.
- drgui_color closeButtonColorPressed;
-
-
- /// Whether or not auto-sizing is enabled. Disabled by default.
- bool isAutoSizeEnabled;
-
- /// Whether or not the close buttons are being shown.
- bool isShowingCloseButton;
-
- /// Whether or not close-on-middle-click is enabled.
- bool isCloseOnMiddleClickEnabled;
-
- /// Whether or not the close button is hovered.
- bool isCloseButtonHovered;
-
-
- /// The function to call when a tab needs to be measured.
- drgui_tabbar_on_measure_tab_proc onMeasureTab;
-
- /// The function to call when a tab needs to be painted.
- drgui_tabbar_on_paint_tab_proc onPaintTab;
-
- /// The function to call when a tab is activated.
- drgui_tabbar_on_tab_activated_proc onTabActivated;
-
- /// The function to call when a tab is deactivated.
- drgui_tabbar_on_tab_deactivated_proc onTabDeactivated;
-
- /// The function to call when a tab is closed via the close button.
- drgui_tabbar_on_tab_close_proc onTabClose;
-
- // The function to call when a mouse button is released while over a tab.
- drgui_tabbar_on_tab_mouse_button_up_proc onTabMouseButtonUp;
-
-
- /// The size of the extra data.
- size_t extraDataSize;
-
- /// A pointer to the extra data.
- char pExtraData[1];
-};
-
-struct drgui_tab
-{
- /// The tab bar that owns the tab.
- drgui_element* pTBElement;
-
- /// A pointer to the next tab in the tab bar.
- drgui_tab* pNextTab;
-
- /// A pointer to the previous tab in the tab bar.
- drgui_tab* pPrevTab;
-
-
- /// The tab bar's text.
- char text[DRGUI_MAX_TAB_TEXT_LENGTH];
-
-
- /// The size of the extra data.
- size_t extraDataSize;
-
- /// A pointer to the extra data buffer.
- char pExtraData[1];
-};
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// Tab
-//
-///////////////////////////////////////////////////////////////////////////////
-
-/// Default implementation of the item measure event.
-DRGUI_PRIVATE void drgui_tabbar_on_measure_tab_default(drgui_element* pTBElement, drgui_tab* pTab, float* pWidthOut, float* pHeightOut);
-
-/// Paints the given menu item.
-DRGUI_PRIVATE void drgui_tabbar_on_paint_tab_default(drgui_element* pTBElement, drgui_tab* pTab, drgui_rect relativeClippingRect, float offsetX, float offsetY, float width, float height, void* pPaintData);
-
-/// Finds the tab sitting under the given point, if any.
-DRGUI_PRIVATE drgui_tab* drgui_tabbar_find_tab_under_point(drgui_element* pTBElement, float relativePosX, float relativePosY, bool* pIsOverCloseButtonOut);
-
-drgui_element* drgui_create_tab_bar(drgui_context* pContext, drgui_element* pParent, drgui_tabbar_orientation orientation, size_t extraDataSize, const void* pExtraData)
-{
- if (pContext == NULL) {
- return NULL;
- }
-
- drgui_element* pTBElement = drgui_create_element(pContext, pParent, sizeof(drgui_tab_bar) + extraDataSize, NULL);
- if (pTBElement == NULL) {
- return NULL;
- }
-
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- assert(pTB != NULL);
-
- pTB->orientation = orientation;
- pTB->pFirstTab = NULL;
- pTB->pLastTab = NULL;
- pTB->pHoveredTab = NULL;
- pTB->pActiveTab = NULL;
- pTB->pTabWithCloseButtonPressed = NULL;
-
- pTB->pFont = NULL;
- pTB->tabTextColor = drgui_rgb(224, 224, 224);
- pTB->tabTextColorActivated = drgui_rgb(224, 224, 224);
- pTB->tabTextColorHovered = drgui_rgb(224, 224, 224);
- pTB->tabBackgroundColor = drgui_rgb(58, 58, 58);
- pTB->tabBackgroundColorHovered = drgui_rgb(16, 92, 160);
- pTB->tabBackbroundColorActivated = drgui_rgb(32, 128, 192); //drgui_rgb(80, 80, 80);
- pTB->tabPadding = 4;
- pTB->pCloseButtonImage = NULL;
- pTB->closeButtonPaddingLeft = 6;
- pTB->closeButtonColorDefault = pTB->tabBackgroundColor;
- pTB->closeButtonColorTabHovered = drgui_rgb(192, 192, 192);
- pTB->closeButtonColorHovered = drgui_rgb(255, 96, 96);
- pTB->closeButtonColorPressed = drgui_rgb(192, 32, 32);
- pTB->isAutoSizeEnabled = false;
- pTB->isShowingCloseButton = false;
- pTB->isCloseOnMiddleClickEnabled = false;
- pTB->isCloseButtonHovered = false;
-
- pTB->onMeasureTab = drgui_tabbar_on_measure_tab_default;
- pTB->onPaintTab = drgui_tabbar_on_paint_tab_default;
- pTB->onTabActivated = NULL;
- pTB->onTabDeactivated = NULL;
- pTB->onTabClose = NULL;
-
-
- pTB->extraDataSize = extraDataSize;
- if (pExtraData != NULL) {
- memcpy(pTB->pExtraData, pExtraData, extraDataSize);
- }
-
-
- // Event handlers.
- drgui_set_on_mouse_leave(pTBElement, drgui_tabbar_on_mouse_leave);
- drgui_set_on_mouse_move(pTBElement, drgui_tabbar_on_mouse_move);
- drgui_set_on_mouse_button_down(pTBElement, drgui_tabbar_on_mouse_button_down);
- drgui_set_on_mouse_button_up(pTBElement, drgui_tabbar_on_mouse_button_up);
- drgui_set_on_paint(pTBElement, drgui_tabbar_on_paint);
-
- return pTBElement;
-}
-
-void drgui_delete_tab_bar(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- while (pTB->pFirstTab != NULL) {
- drgui_tab_delete(pTB->pFirstTab);
- }
-
-
- drgui_delete_element(pTBElement);
-}
-
-
-size_t drgui_tabbar_get_extra_data_size(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return 0;
- }
-
- return pTB->extraDataSize;
-}
-
-void* drgui_tabbar_get_extra_data(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return NULL;
- }
-
- return pTB->pExtraData;
-}
-
-drgui_tabbar_orientation drgui_tabbar_get_orientation(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return drgui_tabbar_orientation_top;
- }
-
- return pTB->orientation;
-}
-
-
-void drgui_tabbar_set_font(drgui_element* pTBElement, drgui_font* pFont)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->pFont = pFont;
-
- // A change in font may have changed the size of the tabbar.
- if (pTB->isAutoSizeEnabled) {
- drgui_tabbar_resize_by_tabs(pTBElement);
- }
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
-}
-
-drgui_font* drgui_tabbar_get_font(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return NULL;
- }
-
- return pTB->pFont;
-}
-
-
-void drgui_tabbar_set_text_color(drgui_element* pTBElement, drgui_color color)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->tabTextColor = color;
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
-}
-
-drgui_color drgui_tabbar_get_text_color(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return drgui_rgb(0, 0, 0);
- }
-
- return pTB->tabTextColor;
-}
-
-void drgui_tabbar_set_text_color_active(drgui_element* pTBElement, drgui_color color)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->tabTextColorActivated = color;
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
-}
-
-void drgui_tabbar_set_text_color_hovered(drgui_element* pTBElement, drgui_color color)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->tabTextColorHovered = color;
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
-}
-
-
-void drgui_tabbar_set_close_button_image(drgui_element* pTBElement, drgui_image* pImage)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->pCloseButtonImage = pImage;
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
-}
-
-drgui_image* drgui_tabbar_get_close_button_image(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return NULL;
- }
-
- return pTB->pCloseButtonImage;
-}
-
-void drgui_tabbar_set_close_button_color(drgui_element* pTBElement, drgui_color color)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->closeButtonColorDefault = color;
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
-}
-
-
-void drgui_tabbar_set_tab_padding(drgui_element* pTBElement, float padding)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->tabPadding = padding;
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
-}
-
-float drgui_tabbar_get_tab_padding(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return 0;
- }
-
- return pTB->tabPadding;
-}
-
-void drgui_tabbar_set_close_button_left_padding(drgui_element* pTBElement, float padding)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->closeButtonPaddingLeft = padding;
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
-}
-
-float drgui_tabbar_get_close_button_left_padding(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return 0;
- }
-
- return pTB->closeButtonPaddingLeft;
-}
-
-
-void drgui_tabbar_set_tab_background_color(drgui_element* pTBElement, drgui_color color)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->tabBackgroundColor = color;
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
-}
-
-drgui_color drgui_tabbar_get_tab_background_color(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return drgui_rgb(0, 0, 0);
- }
-
- return pTB->tabBackgroundColor;
-}
-
-void drgui_tabbar_set_tab_background_color_hovered(drgui_element* pTBElement, drgui_color color)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->tabBackgroundColorHovered = color;
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
-}
-
-drgui_color drgui_tabbar_get_tab_background_color_hovered(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return drgui_rgb(0, 0, 0);
- }
-
- return pTB->tabBackgroundColorHovered;
-}
-
-void drgui_tabbar_set_tab_background_color_active(drgui_element* pTBElement, drgui_color color)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->tabBackbroundColorActivated = color;
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
-}
-
-drgui_color drgui_tabbar_get_tab_background_color_actived(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return drgui_rgb(0, 0, 0);
- }
-
- return pTB->tabBackbroundColorActivated;
-}
-
-
-void drgui_tabbar_set_on_measure_tab(drgui_element* pTBElement, drgui_tabbar_on_measure_tab_proc proc)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->onMeasureTab = proc;
-}
-
-void drgui_tabbar_set_on_paint_tab(drgui_element* pTBElement, drgui_tabbar_on_paint_tab_proc proc)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->onPaintTab = proc;
-}
-
-void drgui_tabbar_set_on_tab_activated(drgui_element* pTBElement, drgui_tabbar_on_tab_activated_proc proc)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->onTabActivated = proc;
-}
-
-void drgui_tabbar_set_on_tab_deactivated(drgui_element* pTBElement, drgui_tabbar_on_tab_deactivated_proc proc)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->onTabDeactivated = proc;
-}
-
-void drgui_tabbar_set_on_tab_closed(drgui_element* pTBElement, drgui_tabbar_on_tab_close_proc proc)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->onTabClose = proc;
-}
-
-void drgui_tabbar_set_on_tab_mouse_button_up(drgui_element* pTBElement, drgui_tabbar_on_tab_mouse_button_up_proc proc)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->onTabMouseButtonUp = proc;
-}
-
-
-void drgui_tabbar_measure_tab(drgui_element* pTBElement, drgui_tab* pTab, float* pWidthOut, float* pHeightOut)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- if (pTB->onMeasureTab) {
- pTB->onMeasureTab(pTBElement, pTab, pWidthOut, pHeightOut);
- }
-}
-
-void drgui_tabbar_paint_tab(drgui_element* pTBElement, drgui_tab* pTab, drgui_rect relativeClippingRect, float offsetX, float offsetY, float width, float height, void* pPaintData)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- if (pTB->onPaintTab) {
- pTB->onPaintTab(pTBElement, pTab, relativeClippingRect, offsetX, offsetY, width, height, pPaintData);
- }
-}
-
-
-void drgui_tabbar_resize_by_tabs(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- if (pTB->onMeasureTab == NULL) {
- return;
- }
-
- float maxWidth = 0;
- float maxHeight = 0;
- if (pTB->pFirstTab == NULL) {
- // There are no tabs. Set initial size based on the line height of the font.
- drgui_font_metrics fontMetrics;
- if (drgui_get_font_metrics(pTB->pFont, &fontMetrics)) {
- if (pTB->orientation == drgui_tabbar_orientation_top || pTB->orientation == drgui_tabbar_orientation_bottom) {
- maxHeight = fontMetrics.lineHeight + (pTB->tabPadding*2);
- } else {
- maxWidth = fontMetrics.lineHeight + (pTB->tabPadding*2);
- }
- }
- } else {
- for (drgui_tab* pTab = pTB->pFirstTab; pTab != NULL; pTab = pTab->pNextTab) {
- float tabWidth = 0;
- float tabHeight = 0;
- drgui_tabbar_measure_tab(pTBElement, pTab, &tabWidth, &tabHeight);
-
- maxWidth = (tabWidth > maxWidth) ? tabWidth : maxWidth;
- maxHeight = (tabHeight > maxHeight) ? tabHeight : maxHeight;
- }
- }
-
-
-
-
- if (pTB->orientation == drgui_tabbar_orientation_top || pTB->orientation == drgui_tabbar_orientation_bottom) {
- drgui_set_size(pTBElement, drgui_get_width(pTBElement), maxHeight);
- } else {
- drgui_set_size(pTBElement, maxWidth, drgui_get_height(pTBElement));
- }
-}
-
-void drgui_tabbar_enable_auto_size(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->isAutoSizeEnabled = true;
-}
-
-void drgui_tabbar_disable_auto_size(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->isAutoSizeEnabled = false;
-}
-
-bool drgui_tabbar_is_auto_size_enabled(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return false;
- }
-
- return pTB->isAutoSizeEnabled;
-}
-
-
-drgui_tab* drgui_tabbar_get_first_tab(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return NULL;
- }
-
- return pTB->pFirstTab;
-}
-
-drgui_tab* drgui_tabbar_get_last_tab(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return NULL;
- }
-
- return pTB->pLastTab;
-}
-
-drgui_tab* drgui_tabbar_get_next_tab(drgui_element* pTBElement, drgui_tab* pTab)
-{
- (void)pTBElement;
- return drgui_tab_get_next_tab(pTab);
-}
-
-drgui_tab* drgui_tabbar_get_prev_tab(drgui_element* pTBElement, drgui_tab* pTab)
-{
- (void)pTBElement;
- return drgui_tab_get_prev_tab(pTab);
-}
-
-
-void drgui_tabbar_activate_tab(drgui_element* pTBElement, drgui_tab* pTab)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_tab* pOldActiveTab = pTB->pActiveTab;
- drgui_tab* pNewActiveTab = pTab;
-
- if (pOldActiveTab == pNewActiveTab) {
- return; // The tab is already active - nothing to do.
- }
-
-
- pTB->pActiveTab = pNewActiveTab;
-
- if (pTB->onTabDeactivated && pOldActiveTab != NULL) {
- pTB->onTabDeactivated(pTBElement, pOldActiveTab, pNewActiveTab);
- }
-
- if (pTB->onTabActivated && pNewActiveTab != NULL) {
- pTB->onTabActivated(pTBElement, pNewActiveTab, pOldActiveTab);
- }
-
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
-}
-
-void drgui_tabbar_activate_next_tab(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- if (pTB->pActiveTab == NULL) {
- drgui_tabbar_activate_tab(pTBElement, pTB->pFirstTab);
- return;
- }
-
-
- drgui_tab* pNextTab = pTB->pActiveTab->pNextTab;
- if (pNextTab == NULL) {
- pNextTab = pTB->pFirstTab;
- }
-
- drgui_tabbar_activate_tab(pTBElement, pNextTab);
-}
-
-void drgui_tabbar_activate_prev_tab(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- if (pTB->pActiveTab == NULL) {
- drgui_tabbar_activate_tab(pTBElement, pTB->pLastTab);
- return;
- }
-
-
- drgui_tab* pPrevTab = pTB->pActiveTab->pPrevTab;
- if (pPrevTab == NULL) {
- pPrevTab = pTB->pLastTab;
- }
-
- drgui_tabbar_activate_tab(pTBElement, pPrevTab);
-}
-
-drgui_tab* drgui_tabbar_get_active_tab(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return NULL;
- }
-
- return pTB->pActiveTab;
-}
-
-
-bool drgui_tabbar_is_tab_in_view(drgui_element* pTBElement, drgui_tab* pTabIn)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return false;
- }
-
- float tabbarWidth = 0;
- float tabbarHeight = 0;
- drgui_get_size(pTBElement, &tabbarWidth, &tabbarHeight);
-
-
- // Each tab.
- float runningPosX = 0;
- float runningPosY = 0;
- for (drgui_tab* pTab = pTB->pFirstTab; pTab != NULL; pTab = pTab->pNextTab)
- {
- float tabWidth = 0;
- float tabHeight = 0;
- drgui_tabbar_measure_tab(pTBElement, pTab, &tabWidth, &tabHeight);
-
- if (pTab == pTabIn) {
- return runningPosX + tabWidth <= tabbarWidth && runningPosY + tabHeight <= tabbarHeight;
- }
-
-
- if (pTB->orientation == drgui_tabbar_orientation_top || pTB->orientation == drgui_tabbar_orientation_bottom) {
- runningPosX += tabWidth;
- } else {
- runningPosY += tabHeight;
- }
- }
-
- return false;
-}
-
-
-void drgui_tabbar_show_close_buttons(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->isShowingCloseButton = true;
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
-}
-
-void drgui_tabbar_hide_close_buttons(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->isShowingCloseButton = false;
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
-}
-
-
-void drgui_tabbar_enable_close_on_middle_click(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->isCloseOnMiddleClickEnabled = true;
-}
-
-void drgui_tabbar_disable_close_on_middle_click(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->isCloseOnMiddleClickEnabled = false;
-}
-
-bool drgui_tabbar_is_close_on_middle_click_enabled(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return false;
- }
-
- return pTB->isCloseOnMiddleClickEnabled;
-}
-
-
-void drgui_tabbar_on_mouse_leave(drgui_element* pTBElement)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- if (pTB->pHoveredTab != NULL)
- {
- pTB->pHoveredTab = NULL;
- pTB->isCloseButtonHovered = false;
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
- }
-}
-
-void drgui_tabbar_on_mouse_move(drgui_element* pTBElement, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)stateFlags;
-
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- bool isCloseButtonHovered = false;
-
- drgui_tab* pOldHoveredTab = pTB->pHoveredTab;
- drgui_tab* pNewHoveredTab = drgui_tabbar_find_tab_under_point(pTBElement, (float)relativeMousePosX, (float)relativeMousePosY, &isCloseButtonHovered);
-
- if (pOldHoveredTab != pNewHoveredTab || pTB->isCloseButtonHovered != isCloseButtonHovered)
- {
- pTB->pHoveredTab = pNewHoveredTab;
- pTB->isCloseButtonHovered = isCloseButtonHovered;
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
- }
-}
-
-void drgui_tabbar_on_mouse_button_down(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)stateFlags;
-
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- if (mouseButton == DRGUI_MOUSE_BUTTON_LEFT || mouseButton == DRGUI_MOUSE_BUTTON_RIGHT)
- {
- bool isOverCloseButton = false;
-
- drgui_tab* pOldActiveTab = pTB->pActiveTab;
- drgui_tab* pNewActiveTab = drgui_tabbar_find_tab_under_point(pTBElement, (float)relativeMousePosX, (float)relativeMousePosY, &isOverCloseButton);
-
- if (pNewActiveTab != NULL && pOldActiveTab != pNewActiveTab && !isOverCloseButton) {
- drgui_tabbar_activate_tab(pTBElement, pNewActiveTab);
- }
-
- if (isOverCloseButton && mouseButton == DRGUI_MOUSE_BUTTON_LEFT) {
- pTB->pTabWithCloseButtonPressed = pNewActiveTab;
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
- }
- }
- else if (mouseButton == DRGUI_MOUSE_BUTTON_MIDDLE)
- {
- if (pTB->isCloseOnMiddleClickEnabled)
- {
- drgui_tab* pHoveredTab = drgui_tabbar_find_tab_under_point(pTBElement, (float)relativeMousePosX, (float)relativeMousePosY, NULL);
- if (pHoveredTab != NULL) {
- if (pTB->onTabClose) {
- pTB->onTabClose(pTBElement, pHoveredTab);
- }
- }
- }
- }
-}
-
-void drgui_tabbar_on_mouse_button_up(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)stateFlags;
-
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
-
- bool releasedOverCloseButton = false;
- drgui_tab* pTabUnderMouse = drgui_tabbar_find_tab_under_point(pTBElement, (float)relativeMousePosX, (float)relativeMousePosY, &releasedOverCloseButton);
-
- if (pTB->pTabWithCloseButtonPressed != NULL && mouseButton == DRGUI_MOUSE_BUTTON_LEFT)
- {
- if (releasedOverCloseButton && pTabUnderMouse == pTB->pTabWithCloseButtonPressed) {
- if (pTB->onTabClose) {
- pTB->onTabClose(pTBElement, pTB->pTabWithCloseButtonPressed);
- }
- }
-
-
- pTB->pTabWithCloseButtonPressed = NULL;
-
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
- }
- else
- {
- if (!releasedOverCloseButton && pTB->onTabMouseButtonUp) {
- // TODO: Improve this by passing the mouse position relative to the tab. Currently it is relative to the tab BAR. Can have
- // the drgui_tabbar_find_tab_under_point() function return the position relative to the tab.
- pTB->onTabMouseButtonUp(pTBElement, pTabUnderMouse, mouseButton, relativeMousePosX, relativeMousePosY, stateFlags);
- }
- }
-}
-
-void drgui_tabbar_on_paint(drgui_element* pTBElement, drgui_rect relativeClippingRect, void* pPaintData)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
-
- float tabbarWidth = 0;
- float tabbarHeight = 0;
- drgui_get_size(pTBElement, &tabbarWidth, &tabbarHeight);
-
-
- // Each tab.
- float runningPosX = 0;
- float runningPosY = 0;
- for (drgui_tab* pTab = pTB->pFirstTab; pTab != NULL; pTab = pTab->pNextTab)
- {
- float tabWidth = 0;
- float tabHeight = 0;
- drgui_tabbar_measure_tab(pTBElement, pTab, &tabWidth, &tabHeight);
-
- // If a part of the tab is out of bounds, stop drawing.
- if (runningPosX > tabbarWidth || runningPosY > tabbarHeight) {
- break;
- }
-
-
- drgui_tabbar_paint_tab(pTBElement, pTab, relativeClippingRect, runningPosX, runningPosY, tabWidth, tabHeight, pPaintData);
-
- // After painting the tab, there may be a region of the background that was not drawn by the tab painting callback. We'll need to
- // draw that here.
- if (pTB->orientation == drgui_tabbar_orientation_top || pTB->orientation == drgui_tabbar_orientation_bottom) {
- drgui_draw_rect(pTBElement, drgui_make_rect(runningPosX, runningPosY + tabHeight, tabbarWidth, tabbarHeight), pTB->tabBackgroundColor, pPaintData);
- } else {
- drgui_draw_rect(pTBElement, drgui_make_rect(runningPosX + tabWidth, runningPosY, tabbarWidth, runningPosY + tabHeight), pTB->tabBackgroundColor, pPaintData);
- }
-
-
-
- if (pTB->orientation == drgui_tabbar_orientation_top || pTB->orientation == drgui_tabbar_orientation_bottom) {
- runningPosX += tabWidth;
- } else {
- runningPosY += tabHeight;
- }
- }
-
-
- // Background. We just draw a quad around the region that is not covered by items.
- drgui_draw_rect(pTBElement, drgui_make_rect(runningPosX, runningPosY, tabbarWidth, tabbarHeight), pTB->tabBackgroundColor, pPaintData);
-}
-
-
-DRGUI_PRIVATE void drgui_tabbar_on_measure_tab_default(drgui_element* pTBElement, drgui_tab* pTab, float* pWidthOut, float* pHeightOut)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- float textWidth = 0;
- float textHeight = 0;
-
- if (pTab != NULL) {
- drgui_measure_string(pTB->pFont, pTab->text, strlen(pTab->text), &textWidth, &textHeight);
- }
-
-
- float closeButtonWidth = 0;
- if (pTB->isShowingCloseButton && pTB->pCloseButtonImage != NULL) {
- unsigned int closeImageWidth;
- drgui_get_image_size(pTB->pCloseButtonImage, &closeImageWidth, NULL);
-
- closeButtonWidth = closeImageWidth + pTB->closeButtonPaddingLeft;
- }
-
-
- if (pWidthOut) {
- *pWidthOut = textWidth + closeButtonWidth + pTB->tabPadding*2;
- }
- if (pHeightOut) {
- *pHeightOut = textHeight + pTB->tabPadding*2;
- }
-}
-
-DRGUI_PRIVATE void drgui_tabbar_on_paint_tab_default(drgui_element* pTBElement, drgui_tab* pTab, drgui_rect relativeClippingRect, float offsetX, float offsetY, float width, float height, void* pPaintData)
-{
- (void)relativeClippingRect;
-
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- // Background.
- drgui_color bgcolor = pTB->tabBackgroundColor;
- drgui_color closeButtonColor = pTB->closeButtonColorDefault;
- drgui_color textColor = pTB->tabTextColor;
-
- if (pTB->pHoveredTab == pTab) {
- bgcolor = pTB->tabBackgroundColorHovered;
- closeButtonColor = pTB->closeButtonColorTabHovered;
- textColor = pTB->tabTextColorHovered;
- }
- if (pTB->pActiveTab == pTab) {
- bgcolor = pTB->tabBackbroundColorActivated;
- closeButtonColor = pTB->closeButtonColorTabHovered;
- textColor = pTB->tabTextColorActivated;
- }
-
- if (pTB->pHoveredTab == pTab && pTB->isCloseButtonHovered) {
- closeButtonColor = pTB->closeButtonColorHovered;
-
- if (pTB->pTabWithCloseButtonPressed == pTB->pHoveredTab) {
- closeButtonColor = pTB->closeButtonColorPressed;
- }
- }
-
- drgui_draw_rect_outline(pTBElement, drgui_make_rect(offsetX, offsetY, offsetX + width, offsetY + height), bgcolor, pTB->tabPadding, pPaintData);
-
-
- // Text.
- float textPosX = offsetX + pTB->tabPadding;
- float textPosY = offsetY + pTB->tabPadding;
- if (pTab != NULL) {
- drgui_draw_text(pTBElement, pTB->pFont, pTab->text, (int)strlen(pTab->text), textPosX, textPosY, textColor, bgcolor, pPaintData);
- }
-
-
- // Close button.
- if (pTB->isShowingCloseButton && pTB->pCloseButtonImage != NULL)
- {
- float textWidth = 0;
- float textHeight = 0;
- if (pTab != NULL) {
- drgui_measure_string(pTB->pFont, pTab->text, strlen(pTab->text), &textWidth, &textHeight);
- }
-
- float closeButtonPosX = textPosX + textWidth + pTB->closeButtonPaddingLeft;
- float closeButtonPosY = textPosY;
-
- unsigned int iconWidth;
- unsigned int iconHeight;
- drgui_get_image_size(pTB->pCloseButtonImage, &iconWidth, &iconHeight);
-
- drgui_draw_image_args args;
- args.dstX = closeButtonPosX;
- args.dstY = closeButtonPosY;
- args.dstWidth = (float)iconWidth;
- args.dstHeight = (float)iconHeight;
- args.srcX = 0;
- args.srcY = 0;
- args.srcWidth = (float)iconWidth;
- args.srcHeight = (float)iconHeight;
- args.dstBoundsX = args.dstX;
- args.dstBoundsY = args.dstY;
- args.dstBoundsWidth = (float)iconWidth;
- args.dstBoundsHeight = height - (pTB->tabPadding*2);
- args.foregroundTint = closeButtonColor;
- args.backgroundColor = bgcolor;
- args.boundsColor = bgcolor;
- args.options = DRGUI_IMAGE_DRAW_BACKGROUND | DRGUI_IMAGE_DRAW_BOUNDS | DRGUI_IMAGE_CLIP_BOUNDS | DRGUI_IMAGE_ALIGN_CENTER;
- drgui_draw_image(pTBElement, pTB->pCloseButtonImage, &args, pPaintData);
-
-
- /// Space between the text and the padding.
- drgui_draw_rect(pTBElement, drgui_make_rect(textPosX + textWidth, textPosY, closeButtonPosX, textPosY + textHeight), bgcolor, pPaintData);
- }
-}
-
-DRGUI_PRIVATE drgui_tab* drgui_tabbar_find_tab_under_point(drgui_element* pTBElement, float relativePosX, float relativePosY, bool* pIsOverCloseButtonOut)
-{
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return NULL;
- }
-
- unsigned int closeButtonWidth;
- unsigned int closeButtonHeight;
- drgui_get_image_size(pTB->pCloseButtonImage, &closeButtonWidth, &closeButtonHeight);
-
- float runningPosX = 0;
- float runningPosY = 0;
- for (drgui_tab* pTab = pTB->pFirstTab; pTab != NULL; pTab = pTab->pNextTab)
- {
- float tabWidth = 0;
- float tabHeight = 0;
- drgui_tabbar_measure_tab(pTBElement, pTab, &tabWidth, &tabHeight);
-
- if (relativePosX >= runningPosX && relativePosX < runningPosX + tabWidth && relativePosY >= runningPosY && relativePosY < runningPosY + tabHeight)
- {
- if (pIsOverCloseButtonOut)
- {
- // The close button is in the center, vertically.
- drgui_rect closeButtonRect;
- closeButtonRect.left = runningPosX + tabWidth - (pTB->tabPadding + closeButtonWidth);
- closeButtonRect.right = closeButtonRect.left + closeButtonWidth;
- closeButtonRect.top = runningPosY + (tabHeight - (pTB->tabPadding + closeButtonHeight))/2;
- closeButtonRect.bottom = closeButtonRect.top + closeButtonHeight;
-
- if (pTB->isShowingCloseButton && drgui_rect_contains_point(closeButtonRect, relativePosX, relativePosY)) {
- *pIsOverCloseButtonOut = true;
- } else {
- *pIsOverCloseButtonOut = false;
- }
- }
-
- return pTab;
- }
-
- if (pTB->orientation == drgui_tabbar_orientation_top || pTB->orientation == drgui_tabbar_orientation_bottom) {
- runningPosX += tabWidth;
- } else {
- runningPosY += tabHeight;
- }
- }
-
-
- if (pIsOverCloseButtonOut) {
- *pIsOverCloseButtonOut = false;
- }
-
- return NULL;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// Tab
-//
-///////////////////////////////////////////////////////////////////////////////
-
-/// Appends the given tab to the given tab bar.
-DRGUI_PRIVATE void tab_append(drgui_tab* pTab, drgui_element* pTBElement);
-
-/// Prepends the given tab to the given tab bar.
-DRGUI_PRIVATE void tab_prepend(drgui_tab* pTab, drgui_element* pTBElement);
-
-/// Detaches the given tab bar from it's tab bar element's hierarchy.
-///
-/// @remarks
-/// This does not deactivate the tab or what - it only detaches the tab from the hierarchy.
-DRGUI_PRIVATE void tab_detach_from_hierarchy(drgui_tab* pTab);
-
-/// Detaches the given tab bar from it's tab bar element.
-DRGUI_PRIVATE void tab_detach(drgui_tab* pTab);
-
-DRGUI_PRIVATE drgui_tab* tb_create_tab(drgui_element* pTBElement, const char* text, size_t extraDataSize, const void* pExtraData)
-{
- if (pTBElement == NULL) {
- return NULL;
- }
-
- drgui_tab* pTab = (drgui_tab*)malloc(sizeof(*pTab) + extraDataSize);
- if (pTab == NULL) {
- return NULL;
- }
-
- pTab->pTBElement = NULL;
- pTab->pNextTab = NULL;
- pTab->pPrevTab = NULL;
- pTab->text[0] = '\0';
-
- pTab->extraDataSize = extraDataSize;
- if (pExtraData) {
- memcpy(pTab->pExtraData, pExtraData, extraDataSize);
- }
-
- if (text != NULL) {
- drgui__strncpy_s(pTab->text, sizeof(pTab->text), text, (size_t)-1); // -1 = _TRUNCATE
- }
-
- return pTab;
-}
-
-drgui_tab* drgui_tabbar_create_and_append_tab(drgui_element* pTBElement, const char* text, size_t extraDataSize, const void* pExtraData)
-{
- drgui_tab* pTab = (drgui_tab*)tb_create_tab(pTBElement, text, extraDataSize, pExtraData);
- if (pTab != NULL)
- {
- tab_append(pTab, pTBElement);
- }
-
- return pTab;
-}
-
-drgui_tab* drgui_tabbar_create_and_prepend_tab(drgui_element* pTBElement, const char* text, size_t extraDataSize, const void* pExtraData)
-{
- drgui_tab* pTab = (drgui_tab*)tb_create_tab(pTBElement, text, extraDataSize, pExtraData);
- if (pTab != NULL)
- {
- tab_prepend(pTab, pTBElement);
- }
-
- return pTab;
-}
-
-void drgui_tab_delete(drgui_tab* pTab)
-{
- if (pTab == NULL) {
- return;
- }
-
- tab_detach(pTab);
- free(pTab);
-}
-
-drgui_element* drgui_tab_get_tab_bar_element(drgui_tab* pTab)
-{
- if (pTab == NULL) {
- return NULL;
- }
-
- return pTab->pTBElement;
-}
-
-size_t drgui_tab_get_extra_data_size(drgui_tab* pTab)
-{
- if (pTab == NULL) {
- return 0;
- }
-
- return pTab->extraDataSize;
-}
-
-void* drgui_tab_get_extra_data(drgui_tab* pTab)
-{
- if (pTab == NULL) {
- return NULL;
- }
-
- return pTab->pExtraData;
-}
-
-
-void drgui_tab_set_text(drgui_tab* pTab, const char* text)
-{
- if (pTab == NULL) {
- return;
- }
-
- if (text != NULL) {
- drgui__strncpy_s(pTab->text, sizeof(pTab->text), text, (size_t)-1); // -1 = _TRUNCATE
- } else {
- pTab->text[0] = '\0';
- }
-
- // The content of the menu has changed so we'll need to schedule a redraw.
- if (drgui_is_auto_dirty_enabled(pTab->pTBElement->pContext)) {
- drgui_dirty(pTab->pTBElement, drgui_get_local_rect(pTab->pTBElement));
- }
-}
-
-const char* drgui_tab_get_text(drgui_tab* pTab)
-{
- if (pTab == NULL) {
- return NULL;
- }
-
- return pTab->text;
-}
-
-
-drgui_tab* drgui_tab_get_next_tab(drgui_tab* pTab)
-{
- if (pTab == NULL) {
- return NULL;
- }
-
- return pTab->pNextTab;
-}
-
-drgui_tab* drgui_tab_get_prev_tab(drgui_tab* pTab)
-{
- if (pTab == NULL) {
- return NULL;
- }
-
- return pTab->pPrevTab;
-}
-
-
-void drgui_tab_move_to_front(drgui_tab* pTab)
-{
- if (pTab == NULL) {
- return;
- }
-
- drgui_element* pTBElement = pTab->pTBElement;
-
- tab_detach_from_hierarchy(pTab);
- tab_prepend(pTab, pTBElement);
-}
-
-bool drgui_tab_is_in_view(drgui_tab* pTab)
-{
- if (pTab == NULL) {
- return false;
- }
-
- return drgui_tabbar_is_tab_in_view(pTab->pTBElement, pTab);
-}
-
-void drgui_tab_move_into_view(drgui_tab* pTab)
-{
- if (!drgui_tab_is_in_view(pTab)) {
- drgui_tab_move_to_front(pTab);
- }
-}
-
-
-
-
-DRGUI_PRIVATE void tab_append(drgui_tab* pTab, drgui_element* pTBElement)
-{
- if (pTab == NULL || pTBElement == NULL) {
- return;
- }
-
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- assert(pTB != NULL);
-
- pTab->pTBElement = pTBElement;
- if (pTB->pFirstTab == NULL)
- {
- assert(pTB->pLastTab == NULL);
-
- pTB->pFirstTab = pTab;
- pTB->pLastTab = pTab;
- }
- else
- {
- assert(pTB->pLastTab != NULL);
-
- pTab->pPrevTab = pTB->pLastTab;
-
- pTB->pLastTab->pNextTab = pTab;
- pTB->pLastTab = pTab;
- }
-
-
- if (pTB->isAutoSizeEnabled) {
- drgui_tabbar_resize_by_tabs(pTBElement);
- }
-
- // The content of the menu has changed so we'll need to schedule a redraw.
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
-}
-
-DRGUI_PRIVATE void tab_prepend(drgui_tab* pTab, drgui_element* pTBElement)
-{
- if (pTab == NULL || pTBElement == NULL) {
- return;
- }
-
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- assert(pTB != NULL);
-
- pTab->pTBElement = pTBElement;
- if (pTB->pFirstTab == NULL)
- {
- assert(pTB->pLastTab == NULL);
-
- pTB->pFirstTab = pTab;
- pTB->pLastTab = pTab;
- }
- else
- {
- assert(pTB->pLastTab != NULL);
-
- pTab->pNextTab = pTB->pFirstTab;
-
- pTB->pFirstTab->pPrevTab = pTab;
- pTB->pFirstTab = pTab;
- }
-
-
- if (pTB->isAutoSizeEnabled) {
- drgui_tabbar_resize_by_tabs(pTBElement);
- }
-
- // The content of the menu has changed so we'll need to schedule a redraw.
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
-}
-
-DRGUI_PRIVATE void tab_detach_from_hierarchy(drgui_tab* pTab)
-{
- if (pTab == NULL) {
- return;
- }
-
- drgui_element* pTBElement = pTab->pTBElement;
- if (pTBElement == NULL) {
- return;
- }
-
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- assert(pTB != NULL);
-
-
- if (pTab->pNextTab != NULL) {
- pTab->pNextTab->pPrevTab = pTab->pPrevTab;
- }
-
- if (pTab->pPrevTab != NULL) {
- pTab->pPrevTab->pNextTab = pTab->pNextTab;
- }
-
-
- if (pTab == pTB->pFirstTab) {
- pTB->pFirstTab = pTab->pNextTab;
- }
-
- if (pTab == pTB->pLastTab) {
- pTB->pLastTab = pTab->pPrevTab;
- }
-
-
- pTab->pNextTab = NULL;
- pTab->pPrevTab = NULL;
- pTab->pTBElement = NULL;
-}
-
-DRGUI_PRIVATE void tab_detach(drgui_tab* pTab)
-{
- if (pTab == NULL) {
- return;
- }
-
- drgui_element* pTBElement = pTab->pTBElement;
- if (pTBElement == NULL) {
- return;
- }
-
- drgui_tab_bar* pTB = (drgui_tab_bar*)drgui_get_extra_data(pTBElement);
- assert(pTB != NULL);
-
- if (pTB->pHoveredTab == pTab) {
- pTB->pHoveredTab = NULL;
- pTB->isCloseButtonHovered = false;
- }
-
- if (pTB->pActiveTab == pTab) {
- pTB->pActiveTab = NULL;
- }
-
- if (pTB->pTabWithCloseButtonPressed == pTab) {
- pTB->pTabWithCloseButtonPressed = NULL;
- }
-
-
- tab_detach_from_hierarchy(pTab);
-
-
- if (pTB->isAutoSizeEnabled) {
- drgui_tabbar_resize_by_tabs(pTBElement);
- }
-
- // The content of the menu has changed so we'll need to schedule a redraw.
- if (drgui_is_auto_dirty_enabled(pTBElement->pContext)) {
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
- }
-}
-#endif //DR_GUI_IMPLEMENTATION
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-//
-// Text Box
-//
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-#ifndef DRGUI_NO_TEXT_EDITING
-// QUICK NOTES
-//
-// - By default the cursor/caret does not blink automatically. Instead, the application must "step" the text box by
-// calling drgui_textbox_step().
-
-#ifndef drgui_textbox_h
-#define drgui_textbox_h
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef void (* drgui_textbox_on_cursor_move_proc)(drgui_element* pTBElement);
-typedef void (* drgui_textbox_on_undo_point_changed_proc)(drgui_element* pTBElement, unsigned int iUndoPoint);
-
-
-/// Creates a new text box control.
-drgui_element* drgui_create_textbox(drgui_context* pContext, drgui_element* pParent, size_t extraDataSize, const void* pExtraData);
-
-/// Deletest the given text box control.
-void drgui_delete_textbox(drgui_element* pTBElement);
-
-
-/// Retrieves the size of the extra data associated with the given text box.
-size_t drgui_textbox_get_extra_data_size(drgui_element* pTBElement);
-
-/// Retrieves a pointer to the extra data associated with the given text box.
-void* drgui_textbox_get_extra_data(drgui_element* pTBElement);
-
-
-/// Sets the font to use with the given text box.
-void drgui_textbox_set_font(drgui_element* pTBElement, drgui_font* pFont);
-
-/// Retrieves the font being used with the given text box.
-drgui_font* drgui_textbox_get_font(drgui_element* pTBElement);
-
-/// Sets the color of the text in teh given text box.
-void drgui_textbox_set_text_color(drgui_element* pTBElement, drgui_color color);
-
-/// Sets the background color of the given text box.
-void drgui_textbox_set_background_color(drgui_element* pTBElement, drgui_color color);
-
-/// Sets the background color of selected text.
-void drgui_textbox_set_selection_background_color(drgui_element* pTBElement, drgui_color color);
-
-/// Retrieves the background color of selected text.
-drgui_color drgui_textbox_get_selection_background_color(drgui_element* pTBElement);
-
-/// Sets the background color for the line the caret is currently sitting on.
-void drgui_textbox_set_active_line_background_color(drgui_element* pTBElement, drgui_color color);
-
-/// Sets the width of the text cursor.
-void drgui_textbox_set_cursor_width(drgui_element* pTBElement, float cursorWidth);
-
-/// Retrieves the width of the text cursor.
-float drgui_textbox_get_cursor_width(drgui_element* pTBElement);
-
-/// Sets the color of the cursor of the given text box.
-void drgui_textbox_set_cursor_color(drgui_element* pTBElement, drgui_color color);
-
-/// Sets the border color of the given text box.
-void drgui_textbox_set_border_color(drgui_element* pTBElement, drgui_color color);
-
-/// Sets the border width of the given text box.
-void drgui_textbox_set_border_width(drgui_element* pTBElement, float borderWidth);
-
-/// Sets the amount of padding to apply to given text box.
-void drgui_textbox_set_padding(drgui_element* pTBElement, float padding);
-
-/// Retrieves the amound of vertical padding to apply to the given text box.
-float drgui_textbox_get_padding_vert(drgui_element* pTBElement);
-
-/// Retrieves the amound of horizontal padding to apply to the given text box.
-float drgui_textbox_get_padding_horz(drgui_element* pTBElement);
-
-/// Sets the vertical alignment of the given text box.
-void drgui_textbox_set_vertical_align(drgui_element* pTBElement, drgui_text_engine_alignment align);
-
-/// Sets the horizontal alignment of the given text box.
-void drgui_textbox_set_horizontal_align(drgui_element* pTBElement, drgui_text_engine_alignment align);
-
-// Sets the width of the line numbers.
-void drgui_textbox_set_line_numbers_width(drgui_element* pTBElement, float lineNumbersWidth);
-
-// Retrieves the width of the line numbers.
-float drgui_textbox_get_line_numbers_width(drgui_element* pTBElement);
-
-// Sets the padding to apply between the line numbers and the text.
-void drgui_textbox_set_line_numbers_padding(drgui_element* pTBElement, float lineNumbersPadding);
-
-// Retrieves the padding to apply between the line numbers and the text.
-float drgui_textbox_get_line_numbers_padding(drgui_element* pTBElement);
-
-// Sets the color of the text of the line numbers.
-void drgui_textbox_set_line_numbers_color(drgui_element* pTBElement, drgui_color color);
-
-// Retrieves the color of the text of the line numbers.
-drgui_color drgui_textbox_get_line_numbers_color(drgui_element* pTBElement);
-
-// Sets the color of the background of the line numbers.
-void drgui_textbox_set_line_numbers_background_color(drgui_element* pTBElement, drgui_color color);
-
-// Retrieves the color of the background of the line numbers.
-drgui_color drgui_textbox_get_line_numbers_background_color(drgui_element* pTBElement);
-
-
-/// Sets the text of the given text box.
-void drgui_textbox_set_text(drgui_element* pTBElement, const char* text);
-
-/// Retrieves the text of the given text box.
-size_t drgui_textbox_get_text(drgui_element* pTBElement, char* pTextOut, size_t textOutSize);
-
-/// Steps the text box to allow it to blink the cursor.
-void drgui_textbox_step(drgui_element* pTBElement, unsigned int milliseconds);
-
-/// Sets the blink rate of the cursor in milliseconds.
-void drgui_textbox_set_cursor_blink_rate(drgui_element* pTBElement, unsigned int blinkRateInMilliseconds);
-
-/// Moves the caret to the end of the text.
-void drgui_textbox_move_cursor_to_end_of_text(drgui_element* pTBElement);
-
-/// Moves the caret to the beginning of the line at the given index.
-void drgui_textbox_move_cursor_to_start_of_line_by_index(drgui_element* pTBElement, size_t iLine);
-
-/// Determines whether or not anything is selected in the given text box.
-bool drgui_textbox_is_anything_selected(drgui_element* pTBElement);
-
-/// Selects all of the text inside the text box.
-void drgui_textbox_select_all(drgui_element* pTBElement);
-
-/// Deselect everything.
-void drgui_textbox_deselect_all(drgui_element* pTBElement);
-
-/// Retrieves a copy of the selected text.
-///
-/// @remarks
-/// This returns the length of the selected text. Call this once with set to NULL to calculate the required size of the
-/// buffer.
-/// @par
-/// If the output buffer is not larger enough, the string will be truncated.
-size_t drgui_textbox_get_selected_text(drgui_element* pTBElement, char* textOut, size_t textOutLength);
-
-/// Deletes the character to the right of the cursor.
-///
-/// @return True if the text within the text engine has changed.
-bool drgui_textbox_delete_character_to_right_of_cursor(drgui_element* pTBElement);
-
-/// Deletes the currently selected text.
-///
-/// @return True if the text within the text engine has changed.
-bool drgui_textbox_delete_selected_text(drgui_element* pTBElement);
-
-/// Inserts a character at the position of the cursor.
-///
-/// @return True if the text within the text engine has changed.
-bool drgui_textbox_insert_text_at_cursor(drgui_element* pTBElement, const char* text);
-
-/// Performs an undo operation.
-bool drgui_textbox_undo(drgui_element* pTBElement);
-
-/// Performs a redo operation.
-bool drgui_textbox_redo(drgui_element* pTBElement);
-
-/// Retrieves the number of undo points remaining.
-unsigned int drgui_textbox_get_undo_points_remaining_count(drgui_element* pTBElement);
-
-/// Retrieves the number of redo points remaining.
-unsigned int drgui_textbox_get_redo_points_remaining_count(drgui_element* pTBElement);
-
-/// Clears the undo/redo stack.
-void drgui_textbox_clear_undo_stack(drgui_element* pTBElement);
-
-/// Retrieves the index of the line the cursor is current sitting on.
-size_t drgui_textbox_get_cursor_line(drgui_element* pTBElement);
-
-/// Retrieves the index of the column the cursor is current sitting on.
-size_t drgui_textbox_get_cursor_column(drgui_element* pTBElement);
-
-/// Retrieves the number of lines in the given text box.
-size_t drgui_textbox_get_line_count(drgui_element* pTBElement);
-
-
-/// Finds and selects the next occurance of the given string, starting from the cursor and looping back to the start.
-bool drgui_textbox_find_and_select_next(drgui_element* pTBElement, const char* text);
-
-/// Finds the next occurance of the given string and replaces it with another.
-bool drgui_textbox_find_and_replace_next(drgui_element* pTBElement, const char* text, const char* replacement);
-
-/// Finds every occurance of the given string and replaces it with another.
-bool drgui_textbox_find_and_replace_all(drgui_element* pTBElement, const char* text, const char* replacement);
-
-
-/// Shows the line numbers.
-void drgui_textbox_show_line_numbers(drgui_element* pTBElement);
-
-/// Hides the line numbers.
-void drgui_textbox_hide_line_numbers(drgui_element* pTBElement);
-
-
-/// Disables the vertical scrollbar.
-void drgui_textbox_disable_vertical_scrollbar(drgui_element* pTBElement);
-
-/// Enables the vertical scrollbar.
-void drgui_textbox_enable_vertical_scrollbar(drgui_element* pTBElement);
-
-/// Disables the horizontal scrollbar.
-void drgui_textbox_disable_horizontal_scrollbar(drgui_element* pTBElement);
-
-/// Enables the horizontal scrollbar.
-void drgui_textbox_enable_horizontal_scrollbar(drgui_element* pTBElement);
-
-// Retrieves the vertical scrollbar.
-drgui_element* drgui_textbox_get_vertical_scrollbar(drgui_element* pTBElement);
-
-// Retrieves the horizontal scrollbar.
-drgui_element* drgui_textbox_get_horizontal_scrollbar(drgui_element* pTBElement);
-
-// Sets the size of both the vertical and horizontal scrollbars.
-void drgui_textbox_set_scrollbar_size(drgui_element* pTBElement, float size);
-
-
-/// Sets the function to call when the cursor moves.
-void drgui_textbox_set_on_cursor_move(drgui_element* pTBElement, drgui_textbox_on_cursor_move_proc proc);
-
-/// Sets the function to call when the undo point changes.
-void drgui_textbox_set_on_undo_point_changed(drgui_element* pTBElement, drgui_textbox_on_undo_point_changed_proc proc);
-
-
-
-/// on_size.
-void drgui_textbox_on_size(drgui_element* pTBElement, float newWidth, float newHeight);
-
-/// on_mouse_move.
-void drgui_textbox_on_mouse_move(drgui_element* pTBElement, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// on_mouse_button_down.
-void drgui_textbox_on_mouse_button_down(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// on_mouse_button_up.
-void drgui_textbox_on_mouse_button_up(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// on_mouse_button_dblclick.
-void drgui_textbox_on_mouse_button_dblclick(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// on_mouse_wheel
-void drgui_textbox_on_mouse_wheel(drgui_element* pTBElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// on_key_down.
-void drgui_textbox_on_key_down(drgui_element* pTBElement, drgui_key key, int stateFlags);
-
-/// on_key_up.
-void drgui_textbox_on_key_up(drgui_element* pTBElement, drgui_key key, int stateFlags);
-
-/// on_printable_key_down.
-void drgui_textbox_on_printable_key_down(drgui_element* pTBElement, unsigned int utf32, int stateFlags);
-
-/// on_paint.
-void drgui_textbox_on_paint(drgui_element* pTBElement, drgui_rect relativeRect, void* pPaintData);
-
-/// on_capture_keyboard
-void drgui_textbox_on_capture_keyboard(drgui_element* pTBElement, drgui_element* pPrevCapturedElement);
-
-/// on_release_keyboard
-void drgui_textbox_on_release_keyboard(drgui_element* pTBElement, drgui_element* pNewCapturedElement);
-
-/// on_capture_mouse
-void drgui_textbox_on_capture_mouse(drgui_element* pTBElement);
-
-/// on_release_mouse
-void drgui_textbox_on_release_mouse(drgui_element* pTBElement);
-
-
-
-#ifdef __cplusplus
-}
-#endif
-#endif //drgui_textbox_h
-
-
-#ifdef DR_GUI_IMPLEMENTATION
-typedef struct
-{
- /// The text engine.
- drgui_text_engine* pTL;
-
- /// The vertical scrollbar.
- drgui_element* pVertScrollbar;
-
- /// The horizontal scrollbar.
- drgui_element* pHorzScrollbar;
-
- /// The line numbers element.
- drgui_element* pLineNumbers;
-
-
- /// The color of the border.
- drgui_color borderColor;
-
- /// The width of the border.
- float borderWidth;
-
- /// The amount of padding to apply the left and right of the text.
- float padding;
-
- // The width of the line numbers.
- float lineNumbersWidth;
-
- /// The padding to the right of the line numbers.
- float lineNumbersPaddingRight;
-
- // The color of the text of the line numbers.
- drgui_color lineNumbersColor;
-
- // The color of the background of the line numbers.
- drgui_color lineNumbersBackgroundColor;
-
-
- /// The desired width of the vertical scrollbar.
- float vertScrollbarSize;
-
- /// The desired height of the horizontal scrollbar.
- float horzScrollbarSize;
-
- /// Whether or not the vertical scrollbar is enabled.
- bool isVertScrollbarEnabled;
-
- /// Whether or not the horizontal scrollbar is enabled.
- bool isHorzScrollbarEnabled;
-
-
- /// When selecting lines by clicking and dragging on the line numbers, keeps track of the line to anchor the selection to.
- size_t iLineSelectAnchor;
-
-
- /// The function to call when the text cursor/caret moves.
- drgui_textbox_on_cursor_move_proc onCursorMove;
-
- /// The function to call when the undo point changes.
- drgui_textbox_on_undo_point_changed_proc onUndoPointChanged;
-
-
- /// The size of the extra data.
- size_t extraDataSize;
-
- /// A pointer to the extra data.
- char pExtraData[1];
-
-} drgui_textbox;
-
-
-/// Retrieves the offset to draw the text in the text box.
-DRGUI_PRIVATE void drgui_textbox__get_text_offset(drgui_element* pTBElement, float* pOffsetXOut, float* pOffsetYOut);
-
-/// Calculates the required size of the text engine.
-DRGUI_PRIVATE void drgui_textbox__calculate_text_engine_container_size(drgui_element* pTBElement, float* pWidthOut, float* pHeightOut);
-
-/// Retrieves the rectangle of the text engine's container.
-DRGUI_PRIVATE drgui_rect drgui_textbox__get_text_rect(drgui_element* pTBElement);
-
-/// Refreshes the range, page sizes and layouts of the scrollbars.
-DRGUI_PRIVATE void drgui_textbox__refresh_scrollbars(drgui_element* pTBElement);
-
-/// Refreshes the range and page sizes of the scrollbars.
-DRGUI_PRIVATE void drgui_textbox__refresh_scrollbar_ranges(drgui_element* pTBElement);
-
-/// Refreshes the size and position of the scrollbars.
-DRGUI_PRIVATE void drgui_textbox__refresh_scrollbar_layouts(drgui_element* pTBElement);
-
-/// Retrieves a rectangle representing the space between the edges of the two scrollbars.
-DRGUI_PRIVATE drgui_rect drgui_textbox__get_scrollbar_dead_space_rect(drgui_element* pTBElement);
-
-
-/// Called when a mouse button is pressed on the line numbers element.
-DRGUI_PRIVATE void drgui_textbox__on_mouse_move_line_numbers(drgui_element* pLineNumbers, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// Called when a mouse button is pressed on the line numbers element.
-DRGUI_PRIVATE void drgui_textbox__on_mouse_button_down_line_numbers(drgui_element* pLineNumbers, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// Called when a mouse button is pressed on the line numbers element.
-DRGUI_PRIVATE void drgui_textbox__on_mouse_button_up_line_numbers(drgui_element* pLineNumbers, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// Called when the line numbers element needs to be drawn.
-DRGUI_PRIVATE void drgui_textbox__on_paint_line_numbers(drgui_element* pLineNumbers, drgui_rect relativeRect, void* pPaintData);
-
-/// Refreshes the line number of the given text editor.
-DRGUI_PRIVATE void drgui_textbox__refresh_line_numbers(drgui_element* pTBElement);
-
-
-/// on_paint_rect()
-DRGUI_PRIVATE void drgui_textbox__on_text_engine_paint_rect(drgui_text_engine* pLayout, drgui_rect rect, drgui_color color, drgui_element* pTBElement, void* pPaintData);
-
-/// on_paint_text()
-DRGUI_PRIVATE void drgui_textbox__on_text_engine_paint_text(drgui_text_engine* pTL, drgui_text_run* pRun, drgui_element* pTBElement, void* pPaintData);
-
-/// on_dirty()
-DRGUI_PRIVATE void drgui_textbox__on_text_engine_dirty(drgui_text_engine* pTL, drgui_rect rect);
-
-/// on_cursor_move()
-DRGUI_PRIVATE void drgui_textbox__on_text_engine_cursor_move(drgui_text_engine* pTL);
-
-/// on_text_changed()
-DRGUI_PRIVATE void drgui_textbox__on_text_engine_text_changed(drgui_text_engine* pTL);
-
-/// on_undo_point_changed()
-DRGUI_PRIVATE void drgui_textbox__on_text_engine_undo_point_changed(drgui_text_engine* pTL, unsigned int iUndoPoint);
-
-
-DRGUI_PRIVATE void drgui_textbox__on_vscroll(drgui_element* pSBElement, int scrollPos)
-{
- drgui_element* pTBElement = *(drgui_element**)drgui_sb_get_extra_data(pSBElement);
- assert(pTBElement != NULL);
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- assert(pTB != NULL);
-
- drgui_text_engine_set_inner_offset_y(pTB->pTL, -drgui_text_engine_get_line_pos_y(pTB->pTL, scrollPos));
-
- // The line numbers need to be redrawn.
- drgui_dirty(pTB->pLineNumbers, drgui_get_local_rect(pTB->pLineNumbers));
-}
-
-DRGUI_PRIVATE void drgui_textbox__on_hscroll(drgui_element* pSBElement, int scrollPos)
-{
- drgui_element* pTBElement = *(drgui_element**)drgui_sb_get_extra_data(pSBElement);
- assert(pTBElement != NULL);
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- assert(pTB != NULL);
-
- drgui_text_engine_set_inner_offset_x(pTB->pTL, (float)-scrollPos);
-}
-
-
-
-drgui_element* drgui_create_textbox(drgui_context* pContext, drgui_element* pParent, size_t extraDataSize, const void* pExtraData)
-{
- if (pContext == NULL) {
- return NULL;
- }
-
- drgui_element* pTBElement = drgui_create_element(pContext, pParent, sizeof(drgui_textbox) + extraDataSize, NULL);
- if (pTBElement == NULL) {
- return NULL;
- }
-
- drgui_set_cursor(pTBElement, drgui_cursor_text);
- drgui_set_on_size(pTBElement, drgui_textbox_on_size);
- drgui_set_on_mouse_move(pTBElement, drgui_textbox_on_mouse_move);
- drgui_set_on_mouse_button_down(pTBElement, drgui_textbox_on_mouse_button_down);
- drgui_set_on_mouse_button_up(pTBElement, drgui_textbox_on_mouse_button_up);
- drgui_set_on_mouse_button_dblclick(pTBElement, drgui_textbox_on_mouse_button_dblclick);
- drgui_set_on_mouse_wheel(pTBElement, drgui_textbox_on_mouse_wheel);
- drgui_set_on_key_down(pTBElement, drgui_textbox_on_key_down);
- drgui_set_on_printable_key_down(pTBElement, drgui_textbox_on_printable_key_down);
- drgui_set_on_paint(pTBElement, drgui_textbox_on_paint);
- drgui_set_on_capture_keyboard(pTBElement, drgui_textbox_on_capture_keyboard);
- drgui_set_on_release_keyboard(pTBElement, drgui_textbox_on_release_keyboard);
- drgui_set_on_capture_mouse(pTBElement, drgui_textbox_on_capture_mouse);
- drgui_set_on_release_mouse(pTBElement, drgui_textbox_on_release_mouse);
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- assert(pTB != NULL);
-
- pTB->pVertScrollbar = drgui_create_scrollbar(pContext, pTBElement, drgui_sb_orientation_vertical, sizeof(pTBElement), &pTBElement);
- drgui_sb_set_on_scroll(pTB->pVertScrollbar, drgui_textbox__on_vscroll);
- drgui_sb_set_mouse_wheel_scele(pTB->pVertScrollbar, 3);
-
- pTB->pHorzScrollbar = drgui_create_scrollbar(pContext, pTBElement, drgui_sb_orientation_horizontal, sizeof(pTBElement), &pTBElement);
- drgui_sb_set_on_scroll(pTB->pHorzScrollbar, drgui_textbox__on_hscroll);
-
- pTB->pLineNumbers = drgui_create_element(pContext, pTBElement, sizeof(pTBElement), &pTBElement);
- drgui_hide(pTB->pLineNumbers);
- drgui_set_on_mouse_move(pTB->pLineNumbers, drgui_textbox__on_mouse_move_line_numbers);
- drgui_set_on_mouse_button_down(pTB->pLineNumbers, drgui_textbox__on_mouse_button_down_line_numbers);
- drgui_set_on_mouse_button_up(pTB->pLineNumbers, drgui_textbox__on_mouse_button_up_line_numbers);
- drgui_set_on_paint(pTB->pLineNumbers, drgui_textbox__on_paint_line_numbers);
-
- pTB->pTL = drgui_create_text_engine(pContext, sizeof(pTBElement), &pTBElement);
- if (pTB->pTL == NULL) {
- drgui_delete_element(pTBElement);
- return NULL;
- }
-
- drgui_text_engine_set_on_paint_rect(pTB->pTL, drgui_textbox__on_text_engine_paint_rect);
- drgui_text_engine_set_on_paint_text(pTB->pTL, drgui_textbox__on_text_engine_paint_text);
- drgui_text_engine_set_on_dirty(pTB->pTL, drgui_textbox__on_text_engine_dirty);
- drgui_text_engine_set_on_cursor_move(pTB->pTL, drgui_textbox__on_text_engine_cursor_move);
- drgui_text_engine_set_on_text_changed(pTB->pTL, drgui_textbox__on_text_engine_text_changed);
- drgui_text_engine_set_on_undo_point_changed(pTB->pTL, drgui_textbox__on_text_engine_undo_point_changed);
- drgui_text_engine_set_default_text_color(pTB->pTL, drgui_rgb(0, 0, 0));
- drgui_text_engine_set_cursor_color(pTB->pTL, drgui_rgb(0, 0, 0));
- drgui_text_engine_set_default_bg_color(pTB->pTL, drgui_rgb(255, 255, 255));
- drgui_text_engine_set_active_line_bg_color(pTB->pTL, drgui_rgb(255, 255, 255));
- drgui_text_engine_set_vertical_align(pTB->pTL, drgui_text_engine_alignment_center);
-
- pTB->borderColor = drgui_rgb(0, 0, 0);
- pTB->borderWidth = 1;
- pTB->padding = 2;
- pTB->lineNumbersWidth = 64;
- pTB->lineNumbersPaddingRight = 16;
- pTB->lineNumbersColor = drgui_rgb(80, 160, 192);
- pTB->lineNumbersBackgroundColor = drgui_text_engine_get_default_bg_color(pTB->pTL);
- pTB->vertScrollbarSize = 16;
- pTB->horzScrollbarSize = 16;
- pTB->isVertScrollbarEnabled = true;
- pTB->isHorzScrollbarEnabled = true;
- pTB->iLineSelectAnchor = 0;
- pTB->onCursorMove = NULL;
- pTB->onUndoPointChanged = NULL;
-
- pTB->extraDataSize = extraDataSize;
- if (pExtraData != NULL) {
- memcpy(pTB->pExtraData, pExtraData, extraDataSize);
- }
-
- return pTBElement;
-}
-
-void drgui_delete_textbox(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- if (pTB->pTL) {
- drgui_delete_text_engine(pTB->pTL);
- pTB->pTL = NULL;
- }
-
- if (pTB->pLineNumbers) {
- drgui_delete_element(pTB->pLineNumbers);
- pTB->pLineNumbers = NULL;
- }
-
- if (pTB->pHorzScrollbar) {
- drgui_delete_element(pTB->pHorzScrollbar);
- pTB->pHorzScrollbar = NULL;
- }
-
- if (pTB->pVertScrollbar) {
- drgui_delete_element(pTB->pVertScrollbar);
- pTB->pVertScrollbar = NULL;
- }
-
- drgui_delete_element(pTBElement);
-}
-
-size_t drgui_textbox_get_extra_data_size(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return 0;
- }
-
- return pTB->extraDataSize;
-}
-
-void* drgui_textbox_get_extra_data(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return NULL;
- }
-
- return pTB->pExtraData;
-}
-
-
-void drgui_textbox_set_font(drgui_element* pTBElement, drgui_font* pFont)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_begin_dirty(pTBElement);
- {
- drgui_text_engine_set_default_font(pTB->pTL, pFont);
-
- // The font used for line numbers are tied to the main font at the moment.
- drgui_textbox__refresh_line_numbers(pTBElement);
-
- // Emulate a scroll to ensure the scroll position is pinned to a line.
- drgui_textbox__on_vscroll(pTB->pVertScrollbar, drgui_sb_get_scroll_position(pTB->pVertScrollbar));
- drgui_textbox__refresh_scrollbars(pTBElement);
-
- // The caret position needs to be refreshes. We'll cheat here a little bit and just do a full refresh of the text engine.
- //drgui_text_engine__refresh(pTB->pTL);
- drgui_text_engine_refresh_markers(pTB->pTL);
- }
- drgui_end_dirty(pTBElement);
-}
-
-drgui_font* drgui_textbox_get_font(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return NULL;
- }
-
- return drgui_text_engine_get_default_font(pTB->pTL);
-}
-
-void drgui_textbox_set_text_color(drgui_element* pTBElement, drgui_color color)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_set_default_text_color(pTB->pTL, color);
-}
-
-void drgui_textbox_set_background_color(drgui_element* pTBElement, drgui_color color)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_set_default_bg_color(pTB->pTL, color);
-}
-
-void drgui_textbox_set_selection_background_color(drgui_element* pTBElement, drgui_color color)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_set_selection_bg_color(pTB->pTL, color);
-}
-
-drgui_color drgui_textbox_get_selection_background_color(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return drgui_rgb(0, 0, 0);
- }
-
- return drgui_text_engine_get_selection_bg_color(pTB->pTL);
-}
-
-void drgui_textbox_set_active_line_background_color(drgui_element* pTBElement, drgui_color color)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_set_active_line_bg_color(pTB->pTL, color);
-}
-
-void drgui_textbox_set_cursor_width(drgui_element* pTBElement, float cursorWidth)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_set_cursor_width(pTB->pTL, cursorWidth);
-}
-
-float drgui_textbox_get_cursor_width(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return 0;
- }
-
- return drgui_text_engine_get_cursor_width(pTB->pTL);
-}
-
-void drgui_textbox_set_cursor_color(drgui_element* pTBElement, drgui_color color)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_set_cursor_color(pTB->pTL, color);
-}
-
-void drgui_textbox_set_border_color(drgui_element* pTBElement, drgui_color color)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->borderColor = color;
-}
-
-void drgui_textbox_set_border_width(drgui_element* pTBElement, float borderWidth)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->borderWidth = borderWidth;
-}
-
-void drgui_textbox_set_padding(drgui_element* pTBElement, float padding)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->padding = padding;
-}
-
-float drgui_textbox_get_padding_vert(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return 0;
- }
-
- return pTB->padding;
-}
-
-float drgui_textbox_get_padding_horz(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return 0;
- }
-
- return pTB->padding;
-}
-
-void drgui_textbox_set_vertical_align(drgui_element* pTBElement, drgui_text_engine_alignment align)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_set_vertical_align(pTB->pTL, align);
-}
-
-void drgui_textbox_set_horizontal_align(drgui_element* pTBElement, drgui_text_engine_alignment align)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_set_horizontal_align(pTB->pTL, align);
-}
-
-void drgui_textbox_set_line_numbers_width(drgui_element* pTBElement, float lineNumbersWidth)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->lineNumbersWidth = lineNumbersWidth;
-}
-
-float drgui_textbox_get_line_numbers_width(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return 0;
- }
-
- return pTB->lineNumbersWidth;
-}
-
-void drgui_textbox_set_line_numbers_padding(drgui_element* pTBElement, float lineNumbersPadding)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->lineNumbersPaddingRight = lineNumbersPadding;
-}
-
-float drgui_textbox_get_line_numbers_padding(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return 0;
- }
-
- return pTB->lineNumbersPaddingRight;
-}
-
-void drgui_textbox_set_line_numbers_color(drgui_element* pTBElement, drgui_color color)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->lineNumbersColor = color;
- drgui_textbox__refresh_line_numbers(pTBElement);
-}
-
-drgui_color drgui_textbox_get_line_numbers_color(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return drgui_rgb(0, 0, 0);
- }
-
- return pTB->lineNumbersColor;
-}
-
-void drgui_textbox_set_line_numbers_background_color(drgui_element* pTBElement, drgui_color color)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->lineNumbersBackgroundColor = color;
- drgui_textbox__refresh_line_numbers(pTBElement);
-}
-
-drgui_color drgui_textbox_get_line_numbers_background_color(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return drgui_rgb(0, 0, 0);
- }
-
- return pTB->lineNumbersBackgroundColor;
-}
-
-
-
-void drgui_textbox_set_text(drgui_element* pTBElement, const char* text)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_prepare_undo_point(pTB->pTL);
- {
- drgui_text_engine_set_text(pTB->pTL, text);
- }
- drgui_text_engine_commit_undo_point(pTB->pTL);
-}
-
-size_t drgui_textbox_get_text(drgui_element* pTBElement, char* pTextOut, size_t textOutSize)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return 0;
- }
-
- return drgui_text_engine_get_text(pTB->pTL, pTextOut, textOutSize);
-}
-
-void drgui_textbox_step(drgui_element* pTBElement, unsigned int milliseconds)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_step(pTB->pTL, milliseconds);
-}
-
-void drgui_textbox_set_cursor_blink_rate(drgui_element* pTBElement, unsigned int blinkRateInMilliseconds)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_set_cursor_blink_rate(pTB->pTL, blinkRateInMilliseconds);
-}
-
-void drgui_textbox_move_cursor_to_end_of_text(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_move_cursor_to_end_of_text(pTB->pTL);
-}
-
-void drgui_textbox_move_cursor_to_start_of_line_by_index(drgui_element* pTBElement, size_t iLine)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_move_cursor_to_start_of_line_by_index(pTB->pTL, iLine);
-}
-
-
-bool drgui_textbox_is_anything_selected(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return false;
- }
-
- return drgui_text_engine_is_anything_selected(pTB->pTL);
-}
-
-void drgui_textbox_select_all(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_select_all(pTB->pTL);
-}
-
-void drgui_textbox_deselect_all(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_deselect_all(pTB->pTL);
-}
-
-size_t drgui_textbox_get_selected_text(drgui_element* pTBElement, char* textOut, size_t textOutLength)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return 0;
- }
-
- return drgui_text_engine_get_selected_text(pTB->pTL, textOut, textOutLength);
-}
-
-bool drgui_textbox_delete_character_to_right_of_cursor(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return false;
- }
-
- bool wasTextChanged = false;
- drgui_text_engine_prepare_undo_point(pTB->pTL);
- {
- wasTextChanged = drgui_text_engine_delete_character_to_right_of_cursor(pTB->pTL);
- }
- if (wasTextChanged) { drgui_text_engine_commit_undo_point(pTB->pTL); }
-
- return wasTextChanged;
-}
-
-bool drgui_textbox_delete_selected_text(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return false;
- }
-
- bool wasTextChanged = false;
- drgui_text_engine_prepare_undo_point(pTB->pTL);
- {
- wasTextChanged = drgui_text_engine_delete_selected_text(pTB->pTL);
- }
- if (wasTextChanged) { drgui_text_engine_commit_undo_point(pTB->pTL); }
-
- return wasTextChanged;
-}
-
-bool drgui_textbox_insert_text_at_cursor(drgui_element* pTBElement, const char* text)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return false;;
- }
-
- bool wasTextChanged = false;
- drgui_text_engine_prepare_undo_point(pTB->pTL);
- {
- wasTextChanged = drgui_text_engine_insert_text_at_cursor(pTB->pTL, text);
- }
- if (wasTextChanged) { drgui_text_engine_commit_undo_point(pTB->pTL); }
-
- return wasTextChanged;
-}
-
-bool drgui_textbox_undo(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return false;
- }
-
- return drgui_text_engine_undo(pTB->pTL);
-}
-
-bool drgui_textbox_redo(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return false;
- }
-
- return drgui_text_engine_redo(pTB->pTL);
-}
-
-unsigned int drgui_textbox_get_undo_points_remaining_count(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return false;
- }
-
- return drgui_text_engine_get_undo_points_remaining_count(pTB->pTL);
-}
-
-unsigned int drgui_textbox_get_redo_points_remaining_count(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return false;
- }
-
- return drgui_text_engine_get_redo_points_remaining_count(pTB->pTL);
-}
-
-void drgui_textbox_clear_undo_stack(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_clear_undo_stack(pTB->pTL);
-}
-
-
-size_t drgui_textbox_get_cursor_line(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return 0;
- }
-
- return drgui_text_engine_get_cursor_line(pTB->pTL);
-}
-
-size_t drgui_textbox_get_cursor_column(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return 0;
- }
-
- return drgui_text_engine_get_cursor_column(pTB->pTL);
-}
-
-size_t drgui_textbox_get_line_count(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return 0;
- }
-
- return drgui_text_engine_get_line_count(pTB->pTL);
-}
-
-
-bool drgui_textbox_find_and_select_next(drgui_element* pTBElement, const char* text)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return 0;
- }
-
- size_t selectionStart;
- size_t selectionEnd;
- if (drgui_text_engine_find_next(pTB->pTL, text, &selectionStart, &selectionEnd))
- {
- drgui_text_engine_select(pTB->pTL, selectionStart, selectionEnd);
- drgui_text_engine_move_cursor_to_end_of_selection(pTB->pTL);
-
- return true;
- }
-
- return false;
-}
-
-bool drgui_textbox_find_and_replace_next(drgui_element* pTBElement, const char* text, const char* replacement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return 0;
- }
-
- bool wasTextChanged = false;
- drgui_text_engine_prepare_undo_point(pTB->pTL);
- {
- size_t selectionStart;
- size_t selectionEnd;
- if (drgui_text_engine_find_next(pTB->pTL, text, &selectionStart, &selectionEnd))
- {
- drgui_text_engine_select(pTB->pTL, selectionStart, selectionEnd);
- drgui_text_engine_move_cursor_to_end_of_selection(pTB->pTL);
-
- wasTextChanged = drgui_text_engine_delete_selected_text(pTB->pTL) || wasTextChanged;
- wasTextChanged = drgui_text_engine_insert_text_at_cursor(pTB->pTL, replacement) || wasTextChanged;
- }
- }
- if (wasTextChanged) { drgui_text_engine_commit_undo_point(pTB->pTL); }
-
- return wasTextChanged;
-}
-
-bool drgui_textbox_find_and_replace_all(drgui_element* pTBElement, const char* text, const char* replacement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return 0;
- }
-
- size_t originalCursorLine = drgui_text_engine_get_cursor_line(pTB->pTL);
- size_t originalCursorPos = drgui_text_engine_get_cursor_character(pTB->pTL) - drgui_text_engine_get_line_first_character(pTB->pTL, originalCursorLine);
- int originalScrollPosX = drgui_sb_get_scroll_position(pTB->pHorzScrollbar);
- int originalScrollPosY = drgui_sb_get_scroll_position(pTB->pVertScrollbar);
-
- bool wasTextChanged = false;
- drgui_text_engine_prepare_undo_point(pTB->pTL);
- {
- // It's important that we don't replace the replacement text. To handle this, we just move the cursor to the top of the text and find
- // and replace every occurance without looping.
- drgui_text_engine_move_cursor_to_start_of_text(pTB->pTL);
-
- size_t selectionStart;
- size_t selectionEnd;
- while (drgui_text_engine_find_next_no_loop(pTB->pTL, text, &selectionStart, &selectionEnd))
- {
- drgui_text_engine_select(pTB->pTL, selectionStart, selectionEnd);
- drgui_text_engine_move_cursor_to_end_of_selection(pTB->pTL);
-
- wasTextChanged = drgui_text_engine_delete_selected_text(pTB->pTL) || wasTextChanged;
- wasTextChanged = drgui_text_engine_insert_text_at_cursor(pTB->pTL, replacement) || wasTextChanged;
- }
-
- // The cursor may have moved so we'll need to restore it.
- size_t lineCharStart;
- size_t lineCharEnd;
- drgui_text_engine_get_line_character_range(pTB->pTL, originalCursorLine, &lineCharStart, &lineCharEnd);
-
- size_t newCursorPos = lineCharStart + originalCursorPos;
- if (newCursorPos > lineCharEnd) {
- newCursorPos = lineCharEnd;
- }
- drgui_text_engine_move_cursor_to_character(pTB->pTL, newCursorPos);
- }
- if (wasTextChanged) { drgui_text_engine_commit_undo_point(pTB->pTL); }
-
-
- // The scroll positions may have moved so we'll need to restore them.
- drgui_sb_scroll_to(pTB->pHorzScrollbar, originalScrollPosX);
- drgui_sb_scroll_to(pTB->pVertScrollbar, originalScrollPosY);
-
- return wasTextChanged;
-}
-
-
-void drgui_textbox_show_line_numbers(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_show(pTB->pLineNumbers);
- drgui_textbox__refresh_line_numbers(pTBElement);
-}
-
-void drgui_textbox_hide_line_numbers(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_hide(pTB->pLineNumbers);
- drgui_textbox__refresh_line_numbers(pTBElement);
-}
-
-
-void drgui_textbox_disable_vertical_scrollbar(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- if (pTB->isVertScrollbarEnabled) {
- pTB->isVertScrollbarEnabled = false;
- drgui_textbox__refresh_scrollbars(pTBElement);
- }
-}
-
-void drgui_textbox_enable_vertical_scrollbar(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- if (!pTB->isVertScrollbarEnabled) {
- pTB->isVertScrollbarEnabled = true;
- drgui_textbox__refresh_scrollbars(pTBElement);
- }
-}
-
-void drgui_textbox_disable_horizontal_scrollbar(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- if (pTB->isHorzScrollbarEnabled) {
- pTB->isHorzScrollbarEnabled = false;
- drgui_textbox__refresh_scrollbars(pTBElement);
- }
-}
-
-void drgui_textbox_enable_horizontal_scrollbar(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- if (!pTB->isHorzScrollbarEnabled) {
- pTB->isHorzScrollbarEnabled = true;
- drgui_textbox__refresh_scrollbars(pTBElement);
- }
-}
-
-drgui_element* drgui_textbox_get_vertical_scrollbar(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return NULL;
- }
-
- return pTB->pVertScrollbar;
-}
-
-drgui_element* drgui_textbox_get_horizontal_scrollbar(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return NULL;
- }
-
- return pTB->pHorzScrollbar;
-}
-
-void drgui_textbox_set_scrollbar_size(drgui_element* pTBElement, float size)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->horzScrollbarSize = size;
- pTB->vertScrollbarSize = size;
-
- drgui_textbox__refresh_scrollbars(pTBElement);
-}
-
-
-void drgui_textbox_set_on_cursor_move(drgui_element* pTBElement, drgui_textbox_on_cursor_move_proc proc)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->onCursorMove = proc;
-}
-
-void drgui_textbox_set_on_undo_point_changed(drgui_element* pTBElement, drgui_textbox_on_undo_point_changed_proc proc)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- pTB->onUndoPointChanged = proc;
-}
-
-
-void drgui_textbox_on_size(drgui_element* pTBElement, float newWidth, float newHeight)
-{
- (void)newWidth;
- (void)newHeight;
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- // The text engine needs to be resized.
- float containerWidth;
- float containerHeight;
- drgui_textbox__calculate_text_engine_container_size(pTBElement, &containerWidth, &containerHeight);
- drgui_text_engine_set_container_size(pTB->pTL, containerWidth, containerHeight);
-
- // Scrollbars need to be refreshed first.
- drgui_textbox__refresh_scrollbars(pTBElement);
-
- // Line numbers need to be refreshed.
- drgui_textbox__refresh_line_numbers(pTBElement);
-}
-
-void drgui_textbox_on_mouse_move(drgui_element* pTBElement, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)stateFlags;
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- if (drgui_get_element_with_mouse_capture(pTBElement->pContext) == pTBElement)
- {
- float offsetX;
- float offsetY;
- drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
-
- drgui_text_engine_move_cursor_to_point(pTB->pTL, (float)relativeMousePosX - offsetX, (float)relativeMousePosY - offsetY);
- }
-}
-
-void drgui_textbox_on_mouse_button_down(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- // Focus the text editor.
- drgui_capture_keyboard(pTBElement);
-
- if (mouseButton == DRGUI_MOUSE_BUTTON_LEFT)
- {
- // If we are not in selection mode, make sure everything is deselected.
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) == 0) {
- drgui_text_engine_deselect_all(pTB->pTL);
- drgui_text_engine_leave_selection_mode(pTB->pTL);
- } else {
- drgui_text_engine_enter_selection_mode(pTB->pTL);
- }
-
- float offsetX;
- float offsetY;
- drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
- drgui_text_engine_move_cursor_to_point(pTB->pTL, (float)relativeMousePosX - offsetX, (float)relativeMousePosY - offsetY);
-
- // In order to support selection with the mouse we need to capture the mouse and enter selection mode.
- drgui_capture_mouse(pTBElement);
-
- // If we didn't previously enter selection mode we'll need to do that now so we can drag select.
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) == 0) {
- drgui_text_engine_enter_selection_mode(pTB->pTL);
- }
- }
-
- if (mouseButton == DRGUI_MOUSE_BUTTON_RIGHT)
- {
- drgui_text_engine_leave_selection_mode(pTB->pTL);
- }
-}
-
-void drgui_textbox_on_mouse_button_up(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)relativeMousePosX;
- (void)relativeMousePosY;
- (void)stateFlags;
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- if (mouseButton == DRGUI_MOUSE_BUTTON_LEFT)
- {
- if (drgui_get_element_with_mouse_capture(pTBElement->pContext) == pTBElement)
- {
- // Releasing the mouse will leave selectionmode.
- drgui_release_mouse(pTBElement->pContext);
- }
- }
-}
-
-void drgui_textbox_on_mouse_button_dblclick(drgui_element* pTBElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)mouseButton;
- (void)relativeMousePosX;
- (void)relativeMousePosY;
- (void)stateFlags;
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-}
-
-void drgui_textbox_on_mouse_wheel(drgui_element* pTBElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)relativeMousePosX;
- (void)relativeMousePosY;
- (void)stateFlags;
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_sb_scroll(pTB->pVertScrollbar, -delta * drgui_sb_get_mouse_wheel_scale(pTB->pVertScrollbar));
-}
-
-void drgui_textbox_on_key_down(drgui_element* pTBElement, drgui_key key, int stateFlags)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- switch (key)
- {
- case DRGUI_BACKSPACE:
- {
- bool wasTextChanged = false;
- drgui_text_engine_prepare_undo_point(pTB->pTL);
- {
- if (drgui_text_engine_is_anything_selected(pTB->pTL)) {
- wasTextChanged = drgui_text_engine_delete_selected_text(pTB->pTL);
- } else {
- wasTextChanged = drgui_text_engine_delete_character_to_left_of_cursor(pTB->pTL);
- }
- }
- if (wasTextChanged) { drgui_text_engine_commit_undo_point(pTB->pTL); }
- } break;
-
- case DRGUI_DELETE:
- {
- bool wasTextChanged = false;
- drgui_text_engine_prepare_undo_point(pTB->pTL);
- {
- if (drgui_text_engine_is_anything_selected(pTB->pTL)) {
- wasTextChanged = drgui_text_engine_delete_selected_text(pTB->pTL);
- } else {
- wasTextChanged = drgui_text_engine_delete_character_to_right_of_cursor(pTB->pTL);
- }
- }
- if (wasTextChanged) { drgui_text_engine_commit_undo_point(pTB->pTL); }
- } break;
-
-
- case DRGUI_ARROW_LEFT:
- {
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
- drgui_text_engine_enter_selection_mode(pTB->pTL);
- }
-
- if (drgui_text_engine_is_anything_selected(pTB->pTL) && !drgui_text_engine_is_in_selection_mode(pTB->pTL)) {
- drgui_text_engine_move_cursor_to_start_of_selection(pTB->pTL);
- drgui_text_engine_deselect_all(pTB->pTL);
- } else {
- drgui_text_engine_move_cursor_left(pTB->pTL);
- }
-
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
- drgui_text_engine_leave_selection_mode(pTB->pTL);
- }
- } break;
-
- case DRGUI_ARROW_RIGHT:
- {
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
- drgui_text_engine_enter_selection_mode(pTB->pTL);
- }
-
- if (drgui_text_engine_is_anything_selected(pTB->pTL) && !drgui_text_engine_is_in_selection_mode(pTB->pTL)) {
- drgui_text_engine_move_cursor_to_end_of_selection(pTB->pTL);
- drgui_text_engine_deselect_all(pTB->pTL);
- } else {
- drgui_text_engine_move_cursor_right(pTB->pTL);
- }
-
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
- drgui_text_engine_leave_selection_mode(pTB->pTL);
- }
- } break;
-
- case DRGUI_ARROW_UP:
- {
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
- drgui_text_engine_enter_selection_mode(pTB->pTL);
- }
-
- if (drgui_text_engine_is_anything_selected(pTB->pTL) && !drgui_text_engine_is_in_selection_mode(pTB->pTL)) {
- drgui_text_engine_deselect_all(pTB->pTL);
- }
-
- drgui_text_engine_move_cursor_up(pTB->pTL);
-
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
- drgui_text_engine_leave_selection_mode(pTB->pTL);
- }
- } break;
-
- case DRGUI_ARROW_DOWN:
- {
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
- drgui_text_engine_enter_selection_mode(pTB->pTL);
- }
-
- if (drgui_text_engine_is_anything_selected(pTB->pTL) && !drgui_text_engine_is_in_selection_mode(pTB->pTL)) {
- drgui_text_engine_deselect_all(pTB->pTL);
- }
-
- drgui_text_engine_move_cursor_down(pTB->pTL);
-
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
- drgui_text_engine_leave_selection_mode(pTB->pTL);
- }
- } break;
-
-
- case DRGUI_END:
- {
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
- drgui_text_engine_enter_selection_mode(pTB->pTL);
- }
-
- if (drgui_text_engine_is_anything_selected(pTB->pTL) && !drgui_text_engine_is_in_selection_mode(pTB->pTL)) {
- drgui_text_engine_deselect_all(pTB->pTL);
- }
-
- if ((stateFlags & DRGUI_KEY_STATE_CTRL_DOWN) != 0) {
- drgui_text_engine_move_cursor_to_end_of_text(pTB->pTL);
- } else {
- drgui_text_engine_move_cursor_to_end_of_line(pTB->pTL);
- }
-
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
- drgui_text_engine_leave_selection_mode(pTB->pTL);
- }
- } break;
-
- case DRGUI_HOME:
- {
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
- drgui_text_engine_enter_selection_mode(pTB->pTL);
- }
-
- if (drgui_text_engine_is_anything_selected(pTB->pTL) && !drgui_text_engine_is_in_selection_mode(pTB->pTL)) {
- drgui_text_engine_deselect_all(pTB->pTL);
- }
-
- if ((stateFlags & DRGUI_KEY_STATE_CTRL_DOWN) != 0) {
- drgui_text_engine_move_cursor_to_start_of_text(pTB->pTL);
- } else {
- drgui_text_engine_move_cursor_to_start_of_line(pTB->pTL);
- }
-
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
- drgui_text_engine_leave_selection_mode(pTB->pTL);
- }
- } break;
-
- case DRGUI_PAGE_UP:
- {
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
- drgui_text_engine_enter_selection_mode(pTB->pTL);
- }
-
- if (drgui_text_engine_is_anything_selected(pTB->pTL) && !drgui_text_engine_is_in_selection_mode(pTB->pTL)) {
- drgui_text_engine_deselect_all(pTB->pTL);
- }
-
- int scrollOffset = drgui_sb_get_page_size(pTB->pVertScrollbar);
- if ((stateFlags & DRGUI_KEY_STATE_CTRL_DOWN) == 0) {
- drgui_sb_scroll(pTB->pVertScrollbar, -scrollOffset);
- }
-
- drgui_text_engine_move_cursor_y(pTB->pTL, -drgui_sb_get_page_size(pTB->pVertScrollbar));
-
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
- drgui_text_engine_leave_selection_mode(pTB->pTL);
- }
- } break;
-
- case DRGUI_PAGE_DOWN:
- {
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
- drgui_text_engine_enter_selection_mode(pTB->pTL);
- }
-
- if (drgui_text_engine_is_anything_selected(pTB->pTL) && !drgui_text_engine_is_in_selection_mode(pTB->pTL)) {
- drgui_text_engine_deselect_all(pTB->pTL);
- }
-
- int scrollOffset = drgui_sb_get_page_size(pTB->pVertScrollbar);
- if (scrollOffset > (int)(drgui_text_engine_get_line_count(pTB->pTL) - drgui_text_engine_get_cursor_line(pTB->pTL))) {
- scrollOffset = 0;
- }
-
- if ((stateFlags & DRGUI_KEY_STATE_CTRL_DOWN) == 0) {
- drgui_sb_scroll(pTB->pVertScrollbar, scrollOffset);
- }
-
- drgui_text_engine_move_cursor_y(pTB->pTL, drgui_sb_get_page_size(pTB->pVertScrollbar));
-
- if ((stateFlags & DRGUI_KEY_STATE_SHIFT_DOWN) != 0) {
- drgui_text_engine_leave_selection_mode(pTB->pTL);
- }
- } break;
-
- default: break;
- }
-}
-
-void drgui_textbox_on_key_up(drgui_element* pTBElement, drgui_key key, int stateFlags)
-{
- (void)pTBElement;
- (void)key;
- (void)stateFlags;
-}
-
-void drgui_textbox_on_printable_key_down(drgui_element* pTBElement, unsigned int utf32, int stateFlags)
-{
- (void)stateFlags;
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_prepare_undo_point(pTB->pTL);
- {
- if (drgui_text_engine_is_anything_selected(pTB->pTL)) {
- drgui_text_engine_delete_selected_text(pTB->pTL);
- }
-
- drgui_text_engine_insert_character_at_cursor(pTB->pTL, utf32);
- }
- drgui_text_engine_commit_undo_point(pTB->pTL);
-}
-
-
-DRGUI_PRIVATE void drgui_textbox__on_text_engine_paint_rect(drgui_text_engine* pTL, drgui_rect rect, drgui_color color, drgui_element* pTBElement, void* pPaintData)
-{
- (void)pTL;
-
- float offsetX;
- float offsetY;
- drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
-
- drgui_draw_rect(pTBElement, drgui_offset_rect(rect, offsetX, offsetY), color, pPaintData);
-}
-
-DRGUI_PRIVATE void drgui_textbox__on_text_engine_paint_text(drgui_text_engine* pTL, drgui_text_run* pRun, drgui_element* pTBElement, void* pPaintData)
-{
- (void)pTL;
-
- float offsetX;
- float offsetY;
- drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
-
- drgui_draw_text(pTBElement, pRun->pFont, pRun->text, (int)pRun->textLength, (float)pRun->posX + offsetX, (float)pRun->posY + offsetY, pRun->textColor, pRun->backgroundColor, pPaintData);
-}
-
-DRGUI_PRIVATE void drgui_textbox__on_text_engine_dirty(drgui_text_engine* pTL, drgui_rect rect)
-{
- drgui_element* pTBElement = *(drgui_element**)drgui_text_engine_get_extra_data(pTL);
- if (pTBElement == NULL) {
- return;
- }
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- float offsetX;
- float offsetY;
- drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
-
- drgui_dirty(pTBElement, drgui_offset_rect(rect, offsetX, offsetY));
-}
-
-DRGUI_PRIVATE void drgui_textbox__on_text_engine_cursor_move(drgui_text_engine* pTL)
-{
- // If the cursor is off the edge of the container we want to scroll it into position.
- drgui_element* pTBElement = *(drgui_element**)drgui_text_engine_get_extra_data(pTL);
- if (pTBElement == NULL) {
- return;
- }
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- // If the cursor is above or below the container, we need to scroll vertically.
- int iLine = (int)drgui_text_engine_get_cursor_line(pTB->pTL);
- if (iLine < drgui_sb_get_scroll_position(pTB->pVertScrollbar)) {
- drgui_sb_scroll_to(pTB->pVertScrollbar, iLine);
- }
-
- int iBottomLine = drgui_sb_get_scroll_position(pTB->pVertScrollbar) + drgui_sb_get_page_size(pTB->pVertScrollbar) - 1;
- if (iLine >= iBottomLine) {
- drgui_sb_scroll_to(pTB->pVertScrollbar, iLine - (drgui_sb_get_page_size(pTB->pVertScrollbar) - 1) + 1);
- }
-
-
- // If the cursor is to the left or right of the container we need to scroll horizontally.
- float cursorPosX;
- float cursorPosY;
- drgui_text_engine_get_cursor_position(pTB->pTL, &cursorPosX, &cursorPosY);
-
- if (cursorPosX < 0) {
- drgui_sb_scroll_to(pTB->pHorzScrollbar, (int)(cursorPosX - drgui_text_engine_get_inner_offset_x(pTB->pTL)) - 100);
- }
- if (cursorPosX >= drgui_text_engine_get_container_width(pTB->pTL)) {
- drgui_sb_scroll_to(pTB->pHorzScrollbar, (int)(cursorPosX - drgui_text_engine_get_inner_offset_x(pTB->pTL) - drgui_text_engine_get_container_width(pTB->pTL)) + 100);
- }
-
-
- if (pTB->onCursorMove) {
- pTB->onCursorMove(pTBElement);
- }
-}
-
-DRGUI_PRIVATE void drgui_textbox__on_text_engine_text_changed(drgui_text_engine* pTL)
-{
- drgui_element* pTBElement = *(drgui_element**)drgui_text_engine_get_extra_data(pTL);
- if (pTBElement == NULL) {
- return;
- }
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- // Scrollbars need to be refreshed whenever text is changed.
- drgui_textbox__refresh_scrollbars(pTBElement);
-
- // The line numbers need to be redrawn.
- // TODO: This can probably be optimized a bit so that it is only redrawn if a line was inserted or deleted.
- drgui_dirty(pTB->pLineNumbers, drgui_get_local_rect(pTB->pLineNumbers));
-}
-
-DRGUI_PRIVATE void drgui_textbox__on_text_engine_undo_point_changed(drgui_text_engine* pTL, unsigned int iUndoPoint)
-{
- drgui_element* pTBElement = *(drgui_element**)drgui_text_engine_get_extra_data(pTL);
- if (pTBElement == NULL) {
- return;
- }
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- if (pTB->onUndoPointChanged) {
- pTB->onUndoPointChanged(pTBElement, iUndoPoint);
- }
-}
-
-
-void drgui_textbox_on_paint(drgui_element* pTBElement, drgui_rect relativeRect, void* pPaintData)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_rect textRect = drgui_textbox__get_text_rect(pTBElement);
-
- // The dead space between the scrollbars should always be drawn with the default background color.
- drgui_draw_rect(pTBElement, drgui_textbox__get_scrollbar_dead_space_rect(pTBElement), drgui_text_engine_get_default_bg_color(pTB->pTL), pPaintData);
-
- // Border.
- drgui_rect borderRect = drgui_get_local_rect(pTBElement);
- drgui_draw_rect_outline(pTBElement, borderRect, pTB->borderColor, pTB->borderWidth, pPaintData);
-
- // Padding.
- drgui_rect paddingRect = drgui_grow_rect(textRect, pTB->padding);
- drgui_draw_rect_outline(pTBElement, paddingRect, drgui_text_engine_get_default_bg_color(pTB->pTL), pTB->padding, pPaintData);
-
- // Text.
- drgui_set_clip(pTBElement, drgui_clamp_rect(textRect, relativeRect), pPaintData);
- drgui_text_engine_paint(pTB->pTL, drgui_offset_rect(drgui_clamp_rect(textRect, relativeRect), -textRect.left, -textRect.top), pTBElement, pPaintData);
-}
-
-void drgui_textbox_on_capture_keyboard(drgui_element* pTBElement, drgui_element* pPrevCapturedElement)
-{
- (void)pPrevCapturedElement;
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_show_cursor(pTB->pTL);
-}
-
-void drgui_textbox_on_release_keyboard(drgui_element* pTBElement, drgui_element* pNewCapturedElement)
-{
- (void)pNewCapturedElement;
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_hide_cursor(pTB->pTL);
-}
-
-void drgui_textbox_on_capture_mouse(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
-}
-
-void drgui_textbox_on_release_mouse(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return;
- }
-
- drgui_text_engine_leave_selection_mode(pTB->pTL);
-}
-
-
-
-DRGUI_PRIVATE void drgui_textbox__get_text_offset(drgui_element* pTBElement, float* pOffsetXOut, float* pOffsetYOut)
-{
- float offsetX = 0;
- float offsetY = 0;
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB != NULL)
- {
- float lineNumbersWidth = 0;
- if (drgui_is_visible(pTB->pLineNumbers)) {
- lineNumbersWidth = drgui_get_width(pTB->pLineNumbers);
- }
-
- offsetX = pTB->borderWidth + pTB->padding + lineNumbersWidth;
- offsetY = pTB->borderWidth + pTB->padding;
- }
-
-
- if (pOffsetXOut != NULL) {
- *pOffsetXOut = offsetX;
- }
- if (pOffsetYOut != NULL) {
- *pOffsetYOut = offsetY;
- }
-}
-
-DRGUI_PRIVATE void drgui_textbox__calculate_text_engine_container_size(drgui_element* pTBElement, float* pWidthOut, float* pHeightOut)
-{
- float width = 0;
- float height = 0;
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB != NULL)
- {
- float horzScrollbarSize = 0;
- if (drgui_is_visible(pTB->pHorzScrollbar)) {
- horzScrollbarSize = drgui_get_height(pTB->pHorzScrollbar);
- }
-
- float vertScrollbarSize = 0;
- if (drgui_is_visible(pTB->pVertScrollbar)) {
- vertScrollbarSize = drgui_get_width(pTB->pVertScrollbar);
- }
-
- float lineNumbersWidth = 0;
- if (drgui_is_visible(pTB->pLineNumbers)) {
- lineNumbersWidth = drgui_get_width(pTB->pLineNumbers);
- }
-
- width = drgui_get_width(pTBElement) - (pTB->borderWidth + pTB->padding)*2 - vertScrollbarSize - lineNumbersWidth;
- height = drgui_get_height(pTBElement) - (pTB->borderWidth + pTB->padding)*2 - horzScrollbarSize;
- }
-
- if (pWidthOut != NULL) {
- *pWidthOut = width;
- }
- if (pHeightOut != NULL) {
- *pHeightOut = height;
- }
-}
-
-DRGUI_PRIVATE drgui_rect drgui_textbox__get_text_rect(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- if (pTB == NULL) {
- return drgui_make_rect(0, 0, 0, 0);
- }
-
- float offsetX;
- float offsetY;
- drgui_textbox__get_text_offset(pTBElement, &offsetX, &offsetY);
-
- float width;
- float height;
- drgui_textbox__calculate_text_engine_container_size(pTBElement, &width, &height);
-
- return drgui_make_rect(offsetX, offsetY, offsetX + width, offsetY + height);
-}
-
-
-DRGUI_PRIVATE void drgui_textbox__refresh_scrollbars(drgui_element* pTBElement)
-{
- // The layout depends on the range because we may be dynamically hiding and showing the scrollbars depending on the range. Thus, we
- // refresh the range first. However, dynamically showing and hiding the scrollbars (which is done when the layout is refreshed) affects
- // the size of the text box, which in turn affects the range. Thus, we need to refresh the ranges a second time after the layouts.
-
- drgui_textbox__refresh_scrollbar_ranges(pTBElement);
- drgui_textbox__refresh_scrollbar_layouts(pTBElement);
- drgui_textbox__refresh_scrollbar_ranges(pTBElement);
-}
-
-DRGUI_PRIVATE void drgui_textbox__refresh_scrollbar_ranges(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- assert(pTB != NULL);
-
- // The vertical scrollbar is based on the line count.
- size_t lineCount = drgui_text_engine_get_line_count(pTB->pTL);
- size_t pageSize = drgui_text_engine_get_visible_line_count_starting_at(pTB->pTL, drgui_sb_get_scroll_position(pTB->pVertScrollbar));
- drgui_sb_set_range_and_page_size(pTB->pVertScrollbar, 0, (int)(lineCount + pageSize - 1 - 1), (int)pageSize); // -1 to make the range 0 based. -1 to ensure at least one line is visible.
-
- if (drgui_sb_is_thumb_visible(pTB->pVertScrollbar)) {
- if (!drgui_is_visible(pTB->pVertScrollbar)) {
- drgui_show(pTB->pVertScrollbar);
- }
- } else {
- if (drgui_is_visible(pTB->pVertScrollbar)) {
- drgui_hide(pTB->pVertScrollbar);
- }
- }
-
-
- // The horizontal scrollbar is a per-pixel scrollbar, and is based on the width of the text versus the width of the container.
- drgui_rect textRect = drgui_text_engine_get_text_rect_relative_to_bounds(pTB->pTL);
- float containerWidth;
- drgui_text_engine_get_container_size(pTB->pTL, &containerWidth, NULL);
- drgui_sb_set_range_and_page_size(pTB->pHorzScrollbar, 0, (int)(textRect.right - textRect.left + (containerWidth/2)), (int)containerWidth);
-
- if (drgui_sb_is_thumb_visible(pTB->pHorzScrollbar)) {
- if (!drgui_is_visible(pTB->pHorzScrollbar)) {
- drgui_show(pTB->pHorzScrollbar);
- drgui_textbox__refresh_line_numbers(pTBElement);
- }
- } else {
- if (drgui_is_visible(pTB->pHorzScrollbar)) {
- drgui_hide(pTB->pHorzScrollbar);
- drgui_textbox__refresh_line_numbers(pTBElement);
- }
- }
-}
-
-DRGUI_PRIVATE void drgui_textbox__refresh_scrollbar_layouts(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- assert(pTB != NULL);
-
- float offsetLeft = pTB->borderWidth;
- float offsetTop = pTB->borderWidth;
- float offsetRight = pTB->borderWidth;
- float offsetBottom = pTB->borderWidth;
-
- float scrollbarSizeH = (drgui_sb_is_thumb_visible(pTB->pHorzScrollbar) && pTB->isHorzScrollbarEnabled) ? pTB->horzScrollbarSize : 0;
- float scrollbarSizeV = (drgui_sb_is_thumb_visible(pTB->pVertScrollbar) && pTB->isVertScrollbarEnabled) ? pTB->vertScrollbarSize : 0;
-
- drgui_set_size(pTB->pVertScrollbar, scrollbarSizeV, drgui_get_height(pTBElement) - scrollbarSizeH - (offsetTop + offsetBottom));
- drgui_set_size(pTB->pHorzScrollbar, drgui_get_width(pTBElement) - scrollbarSizeV - (offsetLeft + offsetRight), scrollbarSizeH);
-
- drgui_set_relative_position(pTB->pVertScrollbar, drgui_get_width(pTBElement) - scrollbarSizeV - offsetRight, offsetTop);
- drgui_set_relative_position(pTB->pHorzScrollbar, offsetLeft, drgui_get_height(pTBElement) - scrollbarSizeH - offsetBottom);
-
-
- // A change in the layout of the horizontal scrollbar will affect the layout of the line numbers.
- drgui_textbox__refresh_line_numbers(pTBElement);
-}
-
-DRGUI_PRIVATE drgui_rect drgui_textbox__get_scrollbar_dead_space_rect(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- assert(pTB != NULL);
-
- float offsetLeft = pTB->borderWidth;
- float offsetTop = pTB->borderWidth;
- float offsetRight = pTB->borderWidth;
- float offsetBottom = pTB->borderWidth;
-
- float scrollbarSizeH = (drgui_is_visible(pTB->pHorzScrollbar) && pTB->isHorzScrollbarEnabled) ? drgui_get_width(pTB->pHorzScrollbar) : 0;
- float scrollbarSizeV = (drgui_is_visible(pTB->pVertScrollbar) && pTB->isHorzScrollbarEnabled) ? drgui_get_height(pTB->pVertScrollbar) : 0;
-
- if (scrollbarSizeH == 0 && scrollbarSizeV == 0) {
- return drgui_make_rect(0, 0, 0, 0);
- }
-
- return drgui_make_rect(scrollbarSizeH + offsetLeft, scrollbarSizeV + offsetTop, drgui_get_width(pTBElement) - offsetRight, drgui_get_height(pTBElement) - offsetBottom);
-}
-
-
-DRGUI_PRIVATE void drgui_textbox__on_mouse_move_line_numbers(drgui_element* pLineNumbers, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)relativeMousePosX;
-
- drgui_element* pTBElement = *(drgui_element**)drgui_get_extra_data(pLineNumbers);
- assert(pTBElement != NULL);
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- assert(pTB != NULL);
-
- if ((stateFlags & DRGUI_MOUSE_BUTTON_LEFT_DOWN) != 0)
- {
- if (drgui_get_element_with_mouse_capture(pLineNumbers->pContext) == pLineNumbers)
- {
- // We just move the cursor around based on the line number we've moved over.
- drgui_text_engine_enter_selection_mode(pTB->pTL);
- {
- //float offsetX = pTextEditorData->padding;
- float offsetY = pTB->padding;
- size_t iLine = drgui_text_engine_get_line_at_pos_y(pTB->pTL, relativeMousePosY - offsetY);
- size_t iAnchorLine = pTB->iLineSelectAnchor;
- size_t lineCount = drgui_text_engine_get_line_count(pTB->pTL);
-
- size_t iSelectionFirstLine = drgui_text_engine_get_selection_first_line(pTB->pTL);
- size_t iSelectionLastLine = drgui_text_engine_get_selection_last_line(pTB->pTL);
- if (iSelectionLastLine != iSelectionFirstLine) {
- iSelectionLastLine -= 1;
- }
-
- // If we're moving updwards we want to position the cursor at the start of the line. Otherwise we want to move the cursor to the start
- // of the next line, or the end of the text.
- bool movingUp = false;
- if (iLine < iAnchorLine) {
- movingUp = true;
- }
-
- // If we're moving up the selection anchor needs to be placed at the end of the last line. Otherwise we need to move it to the start
- // of the first line.
- if (movingUp) {
- if (iAnchorLine + 1 < lineCount) {
- drgui_text_engine_move_selection_anchor_to_start_of_line(pTB->pTL, iAnchorLine + 1);
- } else {
- drgui_text_engine_move_selection_anchor_to_end_of_line(pTB->pTL, iAnchorLine);
- }
- } else {
- drgui_text_engine_move_selection_anchor_to_start_of_line(pTB->pTL, iAnchorLine);
- }
-
-
- // If we're moving up we want the cursor to be placed at the start of the selection range. Otherwise we want to place the cursor
- // at the end of the selection range.
- if (movingUp) {
- drgui_text_engine_move_cursor_to_start_of_line_by_index(pTB->pTL, iLine);
- } else {
- if (iLine + 1 < lineCount) {
- drgui_text_engine_move_cursor_to_start_of_line_by_index(pTB->pTL, iLine + 1);
- } else {
- drgui_text_engine_move_cursor_to_end_of_line_by_index(pTB->pTL, iLine);
- }
- }
- }
- drgui_text_engine_leave_selection_mode(pTB->pTL);
- }
- }
-}
-
-DRGUI_PRIVATE void drgui_textbox__on_mouse_button_down_line_numbers(drgui_element* pLineNumbers, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)relativeMousePosX;
- (void)stateFlags;
-
- drgui_element* pTBElement = *(drgui_element**)drgui_get_extra_data(pLineNumbers);
- assert(pTBElement != NULL);
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- assert(pTB != NULL);
-
- if (mouseButton == DRGUI_MOUSE_BUTTON_LEFT)
- {
- //float offsetX = pTextEditorData->padding;
- float offsetY = pTB->padding;
- pTB->iLineSelectAnchor = drgui_text_engine_get_line_at_pos_y(pTB->pTL, relativeMousePosY - offsetY);
-
- drgui_text_engine_deselect_all(pTB->pTL);
-
- drgui_text_engine_move_cursor_to_start_of_line_by_index(pTB->pTL, pTB->iLineSelectAnchor);
-
- drgui_text_engine_enter_selection_mode(pTB->pTL);
- {
- if (pTB->iLineSelectAnchor + 1 < drgui_text_engine_get_line_count(pTB->pTL)) {
- drgui_text_engine_move_cursor_to_start_of_line_by_index(pTB->pTL, pTB->iLineSelectAnchor + 1);
- } else {
- drgui_text_engine_move_cursor_to_end_of_line(pTB->pTL);
- }
- }
- drgui_text_engine_leave_selection_mode(pTB->pTL);
-
- drgui_capture_mouse(pLineNumbers);
- }
-}
-
-DRGUI_PRIVATE void drgui_textbox__on_mouse_button_up_line_numbers(drgui_element* pLineNumbers, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)relativeMousePosX;
- (void)relativeMousePosY;
- (void)stateFlags;
-
- if (mouseButton == DRGUI_MOUSE_BUTTON_LEFT) {
- if (drgui_has_mouse_capture(pLineNumbers)) {
- drgui_release_mouse(pLineNumbers->pContext);
- }
- }
-}
-
-DRGUI_PRIVATE void drgui_textbox__on_paint_rect_line_numbers(drgui_text_engine* pLayout, drgui_rect rect, drgui_color color, drgui_element* pTBElement, void* pPaintData)
-{
- (void)pLayout;
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- assert(pTB != NULL);
-
- float offsetX = pTB->padding;
- float offsetY = pTB->padding;
-
- drgui_draw_rect(pTB->pLineNumbers, drgui_offset_rect(rect, offsetX, offsetY), color, pPaintData);
-}
-
-DRGUI_PRIVATE void drgui_textbox__on_paint_text_line_numbers(drgui_text_engine* pLayout, drgui_text_run* pRun, drgui_element* pTBElement, void* pPaintData)
-{
- (void)pLayout;
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- assert(pTB != NULL);
-
- float offsetX = pTB->padding;
- float offsetY = pTB->padding;
- drgui_draw_text(pTB->pLineNumbers, pRun->pFont, pRun->text, (int)pRun->textLength, (float)pRun->posX + offsetX, (float)pRun->posY + offsetY, pRun->textColor, pRun->backgroundColor, pPaintData);
-}
-
-DRGUI_PRIVATE void drgui_textbox__on_paint_line_numbers(drgui_element* pLineNumbers, drgui_rect relativeRect, void* pPaintData)
-{
- (void)relativeRect;
-
- drgui_element* pTBElement = *((drgui_element**)drgui_get_extra_data(pLineNumbers));
- assert(pTBElement != NULL);
-
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- assert(pTB != NULL);
-
- float lineNumbersWidth = drgui_get_width(pLineNumbers) - (pTB->padding*2) - pTB->lineNumbersPaddingRight;
- float lineNumbersHeight = drgui_get_height(pLineNumbers) - (pTB->padding*2);
-
- drgui_text_engine_paint_line_numbers(pTB->pTL, lineNumbersWidth, lineNumbersHeight, pTB->lineNumbersColor, pTB->lineNumbersBackgroundColor, drgui_textbox__on_paint_text_line_numbers, drgui_textbox__on_paint_rect_line_numbers, pTBElement, pPaintData);
-
- drgui_draw_rect_outline(pLineNumbers, drgui_get_local_rect(pLineNumbers), pTB->lineNumbersBackgroundColor, pTB->padding, pPaintData);
-
- // Right padding.
- drgui_rect rightPaddingRect = drgui_get_local_rect(pLineNumbers);
- rightPaddingRect.right -= pTB->padding;
- rightPaddingRect.left = rightPaddingRect.right - pTB->lineNumbersPaddingRight;
- drgui_draw_rect(pLineNumbers, rightPaddingRect, pTB->lineNumbersBackgroundColor, pPaintData);
-}
-
-DRGUI_PRIVATE void drgui_textbox__refresh_line_numbers(drgui_element* pTBElement)
-{
- drgui_textbox* pTB = (drgui_textbox*)drgui_get_extra_data(pTBElement);
- assert(pTB != NULL);
-
- float lineNumbersWidth = 0;
- if (drgui_is_visible(pTB->pLineNumbers)) {
- lineNumbersWidth = pTB->lineNumbersWidth;
- }
-
- float scrollbarHeight = drgui_is_visible(pTB->pHorzScrollbar) ? drgui_get_height(pTB->pHorzScrollbar) : 0;
- drgui_set_size(pTB->pLineNumbers, lineNumbersWidth, drgui_get_height(pTBElement) - scrollbarHeight);
-
-
- // The size of the text container may have changed.
- float textEditorWidth;
- float textEditorHeight;
- drgui_textbox__calculate_text_engine_container_size(pTBElement, &textEditorWidth, &textEditorHeight);
- drgui_text_engine_set_container_size(pTB->pTL, textEditorWidth, textEditorHeight);
-
-
- // Force a redraw just to be sure everything is in a valid state.
- drgui_dirty(pTBElement, drgui_get_local_rect(pTBElement));
-}
-#endif //DR_GUI_IMPLEMENTATION
-#endif //DRGUI_NO_TEXT_EDITING
-
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-//
-// Tree View
-//
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-// QUICK NOTES
-//
-// Tree-View Controls
-// - A tree-view control is a complex control with a hierarchy of items. They are typically used for file explorers.
-
-#ifndef drgui_tree_view_h
-#define drgui_tree_view_h
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define EG_MAX_TREE_VIEW_ITEM_TEXT_LENGTH 256
-
-typedef struct drgui_tree_view_item drgui_tree_view_item;
-
-typedef void (* drgui_tvi_on_mouse_move_proc) (drgui_tree_view_item* pItem, int relativeMousePosX, int relativeMousePosY, bool* pIsOverArrow);
-typedef void (* drgui_tvi_on_mouse_leave_proc) (drgui_tree_view_item* pItem);
-typedef void (* drgui_tvi_on_paint_proc) (drgui_element* pTVElement, drgui_tree_view_item* pItem, drgui_rect relativeClippingRect, drgui_color backgroundColor, float offsetX, float offsetY, float width, float height, void* pPaintData);
-typedef void (* drgui_tvi_measure_proc) (drgui_tree_view_item* pItem, float* pWidthOut, float* pHeightOut);
-typedef void (* drgui_tvi_on_picked_proc) (drgui_tree_view_item* pItem);
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// Tree-View
-//
-///////////////////////////////////////////////////////////////////////////////
-
-/// Creates a tree-view control.
-drgui_element* drgui_create_tree_view(drgui_context* pContext, drgui_element* pParent, size_t extraDataSize, const void* pExtraData);
-
-/// Deletes the given tree-view control.
-void drgui_delete_tree_view(drgui_element* pTVElement);
-
-
-/// Retrieves the size of the extra data associated with the given tree-view control.
-size_t drgui_tv_get_extra_data_size(drgui_element* pTVElement);
-
-/// Retrieves a pointer to the buffer containing the given tree-view's extra data.
-void* drgui_tv_get_extra_data(drgui_element* pTVElement);
-
-/// Retrieves a pointer to the root element of the given tree view control.
-drgui_tree_view_item* drgui_tv_get_root_item(drgui_element* pTVElement);
-
-/// Retrieves a pointer to the vertical scrollbar.
-drgui_element* drgui_tv_get_vertical_scrollbar(drgui_element* pTVElement);
-
-/// Retrieves a pointer to the horizontal scrollbar.
-drgui_element* drgui_tv_get_horizontal_scrollbar(drgui_element* pTVElement);
-
-
-/// Sets the default background color.
-void drgui_tv_set_default_background_color(drgui_element* pTVElement, drgui_color color);
-
-/// Retrieves the default background color.
-drgui_color drgui_tv_get_default_background_color(drgui_element* pTVElement);
-
-/// Sets the default background color of hovered items.
-void drgui_tv_set_hovered_background_color(drgui_element* pTVElement, drgui_color color);
-
-/// Retrieves the default background color of hovered items.
-drgui_color drgui_tv_get_hovered_background_color(drgui_element* pTVElement);
-
-/// Sets the default background color of selected items.
-void drgui_tv_set_selected_background_color(drgui_element* pTVElement, drgui_color color);
-
-/// Retrieves the default background color of selected items.
-drgui_color drgui_tv_get_selected_background_color(drgui_element* pTVElement);
-
-/// Sets the amount of indentation to apply to each child item in the given tree-view.
-void drgui_tv_set_child_offset_x(drgui_element* pTVElement, float childOffsetX);
-
-/// Retrieves the amount of indentation to apply to each child item in the given tree-view.
-float drgui_tv_get_child_offset_x(drgui_element* pTVElement);
-
-
-/// Measures the given item.
-bool drgui_tv_measure_item(drgui_element* pTVElement, drgui_tree_view_item* pItem, float* pWidthOut, float* pHeightOut);
-
-/// Deselects every tree-view item.
-void drgui_tv_deselect_all_items(drgui_element* pTVElement);
-
-/// Enables multi-select.
-///
-/// @remarks
-/// While this is enabled, selections will accumulate. Typically you would call this when the user hits
-/// the CTRL key, and then call drgui_tv_disable_multi_select() when the user releases it.
-void drgui_tv_enable_multi_select(drgui_element* pTVElement);
-
-/// Disables multi-select.
-void drgui_tv_disable_multi_select(drgui_element* pTVElement);
-
-/// Determines whether or not multi-select is enabled.
-bool drgui_tv_is_multi_select_enabled(drgui_element* pTVElement);
-
-
-/// Retrieves the first selected item.
-///
-/// @remarks
-/// This runs in linear time.
-drgui_tree_view_item* drgui_tv_get_first_selected_item(drgui_element* pTVElement);
-
-/// Retrieves the next select item, not including the given item.
-///
-/// @remarks
-/// Use this in conjunction with drgui_tv_get_first_selected_item() to iterate over each selected item.
-/// @par
-/// The order in which retrieving selected items is based on their location in the hierarchy, and not the
-/// order in which they were selected.
-drgui_tree_view_item* drgui_tv_get_next_selected_item(drgui_element* pTVElement, drgui_tree_view_item* pItem);
-
-
-/// Sets the function to call when the mouse is moved while over a tree-view item.
-void drgui_tv_set_on_item_mouse_move(drgui_element* pTVElement, drgui_tvi_on_mouse_move_proc proc);
-
-/// Sets the function call when the mouse leaves a tree-view item.
-void drgui_tv_set_on_item_mouse_leave(drgui_element* pTVElement, drgui_tvi_on_mouse_leave_proc proc);
-
-/// Sets the function to call when a tree-view item needs to be drawn.
-void drgui_tv_set_on_item_paint(drgui_element* pTVElement, drgui_tvi_on_paint_proc proc);
-
-/// Sets the function to call when a tree-view item needs to be measured.
-void drgui_tv_set_on_item_measure(drgui_element* pTVElement, drgui_tvi_measure_proc proc);
-
-/// Sets the function to call when a tree-view item is picked.
-///
-/// @remarks
-/// An item is "picked" when it is a leaf item (has no children) and is double-clicked.
-void drgui_tv_set_on_item_picked(drgui_element* pTVElement, drgui_tvi_on_picked_proc proc);
-
-
-/// Called when the size event needs to be processed for the given tree-view control.
-void drgui_tv_on_size(drgui_element* pTVElement, float newWidth, float newHeight);
-
-/// Called when the mouse leave event needs to be processed for the given tree-view control.
-void drgui_tv_on_mouse_leave(drgui_element* pTVElement);
-
-/// Called when the mouse move event needs to be processed for the given tree-view control.
-void drgui_tv_on_mouse_move(drgui_element* pTVElement, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// Called when the mouse button down event needs to be processed for the given tree-view control.
-void drgui_tv_on_mouse_button_down(drgui_element* pTVElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// Called when the mouse button up event needs to be processed for the given tree-view control.
-void drgui_tv_on_mouse_button_up(drgui_element* pTVElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// Called when the mouse button double-click event needs to be processed for the given tree-view control.
-void drgui_tv_on_mouse_button_dblclick(drgui_element* pTVElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// Called when the mouse wheel event needs to be processed for the given tree-view control.
-void drgui_tv_on_mouse_wheel(drgui_element* pTVElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags);
-
-/// Called when the paint event needs to be processed for the given tree-view control.
-void drgui_tv_on_paint(drgui_element* pTVElement, drgui_rect relativeClippingRect, void* pPaintData);
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// Tree-View Item
-//
-///////////////////////////////////////////////////////////////////////////////
-
-/// Creates a tree view item.
-///
-/// @remarks
-/// When pParent is non-null, the tree-view control must match that of the tree-view control that owns the
-/// parent item.
-drgui_tree_view_item* drgui_tv_create_item(drgui_element* pTVElement, drgui_tree_view_item* pParent, size_t extraDataSize, const void* pExtraData);
-
-/// Recursively deletes a tree view item.
-void drgui_tvi_delete(drgui_tree_view_item* pItem);
-
-/// Retrieves the tree-view GUI element that owns the given item.
-drgui_element* drgui_tvi_get_tree_view_element(drgui_tree_view_item* pItem);
-
-/// Retrieves the size of the extra data associated with the given tree-view item.
-size_t drgui_tvi_get_extra_data_size(drgui_tree_view_item* pItem);
-
-/// Retrieves a pointer to the extra data associated with the given tree-view item.
-void* drgui_tvi_get_extra_data(drgui_tree_view_item* pItem);
-
-
-/// Retrieves the parent tree-view item.
-drgui_tree_view_item* drgui_tvi_get_parent(drgui_tree_view_item* pItem);
-
-/// Retrieves a pointer to the first child of the given tree-view item.
-drgui_tree_view_item* drgui_tvi_get_first_child(drgui_tree_view_item* pItem);
-
-/// Retrieves a pointer to the last child of the given tree-view item.
-drgui_tree_view_item* drgui_tvi_get_last_child(drgui_tree_view_item* pItem);
-
-/// Retrieves a pointer to the next sibling of the given tree-view item.
-drgui_tree_view_item* drgui_tvi_get_next_sibling(drgui_tree_view_item* pItem);
-
-/// Retrieves a pointer to the previous sibling of the given tree-view item.
-drgui_tree_view_item* drgui_tvi_get_prev_sibling(drgui_tree_view_item* pItem);
-
-/// Appends a tree view item as a child of the given parent item.
-void drgui_tvi_append(drgui_tree_view_item* pItem, drgui_tree_view_item* pParent);
-
-/// Prepends a tree view item as a child of the given parent item.
-void drgui_tvi_prepend(drgui_tree_view_item* pItem, drgui_tree_view_item* pParent);
-
-/// Appends the given tree view item to the given sibling.
-void drgui_tvi_append_sibling(drgui_tree_view_item* pItemToAppend, drgui_tree_view_item* pItemToAppendTo);
-
-/// Prepends the given tree view item to the given sibling.
-void drgui_tvi_prepend_sibling(drgui_tree_view_item* pItemToPrepend, drgui_tree_view_item* pItemToPrependTo);
-
-
-/// Determines whether or not the given item has any children.
-bool drgui_tvi_has_children(drgui_tree_view_item* pItem);
-
-/// Retrieves the depth of the item.
-///
-/// @remarks
-/// This is a recursive call and runs in linear time.
-int drgui_tvi_get_depth(drgui_tree_view_item* pItem);
-
-/// Retrieves a pointer to the next visible item in the hierarchy that is not a child.
-///
-/// @remarks
-/// This is used for iterating.
-/// @par
-/// is an input and output parameter that is decremented whenver the next item is an ancestor.
-drgui_tree_view_item* drgui_tvi_next_visible_non_child(drgui_tree_view_item* pItem, int* pDepthInOut);
-
-
-/// Selects the given item.
-void drgui_tvi_select(drgui_tree_view_item* pItem);
-
-/// Deselects the given item.
-void drgui_tvi_deselect(drgui_tree_view_item* pItem);
-
-/// Determines whether or not the given tree view item is selected.
-bool drgui_tvi_is_selected(drgui_tree_view_item* pItem);
-
-/// Expands the given item.
-void drgui_tvi_expand(drgui_tree_view_item* pItem);
-
-/// Collapses the given item.
-void drgui_tvi_collapse(drgui_tree_view_item* pItem);
-
-/// Determines whether or not the given item is expanded.
-bool drgui_tvi_is_expanded(drgui_tree_view_item* pItem);
-
-
-
-#ifdef __cplusplus
-}
-#endif
-#endif //drgui_tree_view_h
-
-
-#ifdef DR_GUI_IMPLEMENTATION
-typedef struct drgui_tree_view drgui_tree_view;
-
-struct drgui_tree_view
-{
- /// The root tree-view item.
- drgui_tree_view_item* pRootItem;
-
- /// The vertical scrollbar.
- drgui_element* pScrollbarV;
-
- /// The horizontal scrollbar.
- drgui_element* pScrollbarH;
-
-
- /// The default background color.
- drgui_color defaultBGColor;
-
- /// The hovered background color.
- drgui_color hoveredBGColor;
-
- /// The selected background color.
- drgui_color selectedBGColor;
-
- /// The amount of indentation to apply to each child item.
- float childOffsetX;
-
-
- /// The function to call when an item needs to handle a mouse movement event.
- drgui_tvi_on_mouse_move_proc onItemMouseMove;
-
- /// The function to call when an item needs to handle a mouse leave event.
- drgui_tvi_on_mouse_leave_proc onItemMouseLeave;
-
- /// The function to call when an item needs to be drawn.
- drgui_tvi_on_paint_proc onItemPaint;
-
- /// The function to call when an item needs to be measured.
- drgui_tvi_measure_proc onItemMeasure;
-
- /// The function to call when an item is picked.
- drgui_tvi_on_picked_proc onItemPicked;
-
-
- /// A pointer to the item the mouse is current hovered over.
- drgui_tree_view_item* pHoveredItem;
-
- /// Whether or not the mouse is hovered over the arrow of pHoveredItem.
- bool isMouseOverArrow;
-
- /// Whether or not the mouse is over the given element.
- bool isMouseOver;
-
- /// The relative position of the mouse on the x axis. This is updated whenever the mouse_move event is received.
- int relativeMousePosX;
-
- /// The relative position of the mouse on the y axis. This is updated whenever the mouse_move event is received.
- int relativeMousePosY;
-
-
- /// Whether or not multi-select is enabled.
- bool isMultiSelectEnabled;
-
- /// Whether or not range-select is enabled.
- bool isRangeSelectEnabled;
-
-
- /// The size of the extra data.
- size_t extraDataSize;
-
- /// A pointer to the extra data buffer.
- char pExtraData[1];
-};
-
-struct drgui_tree_view_item
-{
- /// The tree-view control that owns this item.
- drgui_element* pTVElement;
-
-
- /// A pointer to the parent item.
- drgui_tree_view_item* pParent;
-
- /// A pointer to the first child.
- drgui_tree_view_item* pFirstChild;
-
- /// A pointer to the last child.
- drgui_tree_view_item* pLastChild;
-
- /// A pointer to the next sibling.
- drgui_tree_view_item* pNextSibling;
-
- /// A pointer to the prev sibling.
- drgui_tree_view_item* pPrevSibling;
-
-
- /// Whether or not the item is select.
- bool isSelected;
-
- /// Whether or not the item is expanded.
- bool isExpanded;
-
-
- /// The size of the extra data.
- size_t extraDataSize;
-
- /// A pointer to the extra data buffer.
- char pExtraData[1];
-};
-
-typedef struct
-{
- /// A pointer to the relevant item.
- drgui_tree_view_item* pItem;
-
- /// The width of the item.
- float width;
-
- /// The height of the item.
- float height;
-
- /// The position of the item on the x axis.
- float posX;
-
- /// Top position of the item on the y axis.
- float posY;
-
- /// The depth of the item. This is used to calculate the offset of the item.
- int depth;
-
-} drgui_tree_view_iterator;
-
-typedef struct
-{
- /// The width of the item.
- float width;
-
- /// The height of the item.
- float height;
-
- /// The position of the item on the x axis.
- float posX;
-
- /// Top position of the item on the y axis.
- float posY;
-
-} drgui_tree_view_item_metrics;
-
-typedef struct
-{
- /// A pointer to the tree-view control that owns the scrollbar.
- drgui_element* pTVElement;
-
-} drgui_tree_view_scrollbar_data;
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// Tree-View
-//
-///////////////////////////////////////////////////////////////////////////////
-
-/// Refreshes the layout of the given tree-view control and schedules a redraw.
-static void drgui_tv_refresh_and_redraw(drgui_element* pTVElement);
-
-/// Repositions and resizes the scrollbars of the given tree-view control.
-static void drgui_tv_refresh_scrollbar_layouts(drgui_element* pTVElement);
-
-/// Refreshes the ranges and page sizes of the scrollbars of the given tree-view control.
-static void drgui_tv_refresh_scrollbar_ranges(drgui_element* pTVElement);
-
-/// Retrieves the rectangle of the little space that sits between the two scrollbars.
-static drgui_rect drgui_tv_get_scrollbar_dead_space_rect(drgui_element* pTVElement);
-
-/// Retrieves the rectangle region that does not include the scrollbars. This rectangle is used for clipping when drawing the tree-view.
-static drgui_rect drgui_tv_get_inner_rect(drgui_element* pTVElement);
-
-/// Paints the items of the given tree-view control.
-static void drgui_tv_paint_items(drgui_element* pTVElement, drgui_rect relativeClippingRect, void* pPaintData, float* pItemsBottomOut);
-
-/// Creates an iterator beginning at the given item.
-static bool drgui_tv_begin_at(drgui_tree_view_item* pFirst, drgui_tree_view_iterator* pIteratorOut);
-
-/// Moves to the next item in the iterator.
-static bool drgui_tv_next_visible(drgui_tree_view_iterator* pIterator);
-
-/// Paints the given item.
-static void drgui_tv_paint_item(drgui_element* pTVElement, drgui_tree_view_item* pItem, drgui_rect relativeClippingRect, float posX, float posY, float width, float height, void* pPaintData);
-
-/// Finds the item under the given point.
-static drgui_tree_view_item* drgui_tv_find_item_under_point(drgui_element* pTV, float relativePosX, float relativePosY, drgui_tree_view_item_metrics* pMetricsOut);
-
-/// Recursively deselects every item, including the given one.
-static void drgui_tv_deselect_all_items_recursive(drgui_tree_view_item* pItem);
-
-/// Called when the mouse enters a scrollbar. We use this to ensure there are no items marked as hovered as the use moves the
-/// mouse from the tree-view to the scrollbars.
-static void drgui_tv_on_mouse_enter_scrollbar(drgui_element* pSBElement);
-
-/// Called when the vertical scrollbar is scrolled.
-static void drgui_tv_on_scroll_v(drgui_element* pSBElement, int scrollPos);
-
-/// Called when the horizontal scrollbar is scrolled.
-static void drgui_tv_on_scroll_h(drgui_element* pSBElement, int scrollPos);
-
-/// Retrieves a pointer to the first visible item on the page, based on the scroll position.
-static drgui_tree_view_item* drgui_tv_find_first_visible_item_on_page(drgui_element* pTVElement);
-
-
-drgui_element* drgui_create_tree_view(drgui_context* pContext, drgui_element* pParent, size_t extraDataSize, const void* pExtraData)
-{
- drgui_element* pTVElement = drgui_create_element(pContext, pParent, sizeof(drgui_tree_view) + extraDataSize, NULL);
- if (pTVElement == NULL) {
- return NULL;
- }
-
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return NULL;
- }
-
- pTV->pRootItem = drgui_tv_create_item(pTVElement, NULL, 0, NULL);
- if (pTV->pRootItem == NULL) {
- return NULL;
- }
-
-
- drgui_tree_view_scrollbar_data sbdata;
- sbdata.pTVElement = pTVElement;
-
- pTV->pScrollbarV = drgui_create_scrollbar(pContext, pTVElement, drgui_sb_orientation_vertical, sizeof(sbdata), &sbdata);
- drgui_set_on_mouse_enter(pTV->pScrollbarV, drgui_tv_on_mouse_enter_scrollbar);
- drgui_sb_set_on_scroll(pTV->pScrollbarV, drgui_tv_on_scroll_v);
-
- pTV->pScrollbarH = drgui_create_scrollbar(pContext, pTVElement, drgui_sb_orientation_horizontal, sizeof(sbdata), &sbdata);
- drgui_set_on_mouse_enter(pTV->pScrollbarH, drgui_tv_on_mouse_enter_scrollbar);
- drgui_sb_set_on_scroll(pTV->pScrollbarH, drgui_tv_on_scroll_h);
-
-
- pTV->defaultBGColor = drgui_rgb(96, 96, 96);
- pTV->hoveredBGColor = drgui_rgb(112, 112, 112);
- pTV->selectedBGColor = drgui_rgb(80, 160, 255);
- pTV->childOffsetX = 16;
-
- pTV->onItemMouseMove = NULL;
- pTV->onItemMouseLeave = NULL;
- pTV->onItemPaint = NULL;
- pTV->onItemMeasure = NULL;
- pTV->onItemPicked = NULL;
-
- pTV->pHoveredItem = NULL;
- pTV->isMouseOverArrow = false;
- pTV->isMouseOver = false;
- pTV->relativeMousePosX = 0;
- pTV->relativeMousePosY = 0;
-
- pTV->isMultiSelectEnabled = false;
- pTV->isRangeSelectEnabled = false;
-
- pTV->extraDataSize = extraDataSize;
- if (pExtraData != NULL) {
- memcpy(pTV->pExtraData, pExtraData, extraDataSize);
- }
-
-
- // Default event handlers.
- drgui_set_on_size(pTVElement, drgui_tv_on_size);
- drgui_set_on_mouse_leave(pTVElement, drgui_tv_on_mouse_leave);
- drgui_set_on_mouse_move(pTVElement, drgui_tv_on_mouse_move);
- drgui_set_on_mouse_button_down(pTVElement, drgui_tv_on_mouse_button_down);
- drgui_set_on_mouse_button_up(pTVElement, drgui_tv_on_mouse_button_up);
- drgui_set_on_mouse_button_dblclick(pTVElement, drgui_tv_on_mouse_button_dblclick);
- drgui_set_on_mouse_wheel(pTVElement, drgui_tv_on_mouse_wheel);
- drgui_set_on_paint(pTVElement, drgui_tv_on_paint);
-
-
- // Set the mouse wheel scale to 3 by default for the vertical scrollbar.
- drgui_sb_set_mouse_wheel_scele(pTV->pScrollbarV, 3);
-
-
- return pTVElement;
-}
-
-void drgui_delete_tree_view(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- // Recursively delete the tree view items.
- drgui_tvi_delete(pTV->pRootItem);
-
- // Delete the element last.
- drgui_delete_element(pTVElement);
-}
-
-
-size_t drgui_tv_get_extra_data_size(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return 0;
- }
-
- return pTV->extraDataSize;
-}
-
-void* drgui_tv_get_extra_data(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return NULL;
- }
-
- return pTV->pExtraData;
-}
-
-drgui_tree_view_item* drgui_tv_get_root_item(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return NULL;
- }
-
- return pTV->pRootItem;
-}
-
-drgui_element* drgui_tv_get_vertical_scrollbar(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return NULL;
- }
-
- return pTV->pScrollbarV;
-}
-
-drgui_element* drgui_tv_get_horizontal_scrollbar(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return NULL;
- }
-
- return pTV->pScrollbarH;
-}
-
-
-void drgui_tv_set_default_background_color(drgui_element* pTVElement, drgui_color color)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- pTV->defaultBGColor = color;
-}
-
-drgui_color drgui_tv_get_default_background_color(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return drgui_rgb(0, 0, 0);
- }
-
- return pTV->defaultBGColor;
-}
-
-void drgui_tv_set_hovered_background_color(drgui_element* pTVElement, drgui_color color)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- pTV->hoveredBGColor = color;
-}
-
-drgui_color drgui_tv_get_hovered_background_color(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return drgui_rgb(0, 0, 0);
- }
-
- return pTV->hoveredBGColor;
-}
-
-void drgui_tv_set_selected_background_color(drgui_element* pTVElement, drgui_color color)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- pTV->selectedBGColor = color;
-}
-
-drgui_color drgui_tv_get_selected_background_color(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return drgui_rgb(0, 0, 0);
- }
-
- return pTV->selectedBGColor;
-}
-
-void drgui_tv_set_child_offset_x(drgui_element* pTVElement, float childOffsetX)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- pTV->childOffsetX = childOffsetX;
-}
-
-float drgui_tv_get_child_offset_x(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return 0;
- }
-
- return pTV->childOffsetX;
-}
-
-
-bool drgui_tv_measure_item(drgui_element* pTVElement, drgui_tree_view_item* pItem, float* pWidthOut, float* pHeightOut)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return false;
- }
-
- if (pItem == NULL || pItem->pTVElement != pTVElement) {
- return false;
- }
-
- if (pTV->onItemMeasure)
- {
- pTV->onItemMeasure(pItem, pWidthOut, pHeightOut);
- return true;
- }
-
- return false;
-}
-
-void drgui_tv_deselect_all_items(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- drgui_tv_deselect_all_items_recursive(pTV->pRootItem);
-
- // TODO: Only redraw the region that actually changed.
- drgui_dirty(pTVElement, drgui_get_local_rect(pTVElement));
-}
-
-
-void drgui_tv_enable_multi_select(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- pTV->isMultiSelectEnabled = true;
-}
-
-void drgui_tv_disable_multi_select(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- pTV->isMultiSelectEnabled = false;
-}
-
-bool drgui_tv_is_multi_select_enabled(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return false;
- }
-
- return pTV->isMultiSelectEnabled;
-}
-
-drgui_tree_view_item* drgui_tv_get_first_selected_item(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return NULL;
- }
-
- drgui_tree_view_iterator i;
- if (drgui_tv_begin_at(pTV->pRootItem->pFirstChild, &i))
- {
- do
- {
- if (drgui_tvi_is_selected(i.pItem)) {
- return i.pItem;
- }
-
- } while (drgui_tv_next_visible(&i));
- }
-
- return NULL;
-}
-
-drgui_tree_view_item* drgui_tv_get_next_selected_item(drgui_element* pTVElement, drgui_tree_view_item* pItem)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return NULL;
- }
-
- drgui_tree_view_iterator i;
- if (drgui_tv_begin_at(pItem, &i))
- {
- // Note that we're not including in this iteration.
- while (drgui_tv_next_visible(&i))
- {
- if (drgui_tvi_is_selected(i.pItem)) {
- return i.pItem;
- }
- }
- }
-
- return NULL;
-}
-
-
-void drgui_tv_set_on_item_mouse_move(drgui_element* pTVElement, drgui_tvi_on_mouse_move_proc proc)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- pTV->onItemMouseMove = proc;
-}
-
-void drgui_tv_set_on_item_mouse_leave(drgui_element* pTVElement, drgui_tvi_on_mouse_leave_proc proc)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- pTV->onItemMouseLeave = proc;
-}
-
-void drgui_tv_set_on_item_paint(drgui_element* pTVElement, drgui_tvi_on_paint_proc proc)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- pTV->onItemPaint = proc;
-}
-
-void drgui_tv_set_on_item_measure(drgui_element* pTVElement, drgui_tvi_measure_proc proc)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- pTV->onItemMeasure = proc;
-}
-
-void drgui_tv_set_on_item_picked(drgui_element* pTVElement, drgui_tvi_on_picked_proc proc)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- pTV->onItemPicked = proc;
-}
-
-
-void drgui_tv_on_size(drgui_element* pTVElement, float newWidth, float newHeight)
-{
- (void)newWidth;
- (void)newHeight;
-
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- // Move the scrollbars.
- drgui_tv_refresh_scrollbar_layouts(pTVElement);
-
- // Refresh the scrollbar ranges.
- drgui_tv_refresh_scrollbar_ranges(pTVElement);
-}
-
-void drgui_tv_on_mouse_leave(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- pTV->isMouseOver = false;
-
- if (pTV->pHoveredItem != NULL || pTV->isMouseOverArrow)
- {
- if (pTV->onItemMouseLeave) {
- pTV->onItemMouseLeave(pTV->pHoveredItem);
- }
-
- pTV->pHoveredItem = NULL;
- pTV->isMouseOverArrow = false;
-
- // For now just redraw the entire control, but should optimize this to only redraw the regions of the new and old hovered items.
- drgui_dirty(pTVElement, drgui_get_local_rect(pTVElement));
- }
-}
-
-void drgui_tv_on_mouse_move(drgui_element* pTVElement, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)stateFlags;
-
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- pTV->isMouseOver = true;
- pTV->relativeMousePosX = relativeMousePosX;
- pTV->relativeMousePosY = relativeMousePosY;
-
- // If the mouse has entered into the dead space between the scrollbars, we just pretend the mouse has left the tree-view
- // control entirely by posting a manual on_mouse_leave event and returning straight away.
- if (drgui_rect_contains_point(drgui_tv_get_scrollbar_dead_space_rect(pTVElement), (float)relativeMousePosX, (float)relativeMousePosY)) {
- drgui_tv_on_mouse_leave(pTVElement);
- return;
- }
-
-
- drgui_tree_view_item_metrics newHoveredItemMetrics;
- drgui_tree_view_item* pNewHoveredItem = drgui_tv_find_item_under_point(pTVElement, (float)relativeMousePosX, (float)relativeMousePosY, &newHoveredItemMetrics);
- drgui_tree_view_item* pOldHoveredItem = pTV->pHoveredItem;
-
- bool wasMouseOverArrow = pTV->isMouseOverArrow;
- pTV->isMouseOverArrow = false;
-
- if (pNewHoveredItem != NULL)
- {
- if (pTV->onItemMouseMove)
- {
- float relativeMousePosXToItem = (float)relativeMousePosX - newHoveredItemMetrics.posX + drgui_sb_get_scroll_position(pTV->pScrollbarH);
- float relativeMousePosYToItem = (float)relativeMousePosY - newHoveredItemMetrics.posY;
-
- if (relativeMousePosXToItem >= 0 && relativeMousePosXToItem < newHoveredItemMetrics.width &&
- relativeMousePosYToItem >= 0 && relativeMousePosYToItem < newHoveredItemMetrics.height)
- {
- pTV->onItemMouseMove(pNewHoveredItem, (int)relativeMousePosXToItem, (int)relativeMousePosYToItem, &pTV->isMouseOverArrow);
- }
- }
- }
-
- if (pNewHoveredItem != pOldHoveredItem || wasMouseOverArrow != pTV->isMouseOverArrow)
- {
- if (pNewHoveredItem != pOldHoveredItem && pOldHoveredItem != NULL)
- {
- if (pTV->onItemMouseLeave) {
- pTV->onItemMouseLeave(pOldHoveredItem);
- }
- }
-
-
- pTV->pHoveredItem = pNewHoveredItem;
-
- // TODO: Optimize this so that only the rectangle region encompassing the two relevant items is marked as dirty.
- drgui_dirty(pTVElement, drgui_get_local_rect(pTVElement));
- }
-}
-
-void drgui_tv_on_mouse_button_down(drgui_element* pTVElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)relativeMousePosX;
- (void)relativeMousePosY;
- (void)stateFlags;
-
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- if (mouseButton == DRGUI_MOUSE_BUTTON_LEFT)
- {
- if (pTV->isMouseOverArrow)
- {
- if (drgui_tvi_is_expanded(pTV->pHoveredItem)) {
- drgui_tvi_collapse(pTV->pHoveredItem);
- } else {
- drgui_tvi_expand(pTV->pHoveredItem);
- }
- }
- else
- {
- if (pTV->isMultiSelectEnabled)
- {
- if (drgui_tvi_is_selected(pTV->pHoveredItem)) {
- drgui_tvi_deselect(pTV->pHoveredItem);
- } else {
- drgui_tvi_select(pTV->pHoveredItem);
- }
- }
- else
- {
- // TODO: Check if range selection is enabled and handle it here.
-
- drgui_tv_deselect_all_items(pTVElement);
- drgui_tvi_select(pTV->pHoveredItem);
- }
- }
- }
-}
-
-void drgui_tv_on_mouse_button_up(drgui_element* pTVElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)mouseButton;
- (void)relativeMousePosX;
- (void)relativeMousePosY;
- (void)stateFlags;
-
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- // TODO: Implement me.
-}
-
-void drgui_tv_on_mouse_button_dblclick(drgui_element* pTVElement, int mouseButton, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)relativeMousePosX;
- (void)relativeMousePosY;
- (void)stateFlags;
-
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- if (mouseButton == DRGUI_MOUSE_BUTTON_LEFT)
- {
- if (!pTV->isMouseOverArrow)
- {
- if (drgui_tvi_has_children(pTV->pHoveredItem))
- {
- // It is a parent item, so toggle it.
- if (drgui_tvi_is_expanded(pTV->pHoveredItem)) {
- drgui_tvi_collapse(pTV->pHoveredItem);
- } else {
- drgui_tvi_expand(pTV->pHoveredItem);
- }
- }
- else
- {
- // It is a leaf item, so pick it.
- if (pTV->onItemPicked) {
- pTV->onItemPicked(pTV->pHoveredItem);
- }
- }
- }
- }
-}
-
-void drgui_tv_on_mouse_wheel(drgui_element* pTVElement, int delta, int relativeMousePosX, int relativeMousePosY, int stateFlags)
-{
- (void)relativeMousePosX;
- (void)relativeMousePosY;
- (void)stateFlags;
-
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- drgui_sb_scroll(pTV->pScrollbarV, -delta * drgui_sb_get_mouse_wheel_scale(pTV->pScrollbarV));
-}
-
-void drgui_tv_on_paint(drgui_element* pTVElement, drgui_rect relativeClippingRect, void* pPaintData)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- // The dead space between the scrollbars should always be drawn with the default background color.
- drgui_draw_rect(pTVElement, drgui_tv_get_scrollbar_dead_space_rect(pTVElement), pTV->defaultBGColor, pPaintData);
-
- // The clipping rectangle needs to be clamped to the local rectangle that is shrunk such that it does not
- // include the scrollbars. If we don't do this we'll end up drawing underneath the scrollbars which will
- // cause flickering.
- drgui_rect innerClippingRect = drgui_clamp_rect(drgui_tv_get_inner_rect(pTVElement), relativeClippingRect);
- drgui_set_clip(pTVElement, innerClippingRect, pPaintData);
-
-
- // The main content of the tree-view is drawn in two parts. The first part (the top part) contains all of
- // the tree-view items. The second part (the bottom part) is just the background region that is not covered
- // by items.
-
- // We draw the tree-view items (the top part) first. This will retrieve the position of the bottom of the
- // items which is used to determine how much empty space is remaining below it so we can draw a quad over
- // that part.
- float itemsBottom = 0;
- drgui_tv_paint_items(pTVElement, innerClippingRect, pPaintData, &itemsBottom);
-
-
- // At this point the items have been drawn. All that remains is the part of the background that is not
- // covered by items. We can determine this by looking at .
- if (itemsBottom < relativeClippingRect.bottom && itemsBottom < drgui_get_relative_position_y(pTV->pScrollbarH))
- {
- drgui_draw_rect(pTVElement, drgui_make_rect(0, itemsBottom, drgui_get_relative_position_x(pTV->pScrollbarV), drgui_get_relative_position_y(pTV->pScrollbarH)), pTV->defaultBGColor, pPaintData);
- }
-}
-
-
-static void drgui_tv_refresh_and_redraw(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
-
- // Refresh scrollbar ranges and page sizes.
- drgui_tv_refresh_scrollbar_ranges(pTVElement);
-
- // For now, just redraw the entire control.
- drgui_dirty(pTVElement, drgui_get_local_rect(pTVElement));
-}
-
-static void drgui_tv_refresh_scrollbar_layouts(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
-
- // Vertical scrollbar.
- drgui_set_size(pTV->pScrollbarV, 16, drgui_get_height(pTVElement) - 16);
- drgui_set_relative_position(pTV->pScrollbarV, drgui_get_width(pTVElement) - drgui_get_width(pTV->pScrollbarV), 0);
-
- // Horizontal scrollbar.
- drgui_set_size(pTV->pScrollbarH, drgui_get_width(pTVElement) - 16, 16);
- drgui_set_relative_position(pTV->pScrollbarH, 0, drgui_get_height(pTVElement) - drgui_get_height(pTV->pScrollbarH));
-}
-
-static void drgui_tv_refresh_scrollbar_ranges(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- float innerWidth = 0;
- unsigned int totalItemCount = 0;
- unsigned int pageItemCount = 0;
-
- drgui_tree_view_iterator i;
- if (drgui_tv_begin_at(pTV->pRootItem->pFirstChild, &i))
- {
- do
- {
- float itemRight = i.posX + i.width;
- if (itemRight > innerWidth) {
- innerWidth = itemRight;
- }
-
- float itemBottom = i.posY + i.height;
- if (itemBottom > 0 && itemBottom < drgui_get_relative_position_y(pTV->pScrollbarH)) {
- pageItemCount += 1;
- }
-
- totalItemCount += 1;
-
- } while (drgui_tv_next_visible(&i));
- }
-
- if (totalItemCount == 0)
- {
- // Vertical.
- drgui_sb_set_range(pTV->pScrollbarV, 0, 0);
- drgui_sb_set_page_size(pTV->pScrollbarV, 0);
-
- // Horizontal.
- drgui_sb_set_range(pTV->pScrollbarH, 0, 0);
- drgui_sb_set_page_size(pTV->pScrollbarH, 0);
- }
- else
- {
- // Vertical.
- drgui_sb_set_range(pTV->pScrollbarV, 0, (int)totalItemCount - 1); // - 1 because it's a 0-based range.
- drgui_sb_set_page_size(pTV->pScrollbarV, pageItemCount);
-
- // Horizontal.
- drgui_sb_set_range(pTV->pScrollbarH, 0, (int)innerWidth);
- drgui_sb_set_page_size(pTV->pScrollbarH, (int)drgui_get_relative_position_x(pTV->pScrollbarV));
- }
-}
-
-static drgui_rect drgui_tv_get_scrollbar_dead_space_rect(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return drgui_make_rect(0, 0, 0, 0);
- }
-
- return drgui_make_rect(drgui_get_width(pTV->pScrollbarH), drgui_get_height(pTV->pScrollbarV), drgui_get_width(pTVElement), drgui_get_height(pTVElement));
-}
-
-static drgui_rect drgui_tv_get_inner_rect(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return drgui_make_rect(0, 0, 0, 0);
- }
-
- drgui_rect result = drgui_get_local_rect(pTVElement);
- result.right -= drgui_get_width(pTV->pScrollbarV);
- result.bottom -= drgui_get_height(pTV->pScrollbarH);
-
- return result;
-}
-
-static void drgui_tv_paint_items(drgui_element* pTVElement, drgui_rect relativeClippingRect, void* pPaintData, float* pItemsBottomOut)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- float itemsBottom = 0;
-
- // For now we will begin at the root item, but later we want to begin at the first visible item which will be based on the
- // scroll position.
- drgui_tree_view_iterator i;
- if (drgui_tv_begin_at(drgui_tv_find_first_visible_item_on_page(pTVElement), &i))
- {
- do
- {
- drgui_tv_paint_item(pTVElement, i.pItem, relativeClippingRect, i.posX, i.posY, i.width, i.height, pPaintData);
-
- // Restore the clipping rectangle in case the application changed the clipping rectangle.
- drgui_set_clip(pTVElement, relativeClippingRect, pPaintData);
-
- itemsBottom = i.posY + i.height;
-
- } while (itemsBottom < relativeClippingRect.bottom && drgui_tv_next_visible(&i));
- }
-
-
- if (pItemsBottomOut != NULL) {
- *pItemsBottomOut = itemsBottom;
- }
-}
-
-static bool drgui_tv_begin_at(drgui_tree_view_item* pFirst, drgui_tree_view_iterator* pIteratorOut)
-{
- if (pFirst == NULL || pIteratorOut == NULL) {
- return false;
- }
-
- if (!drgui_tv_measure_item(pFirst->pTVElement, pFirst, &pIteratorOut->width, &pIteratorOut->height)) {
- return false;
- }
-
- const int depth = drgui_tvi_get_depth(pFirst);
-
- pIteratorOut->pItem = pFirst;
- pIteratorOut->depth = depth;
- pIteratorOut->posX = depth * drgui_tv_get_child_offset_x(pFirst->pTVElement);
- pIteratorOut->posY = 0;
-
- return true;
-}
-
-static bool drgui_tv_next_visible(drgui_tree_view_iterator* pIterator)
-{
- assert(pIterator != NULL);
-
- if (pIterator->pItem == NULL) {
- return false;
- }
-
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pIterator->pItem->pTVElement);
- if (pTV == NULL) {
- return false;
- }
-
- if (drgui_tvi_has_children(pIterator->pItem) && drgui_tvi_is_expanded(pIterator->pItem))
- {
- pIterator->pItem = pIterator->pItem->pFirstChild;
- pIterator->depth += 1;
- }
- else
- {
- pIterator->pItem = drgui_tvi_next_visible_non_child(pIterator->pItem, &pIterator->depth);
- }
-
-
- if (pIterator->pItem == NULL) {
- return false;
- }
-
- pIterator->posX = pIterator->depth * drgui_tv_get_child_offset_x(pIterator->pItem->pTVElement);
- pIterator->posY += pIterator->height;
-
- if (!drgui_tv_measure_item(pIterator->pItem->pTVElement, pIterator->pItem, &pIterator->width, &pIterator->height)) {
- return false;
- }
-
- return true;
-}
-
-static void drgui_tv_paint_item(drgui_element* pTVElement, drgui_tree_view_item* pItem, drgui_rect relativeClippingRect, float posX, float posY, float width, float height, void* pPaintData)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- if (pTV->onItemPaint)
- {
- // We draw an item in two main parts, with the first part being the background section to the left and right of the item and the
- // second part being the item itself. The first part we do ourselves, whereas the second part we pass off to the host application.
-
- // The background section to the left and right of the main content is done first, by us.
- drgui_color bgcolor;
- if (drgui_tvi_is_selected(pItem)) {
- bgcolor = pTV->selectedBGColor;
- } else if (pTV->pHoveredItem == pItem) {
- bgcolor = pTV->hoveredBGColor;
- } else {
- bgcolor = pTV->defaultBGColor;
- }
-
- float innerOffsetX = (float)-drgui_sb_get_scroll_position(pTV->pScrollbarH);
-
- // Left.
- if (posX + innerOffsetX > 0) {
- drgui_draw_rect(pTVElement, drgui_make_rect(0, posY, posX + innerOffsetX, posY + height), bgcolor, pPaintData);
- }
-
- // Right.
- if (posX + width + innerOffsetX < drgui_get_relative_position_x(pTV->pScrollbarV)) {
- drgui_draw_rect(pTVElement, drgui_make_rect(posX + width + innerOffsetX, posY, drgui_get_relative_position_x(pTV->pScrollbarV), posY + height), bgcolor, pPaintData);
- }
-
-
- // At this point if were to finish drawing we'd have a hole where the main content of the item should be. To fill this we need to
- // let the host application do it.
- pTV->onItemPaint(pTVElement, pItem, relativeClippingRect, bgcolor, posX + innerOffsetX, posY, width, height, pPaintData);
- }
-}
-
-static drgui_tree_view_item* drgui_tv_find_item_under_point(drgui_element* pTVElement, float relativePosX, float relativePosY, drgui_tree_view_item_metrics* pMetricsOut)
-{
- (void)relativePosX; // <-- Unused because we treat items as though they are infinitely wide.
-
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return NULL;
- }
-
- // For now we will begin at the root item, but later we want to begin at the first visible item which will be based on the
- // scroll position.
- drgui_tree_view_iterator i;
- if (drgui_tv_begin_at(drgui_tv_find_first_visible_item_on_page(pTVElement), &i))
- {
- do
- {
- if (relativePosY >= i.posY && relativePosY < i.posY + i.height)
- {
- if (pMetricsOut != NULL)
- {
- pMetricsOut->posX = i.posX;
- pMetricsOut->posY = i.posY;
- pMetricsOut->width = i.width;
- pMetricsOut->height = i.height;
- }
-
- return i.pItem;
- }
-
- } while ((i.posY + i.height < drgui_get_relative_position_y(pTV->pScrollbarH)) && drgui_tv_next_visible(&i));
- }
-
- return NULL;
-}
-
-static void drgui_tv_deselect_all_items_recursive(drgui_tree_view_item* pItem)
-{
- pItem->isSelected = false;
-
- for (drgui_tree_view_item* pChild = pItem->pFirstChild; pChild != NULL; pChild = pChild->pNextSibling)
- {
- drgui_tv_deselect_all_items_recursive(pChild);
- }
-}
-
-static void drgui_tv_on_mouse_enter_scrollbar(drgui_element* pSBElement)
-{
- drgui_tree_view_scrollbar_data* pSB = (drgui_tree_view_scrollbar_data*)drgui_sb_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- // We just pretend the mouse has left the tree-view entirely. This will ensure any item marked as hovered is unmarked and redrawn.
- drgui_tv_on_mouse_leave(pSB->pTVElement);
-}
-
-static void drgui_tv_on_scroll_v(drgui_element* pSBElement, int scrollPos)
-{
- (void)scrollPos;
-
- drgui_tree_view_scrollbar_data* pSB = (drgui_tree_view_scrollbar_data*)drgui_sb_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pSB->pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- // As we scroll, the mouse will be placed over a different item. We just post a manual mouse_move event to trigger a refresh.
- if (pTV->isMouseOver) {
- drgui_tv_on_mouse_move(pSB->pTVElement, pTV->relativeMousePosX, pTV->relativeMousePosY, 0);
- }
-
- // The paint routine is tied directly to the scrollbars, so all we need to do is mark it as dirty to trigger a redraw.
- drgui_dirty(pSB->pTVElement, drgui_get_local_rect(pSB->pTVElement));
-}
-
-static void drgui_tv_on_scroll_h(drgui_element* pSBElement, int scrollPos)
-{
- (void)scrollPos;
-
- drgui_tree_view_scrollbar_data* pSB = (drgui_tree_view_scrollbar_data*)drgui_sb_get_extra_data(pSBElement);
- if (pSB == NULL) {
- return;
- }
-
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pSB->pTVElement);
- if (pTV == NULL) {
- return;
- }
-
- // The paint routine is tied directly to the scrollbars, so all we need to do is mark it as dirty to trigger a redraw.
- drgui_dirty(pSB->pTVElement, drgui_get_local_rect(pSB->pTVElement));
-}
-
-static drgui_tree_view_item* drgui_tv_find_first_visible_item_on_page(drgui_element* pTVElement)
-{
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pTVElement);
- if (pTV == NULL) {
- return NULL;
- }
-
- // We just keep iterating until we hit the index of the scroll position.
- int index = 0;
-
- drgui_tree_view_iterator i;
- if (drgui_tv_begin_at(pTV->pRootItem->pFirstChild, &i))
- {
- do
- {
- if (index == drgui_sb_get_scroll_position(pTV->pScrollbarV)) {
- return i.pItem;
- }
-
- index += 1;
-
- } while (drgui_tv_next_visible(&i));
- }
-
- return NULL;
-}
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// Tree-View Item
-//
-///////////////////////////////////////////////////////////////////////////////
-
-/// Detaches the given tree-view item from it's parent and siblings.
-static void drgui_tvi_detach(drgui_tree_view_item* pItem);
-
-drgui_tree_view_item* drgui_tv_create_item(drgui_element* pTVElement, drgui_tree_view_item* pParent, size_t extraDataSize, const void* pExtraData)
-{
- if (pTVElement == NULL) {
- return NULL;
- }
-
- if (pParent != NULL && pParent->pTVElement != pTVElement) {
- return NULL;
- }
-
-
- drgui_tree_view_item* pItem = (drgui_tree_view_item*)malloc(sizeof(*pItem) + extraDataSize);
- if (pItem == NULL) {
- return NULL;
- }
-
- pItem->pTVElement = pTVElement;
- pItem->pParent = NULL;
- pItem->pFirstChild = NULL;
- pItem->pLastChild = NULL;
- pItem->pNextSibling = NULL;
- pItem->pPrevSibling = NULL;
- pItem->isSelected = false;
- pItem->isExpanded = false;
-
- pItem->extraDataSize = extraDataSize;
- if (pExtraData != NULL) {
- memcpy(pItem->pExtraData, pExtraData, extraDataSize);
- }
-
-
- // Append the item to the end of the parent item.
- drgui_tvi_append(pItem, pParent);
-
- return pItem;
-}
-
-void drgui_tvi_delete(drgui_tree_view_item* pItem)
-{
- if (pItem == NULL) {
- return;
- }
-
-
- // Children need to be deleted first.
- while (pItem->pFirstChild != NULL) {
- drgui_tvi_delete(pItem->pFirstChild);
- }
-
- // We need to grab a pointer to the main tree-view control so we can refresh and redraw it after we have detached the item.
- drgui_element* pTVElement = pItem->pTVElement;
-
- // The item needs to be completely detached first.
- drgui_tvi_detach(pItem);
-
- // Refresh the layout and redraw the tree-view control.
- drgui_tv_refresh_and_redraw(pTVElement);
-
- // Free the item last for safety.
- free(pItem);
-}
-
-drgui_element* drgui_tvi_get_tree_view_element(drgui_tree_view_item* pItem)
-{
- if (pItem == NULL) {
- return NULL;
- }
-
- return pItem->pTVElement;
-}
-
-size_t drgui_tvi_get_extra_data_size(drgui_tree_view_item* pItem)
-{
- if (pItem == NULL) {
- return 0;
- }
-
- return pItem->extraDataSize;
-}
-
-void* drgui_tvi_get_extra_data(drgui_tree_view_item* pItem)
-{
- if (pItem == NULL) {
- return NULL;
- }
-
- return pItem->pExtraData;
-}
-
-
-drgui_tree_view_item* drgui_tvi_get_parent(drgui_tree_view_item* pItem)
-{
- if (pItem == NULL) {
- return NULL;
- }
-
- return pItem->pParent;
-}
-
-drgui_tree_view_item* drgui_tvi_get_first_child(drgui_tree_view_item* pItem)
-{
- if (pItem == NULL) {
- return NULL;
- }
-
- return pItem->pFirstChild;
-}
-
-drgui_tree_view_item* drgui_tvi_get_last_child(drgui_tree_view_item* pItem)
-{
- if (pItem == NULL) {
- return NULL;
- }
-
- return pItem->pLastChild;
-}
-
-drgui_tree_view_item* drgui_tvi_get_next_sibling(drgui_tree_view_item* pItem)
-{
- if (pItem == NULL) {
- return NULL;
- }
-
- return pItem->pNextSibling;
-}
-
-drgui_tree_view_item* drgui_tvi_get_prev_sibling(drgui_tree_view_item* pItem)
-{
- if (pItem == NULL) {
- return NULL;
- }
-
- return pItem->pPrevSibling;
-}
-
-void drgui_tvi_append(drgui_tree_view_item* pItem, drgui_tree_view_item* pParent)
-{
- if (pItem == NULL) {
- return;
- }
-
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pItem->pTVElement);
- if (pTV == NULL) {
- return;
- }
-
-
- // If a parent was not specified, append to the root item.
- if (pParent == NULL)
- {
- if (pTV->pRootItem != NULL) {
- drgui_tvi_append(pItem, pTV->pRootItem);
- }
- }
- else
- {
- assert(pItem->pTVElement == pParent->pTVElement);
-
- // Detach the child from it's current parent first.
- drgui_tvi_detach(pItem);
-
- pItem->pParent = pParent;
- assert(pItem->pParent != NULL);
-
- if (pItem->pParent->pLastChild != NULL) {
- pItem->pPrevSibling = pItem->pParent->pLastChild;
- pItem->pPrevSibling->pNextSibling = pItem;
- }
-
- if (pItem->pParent->pFirstChild == NULL) {
- pItem->pParent->pFirstChild = pItem;
- }
-
- pItem->pParent->pLastChild = pItem;
-
-
- // Refresh the layout and redraw the tree-view control.
- drgui_tv_refresh_and_redraw(pItem->pTVElement);
- }
-}
-
-void drgui_tvi_prepend(drgui_tree_view_item* pItem, drgui_tree_view_item* pParent)
-{
- if (pItem == NULL) {
- return;
- }
-
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pItem->pTVElement);
- if (pTV == NULL) {
- return;
- }
-
-
- // If a parent was not specified, prepend to the root item.
- if (pParent == NULL)
- {
- if (pTV->pRootItem != NULL) {
- drgui_tvi_prepend(pItem, pTV->pRootItem);
- }
- }
- else
- {
- assert(pItem->pTVElement == pParent->pTVElement);
-
- // Detach the child from it's current parent first.
- drgui_tvi_detach(pItem);
-
- pItem->pParent = pParent;
- assert(pItem->pParent != NULL);
-
- if (pItem->pParent->pFirstChild != NULL) {
- pItem->pNextSibling = pItem->pParent->pFirstChild;
- pItem->pNextSibling->pPrevSibling = pItem;
- }
-
- if (pItem->pParent->pLastChild == NULL) {
- pItem->pParent->pLastChild = pItem;
- }
-
- pItem->pParent->pFirstChild = pItem;
-
-
- // Refresh the layout and redraw the tree-view control.
- drgui_tv_refresh_and_redraw(pItem->pTVElement);
- }
-}
-
-void drgui_tvi_append_sibling(drgui_tree_view_item* pItemToAppend, drgui_tree_view_item* pItemToAppendTo)
-{
- if (pItemToAppend == NULL) {
- return;
- }
-
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pItemToAppend->pTVElement);
- if (pTV == NULL) {
- return;
- }
-
-
- // If a parent was not specified, append to the root item.
- if (pItemToAppendTo == NULL)
- {
- if (pTV->pRootItem != NULL) {
- drgui_tvi_append(pItemToAppend, pTV->pRootItem);
- }
- }
- else
- {
- assert(pItemToAppend->pTVElement == pItemToAppendTo->pTVElement);
-
- // Detach the child from it's current parent first.
- drgui_tvi_detach(pItemToAppend);
-
-
- pItemToAppend->pParent = pItemToAppendTo->pParent;
- assert(pItemToAppend->pParent != NULL);
-
- pItemToAppend->pNextSibling = pItemToAppendTo->pNextSibling;
- pItemToAppend->pPrevSibling = pItemToAppendTo;
-
- pItemToAppendTo->pNextSibling->pPrevSibling = pItemToAppend;
- pItemToAppendTo->pNextSibling = pItemToAppend;
-
- if (pItemToAppend->pParent->pLastChild == pItemToAppendTo) {
- pItemToAppend->pParent->pLastChild = pItemToAppend;
- }
-
-
- // Refresh the layout and redraw the tree-view control.
- drgui_tv_refresh_and_redraw(pItemToAppend->pTVElement);
- }
-}
-
-void drgui_tvi_prepend_sibling(drgui_tree_view_item* pItemToPrepend, drgui_tree_view_item* pItemToPrependTo)
-{
- if (pItemToPrepend == NULL) {
- return;
- }
-
- drgui_tree_view* pTV = (drgui_tree_view*)drgui_get_extra_data(pItemToPrepend->pTVElement);
- if (pTV == NULL) {
- return;
- }
-
-
- // If a parent was not specified, prepend to the root item.
- if (pItemToPrependTo == NULL)
- {
- if (pTV->pRootItem != NULL) {
- drgui_tvi_prepend(pItemToPrepend, pTV->pRootItem);
- }
- }
- else
- {
- assert(pItemToPrepend->pTVElement == pItemToPrependTo->pTVElement);
-
- // Detach the child from it's current parent first.
- drgui_tvi_detach(pItemToPrepend);
-
-
- pItemToPrepend->pParent = pItemToPrependTo->pParent;
- assert(pItemToPrepend->pParent != NULL);
-
- pItemToPrepend->pPrevSibling = pItemToPrependTo->pNextSibling;
- pItemToPrepend->pNextSibling = pItemToPrependTo;
-
- pItemToPrependTo->pPrevSibling->pNextSibling = pItemToPrepend;
- pItemToPrependTo->pNextSibling = pItemToPrepend;
-
- if (pItemToPrepend->pParent->pFirstChild == pItemToPrependTo) {
- pItemToPrepend->pParent->pFirstChild = pItemToPrepend;
- }
-
-
- // Refresh the layout and redraw the tree-view control.
- drgui_tv_refresh_and_redraw(pItemToPrepend->pTVElement);
- }
-}
-
-
-bool drgui_tvi_has_children(drgui_tree_view_item* pItem)
-{
- if (pItem == NULL) {
- return false;
- }
-
- return pItem->pFirstChild != NULL;
-}
-
-int drgui_tvi_get_depth(drgui_tree_view_item* pItem)
-{
- if (pItem->pParent == NULL || pItem->pParent == drgui_tv_get_root_item(pItem->pTVElement)) {
- return 0;
- }
-
- return drgui_tvi_get_depth(pItem->pParent) + 1;
-}
-
-drgui_tree_view_item* drgui_tvi_next_visible_non_child(drgui_tree_view_item* pItem, int* pDepthInOut)
-{
- if (pItem == NULL) {
- return NULL;
- }
-
- if (pItem->pNextSibling != NULL) {
- return pItem->pNextSibling;
- }
-
-
- if (pDepthInOut != NULL) {
- *pDepthInOut -= 1;
- }
-
- return drgui_tvi_next_visible_non_child(pItem->pParent, pDepthInOut);
-}
-
-
-void drgui_tvi_select(drgui_tree_view_item* pItem)
-{
- if (pItem == NULL) {
- return;
- }
-
- if (!pItem->isSelected)
- {
- pItem->isSelected = true;
- drgui_dirty(pItem->pTVElement, drgui_get_local_rect(pItem->pTVElement));
- }
-}
-
-void drgui_tvi_deselect(drgui_tree_view_item* pItem)
-{
- if (pItem == NULL) {
- return;
- }
-
- if (pItem->isSelected)
- {
- pItem->isSelected = false;
- drgui_dirty(pItem->pTVElement, drgui_get_local_rect(pItem->pTVElement));
- }
-}
-
-bool drgui_tvi_is_selected(drgui_tree_view_item* pItem)
-{
- if (pItem == NULL) {
- return false;
- }
-
- return pItem->isSelected;
-}
-
-void drgui_tvi_expand(drgui_tree_view_item* pItem)
-{
- if (pItem == NULL) {
- return;
- }
-
- if (!pItem->isExpanded)
- {
- pItem->isExpanded = true;
- drgui_tv_refresh_and_redraw(pItem->pTVElement);
- }
-}
-
-void drgui_tvi_collapse(drgui_tree_view_item* pItem)
-{
- if (pItem == NULL) {
- return;
- }
-
- if (pItem->isExpanded)
- {
- pItem->isExpanded = false;
- drgui_tv_refresh_and_redraw(pItem->pTVElement);
- }
-}
-
-bool drgui_tvi_is_expanded(drgui_tree_view_item* pItem)
-{
- if (pItem == NULL) {
- return false;
- }
-
- return pItem->isExpanded;
-}
-
-
-
-static void drgui_tvi_detach(drgui_tree_view_item* pItem)
-{
- assert(pItem != NULL);
-
- if (pItem->pParent != NULL)
- {
- if (pItem->pParent->pFirstChild == pItem) {
- pItem->pParent->pFirstChild = pItem->pNextSibling;
- }
-
- if (pItem->pParent->pLastChild == pItem) {
- pItem->pParent->pLastChild = pItem->pPrevSibling;
- }
-
-
- if (pItem->pPrevSibling != NULL) {
- pItem->pPrevSibling->pNextSibling = pItem->pNextSibling;
- }
-
- if (pItem->pNextSibling != NULL) {
- pItem->pNextSibling->pPrevSibling = pItem->pPrevSibling;
- }
- }
-
- pItem->pParent = NULL;
- pItem->pPrevSibling = NULL;
- pItem->pNextSibling = NULL;
-}
-#endif //DR_GUI_IMPLEMENTATION
-
-
-#ifdef DR_GUI_INCLUDE_WIP
-// #include WIP files here.
-#endif //DR_GUI_INCLUDE_WIP
-
-/*
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-For more information, please refer to
-*/
diff --git a/source/engine/thirdparty/dr_libs/old/dr_math.h b/source/engine/thirdparty/dr_libs/old/dr_math.h
deleted file mode 100644
index 0a74c05..0000000
--- a/source/engine/thirdparty/dr_libs/old/dr_math.h
+++ /dev/null
@@ -1,676 +0,0 @@
-// Public Domain. See "unlicense" statement at the end of this file.
-
-// NOTE: This is still very much work in progress and is only being updated as I need it. You don't want to be using this library
-// in its current state.
-
-// QUICK NOTES
-// - This library does not use SSE for its basic types (vec4, etc.). Rationale: 1) It keeps things simple; 2) SSE is not always
-// faster than the FPU(s) on modern CPUs; 3) The library can always implement functions that work on __m128 variables directly
-// in the future if the need arises; 4) It doesn't work well with the pass-by-value API this library uses.
-// - Use DISABLE_SSE to disable SSE optimized functions.
-// - Angles are always specified in radians, unless otherwise noted. Rationale: Consistency with the standard library and most
-// other math libraries.
-// - Use radians() and degrees() to convert between the two.
-
-#ifndef dr_math_h
-#define dr_math_h
-
-#include
-
-#if defined(_MSC_VER)
-#define DR_MATHCALL static __forceinline
-#else
-#define DR_MATHCALL static inline
-#endif
-
-#define DR_PI 3.14159265358979323846
-#define DR_PIF 3.14159265358979323846f
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct
-{
- float x;
- float y;
- float z;
- float w;
-} vec4;
-
-typedef struct
-{
- float x;
- float y;
- float z;
-} vec3;
-
-typedef struct
-{
- float x;
- float y;
-} vec2;
-
-typedef struct
-{
- vec4 col[4];
-} mat4;
-
-typedef struct
-{
- float x;
- float y;
- float z;
- float w;
-} quat;
-
-
-// Radians to degrees.
-DR_MATHCALL float dr_degrees(float radians)
-{
- return radians * 57.29577951308232087685f;
-}
-
-// Degrees to radians.
-DR_MATHCALL float dr_radians(float degrees)
-{
- return degrees * 0.01745329251994329577f;
-}
-
-
-
-///////////////////////////////////////////////
-//
-// VEC4
-//
-///////////////////////////////////////////////
-
-DR_MATHCALL vec4 vec4f(float x, float y, float z, float w)
-{
- vec4 result;
- result.x = x;
- result.y = y;
- result.z = z;
- result.w = w;
-
- return result;
-}
-DR_MATHCALL vec4 vec4v(const float* v)
-{
- return vec4f(v[0], v[1], v[2], v[3]);
-}
-DR_MATHCALL vec4 vec4_zero()
-{
- return vec4f(0, 0, 0, 0);
-}
-DR_MATHCALL vec4 vec4_one()
-{
- return vec4f(1, 1, 1, 1);
-}
-
-
-
-DR_MATHCALL vec4 vec4_add(vec4 a, vec4 b)
-{
- return vec4f(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
-}
-
-DR_MATHCALL vec4 vec4_sub(vec4 a, vec4 b)
-{
- return vec4f(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
-}
-
-
-DR_MATHCALL vec4 vec4_mul(vec4 a, vec4 b)
-{
- return vec4f(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
-}
-DR_MATHCALL vec4 vec4_mul_1f(vec4 a, float x)
-{
- return vec4f(a.x * x, a.y * x, a.z * x, a.w * x);
-}
-DR_MATHCALL vec4 vec4_mul_mat4(vec4 v, mat4 m)
-{
- const vec4 m0 = m.col[0];
- const vec4 m1 = m.col[1];
- const vec4 m2 = m.col[2];
- const vec4 m3 = m.col[3];
-
- return vec4f(
- m0.x*v.x + m0.y*v.y + m0.z*v.z + m0.w*v.w,
- m1.x*v.x + m1.y*v.y + m1.z*v.z + m1.w*v.w,
- m2.x*v.x + m2.y*v.y + m2.z*v.z + m2.w*v.w,
- m3.x*v.x + m3.y*v.y + m3.z*v.z + m3.w*v.w
- );
-}
-
-
-DR_MATHCALL vec4 vec4_div(vec4 a, vec4 b)
-{
- return vec4f(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
-}
-
-
-
-
-
-
-///////////////////////////////////////////////
-//
-// VEC3
-//
-///////////////////////////////////////////////
-
-DR_MATHCALL vec3 vec3f(float x, float y, float z)
-{
- vec3 result;
- result.x = x;
- result.y = y;
- result.z = z;
-
- return result;
-}
-DR_MATHCALL vec3 vec3v(const float* v)
-{
- return vec3f(v[0], v[1], v[2]);
-}
-DR_MATHCALL vec3 vec3_zero()
-{
- return vec3f(0, 0, 0);
-}
-DR_MATHCALL vec3 vec3_one()
-{
- return vec3f(1, 1, 1);
-}
-
-
-DR_MATHCALL vec3 vec3_add(vec3 a, vec3 b)
-{
- return vec3f(a.x + b.x, a.y + b.y, a.z + b.z);
-}
-
-DR_MATHCALL vec3 vec3_sub(vec3 a, vec3 b)
-{
- return vec3f(a.x - b.x, a.y - b.y, a.z - b.z);
-}
-
-
-DR_MATHCALL vec3 vec3_mul(vec3 a, vec3 b)
-{
- return vec3f(a.x * b.x, a.y * b.y, a.z * b.z);
-}
-DR_MATHCALL vec3 vec3_mul_1f(vec3 a, float x)
-{
- return vec3f(a.x * x, a.y * x, a.z * x);
-}
-
-
-DR_MATHCALL vec3 vec3_div(vec3 a, vec3 b)
-{
- return vec3f(a.x / b.x, a.y / b.y, a.z / b.z);
-}
-
-
-DR_MATHCALL float vec3_dot(vec3 a, vec3 b)
-{
- return a.x*b.x + a.y*b.y + a.z*b.z;
-}
-
-
-DR_MATHCALL float vec3_length2(vec3 a)
-{
- return vec3_dot(a, a);
-}
-
-DR_MATHCALL float vec3_length(vec3 a)
-{
- return sqrtf(vec3_length2(a));
-}
-
-
-DR_MATHCALL vec3 vec3_normalize(vec3 a)
-{
- float len = vec3_length(a);
-
- return vec3f(
- a.x / len,
- a.y / len,
- a.z / len
- );
-}
-
-DR_MATHCALL vec3 vec3_cross(vec3 a, vec3 b)
-{
- return vec3f(
- a.y*b.z - a.z*b.y,
- a.z*b.x - a.x*b.z,
- a.x*b.y - a.y*b.x
- );
-}
-
-
-DR_MATHCALL vec3 vec3_triangle_normal(vec3 p1, vec3 p2, vec3 p3)
-{
- vec3 u = vec3_sub(p2, p1);
- vec3 v = vec3_sub(p3, p1);
- return vec3_normalize(vec3_cross(u, v));
-}
-
-
-
-///////////////////////////////////////////////
-//
-// VEC2
-//
-///////////////////////////////////////////////
-
-DR_MATHCALL vec2 vec2f(float x, float y)
-{
- vec2 result;
- result.x = x;
- result.y = y;
-
- return result;
-}
-DR_MATHCALL vec2 vec2v(const float* v)
-{
- return vec2f(v[0], v[1]);
-}
-DR_MATHCALL vec2 vec2_zero()
-{
- return vec2f(0, 0);
-}
-DR_MATHCALL vec2 vec2_one()
-{
- return vec2f(1, 1);
-}
-
-
-DR_MATHCALL vec2 vec2_add(vec2 a, vec2 b)
-{
- return vec2f(a.x + b.x, a.y + b.y);
-}
-
-DR_MATHCALL vec2 vec2_sub(vec2 a, vec2 b)
-{
- return vec2f(a.x - b.x, a.y - b.y);
-}
-
-
-DR_MATHCALL vec2 vec2_mul(vec2 a, vec2 b)
-{
- return vec2f(a.x * b.x, a.y * b.y);
-}
-DR_MATHCALL vec2 vec2_mul_1f(vec2 a, float x)
-{
- return vec2f(a.x * x, a.y * x);
-}
-
-
-DR_MATHCALL vec2 vec2_div(vec2 a, vec2 b)
-{
- return vec2f(a.x / b.x, a.y / b.y);
-}
-
-
-DR_MATHCALL float vec2_dot(vec2 a, vec2 b)
-{
- return a.x*b.x + a.y*b.y;
-}
-
-
-DR_MATHCALL float vec2_length2(vec2 a)
-{
- return vec2_dot(a, a);
-}
-
-DR_MATHCALL float vec2_length(vec2 a)
-{
- return sqrtf(vec2_length2(a));
-}
-
-
-DR_MATHCALL vec2 vec2_normalize(vec2 a)
-{
- float len = vec2_length(a);
-
- return vec2f(
- a.x / len,
- a.y / len
- );
-}
-
-
-DR_MATHCALL float vec2_angle(vec2 a, vec2 b)
-{
- return atanf(a.y / a.x) - atanf(b.y / b.x);
-}
-
-DR_MATHCALL vec2 vec2_rotate(vec2 a, float angleInRadians)
-{
- float c = cosf(angleInRadians);
- float s = sinf(angleInRadians);
-
- return vec2f(
- a.x*c - a.y*s,
- a.x*s + a.y*c
- );
-}
-
-
-///////////////////////////////////////////////
-//
-// MAT4
-//
-///////////////////////////////////////////////
-
-DR_MATHCALL mat4 mat4f(vec4 col0, vec4 col1, vec4 col2, vec4 col3)
-{
- mat4 result;
- result.col[0] = col0;
- result.col[1] = col1;
- result.col[2] = col2;
- result.col[3] = col3;
-
- return result;
-}
-
-DR_MATHCALL mat4 mat4_identity()
-{
- mat4 result;
- result.col[0] = vec4f(1, 0, 0, 0);
- result.col[1] = vec4f(0, 1, 0, 0);
- result.col[2] = vec4f(0, 0, 1, 0);
- result.col[3] = vec4f(0, 0, 0, 1);
-
- return result;
-}
-
-DR_MATHCALL mat4 mat4_ortho(float left, float right, float bottom, float top, float znear, float zfar)
-{
- float rml = right - left;
- float tmb = top - bottom;
- float fmn = zfar - znear;
-
- float rpl = right + left;
- float tpb = top + bottom;
- float fpn = zfar + znear;
-
- mat4 result;
- result.col[0] = vec4f(2/rml, 0, 0, 0);
- result.col[1] = vec4f(0, 2/tmb, 0, 0);
- result.col[2] = vec4f(0, 0, -2/fmn, 0);
- result.col[3] = vec4f(-(rpl/rml), -(tpb/tmb), -(fpn/fmn), 1);
-
- return result;
-}
-
-DR_MATHCALL mat4 mat4_perspective(float fovy, float aspect, float znear, float zfar)
-{
- float f = (float)tan(DR_PI/2 - fovy/2);
-
- mat4 result;
- result.col[0] = vec4f(f / aspect, 0, 0, 0);
- result.col[1] = vec4f(0, f, 0, 0);
- result.col[2] = vec4f(0, 0, (zfar + znear) / (znear - zfar), -1);
- result.col[3] = vec4f(0, 0, (2 * zfar * znear) / (znear - zfar), 0);
-
- return result;
-}
-
-DR_MATHCALL mat4 mat4_vulkan_clip_correction()
-{
- mat4 result;
- result.col[0] = vec4f(1, 0, 0, 0);
- result.col[1] = vec4f(0, -1, 0, 0);
- result.col[2] = vec4f(0, 0, 0.5f, 0);
- result.col[3] = vec4f(0, 0, 0.5f, 1);
-
- return result;
-}
-
-DR_MATHCALL mat4 mat4_translate(vec3 translation)
-{
- mat4 result;
- result.col[0] = vec4f(1, 0, 0, 0);
- result.col[1] = vec4f(0, 1, 0, 0);
- result.col[2] = vec4f(0, 0, 1, 0);
- result.col[3] = vec4f(translation.x, translation.y, translation.z, 1);
-
- return result;
-}
-
-DR_MATHCALL mat4 mat4_rotate(float angleInRadians, vec3 axis)
-{
- float c = cosf(angleInRadians);
- float s = sinf(angleInRadians);
-
- float x = axis.x;
- float y = axis.y;
- float z = axis.z;
-
- float xx = x*x;
- float xy = x*y;
- float xz = x*z;
- float yy = y*y;
- float yz = y*z;
- float zz = z*z;
-
- float xs = x*s;
- float ys = y*s;
- float zs = z*s;
-
- mat4 result;
- result.col[0] = vec4f(xx * (1 - c) + c, xy * (1 - c) - zs, xz * (1 - c) + ys, 0);
- result.col[1] = vec4f(xy * (1 - c) + zs, yy * (1 - c) + c, yz * (1 - c) - xs, 0);
- result.col[2] = vec4f(xz * (1 - c) - ys, yz * (1 - c) + xs, zz * (1 - c) + c, 0);
- result.col[3] = vec4f(0, 0, 0, 1);
-
- return result;
-}
-
-DR_MATHCALL mat4 mat4_scale(vec3 scale)
-{
- mat4 result;
- result.col[0] = vec4f(scale.x, 0, 0, 0);
- result.col[1] = vec4f(0, scale.y, 0, 0);
- result.col[2] = vec4f(0, 0, scale.z, 0);
- result.col[3] = vec4f(0, 0, 0, 1);
-
- return result;
-}
-
-
-DR_MATHCALL mat4 mat4_mul(mat4 a, mat4 b)
-{
- const vec4 a0 = a.col[0];
- const vec4 a1 = a.col[1];
- const vec4 a2 = a.col[2];
- const vec4 a3 = a.col[3];
-
- const vec4 b0 = b.col[0];
- const vec4 b1 = b.col[1];
- const vec4 b2 = b.col[2];
- const vec4 b3 = b.col[3];
-
- mat4 result;
- result.col[0] = vec4f(
- a0.x*b0.x + a1.x*b0.y + a2.x*b0.z + a3.x*b0.w,
- a0.y*b0.x + a1.y*b0.y + a2.y*b0.z + a3.y*b0.w,
- a0.z*b0.x + a1.z*b0.y + a2.z*b0.z + a3.z*b0.w,
- a0.w*b0.x + a1.w*b0.y + a2.w*b0.z + a3.w*b0.w
- );
-
- result.col[1] = vec4f(
- a0.x*b1.x + a1.x*b1.y + a2.x*b1.z + a3.x*b1.w,
- a0.y*b1.x + a1.y*b1.y + a2.y*b1.z + a3.y*b1.w,
- a0.z*b1.x + a1.z*b1.y + a2.z*b1.z + a3.z*b1.w,
- a0.w*b1.x + a1.w*b1.y + a2.w*b1.z + a3.w*b1.w
- );
-
- result.col[2] = vec4f(
- a0.x*b2.x + a1.x*b2.y + a2.x*b2.z + a3.x*b2.w,
- a0.y*b2.x + a1.y*b2.y + a2.y*b2.z + a3.y*b2.w,
- a0.z*b2.x + a1.z*b2.y + a2.z*b2.z + a3.z*b2.w,
- a0.w*b2.x + a1.w*b2.y + a2.w*b2.z + a3.w*b2.w
- );
-
- result.col[3] = vec4f(
- a0.x*b3.x + a1.x*b3.y + a2.x*b3.z + a3.x*b3.w,
- a0.y*b3.x + a1.y*b3.y + a2.y*b3.z + a3.y*b3.w,
- a0.z*b3.x + a1.z*b3.y + a2.z*b3.z + a3.z*b3.w,
- a0.w*b3.x + a1.w*b3.y + a2.w*b3.z + a3.w*b3.w
- );
-
- return result;
-}
-
-DR_MATHCALL vec4 mat4_mul_vec4(mat4 m, vec4 v)
-{
- const vec4 m0 = m.col[0];
- const vec4 m1 = m.col[1];
- const vec4 m2 = m.col[2];
- const vec4 m3 = m.col[3];
-
- return vec4f(
- m0.x*v.x + m1.x*v.y + m2.x*v.z + m3.x*v.w,
- m0.y*v.x + m1.y*v.y + m2.y*v.z + m3.y*v.w,
- m0.z*v.x + m1.z*v.y + m2.z*v.z + m3.z*v.w,
- m0.w*v.x + m1.w*v.y + m2.w*v.z + m3.w*v.w
- );
-}
-
-
-///////////////////////////////////////////////
-//
-// QUAT
-//
-///////////////////////////////////////////////
-
-DR_MATHCALL quat quatf(float x, float y, float z, float w)
-{
- quat result;
- result.x = x;
- result.y = y;
- result.z = z;
- result.w = w;
-
- return result;
-}
-DR_MATHCALL quat quatv(const float* v)
-{
- return quatf(v[0], v[1], v[2], v[3]);
-}
-
-DR_MATHCALL quat quat_identity()
-{
- return quatf(0, 0, 0, 1);
-}
-
-
-
-
-
-///////////////////////////////////////////////
-//
-// TRANSFORM
-//
-///////////////////////////////////////////////
-
-typedef struct
-{
- vec3 position;
- quat rotation;
- vec3 scale;
-}transform_t;
-
-DR_MATHCALL transform_t transform_init(vec3 position, quat rotation, vec3 scale)
-{
- transform_t result;
- result.position = position;
- result.rotation = rotation;
- result.scale = scale;
-
- return result;
-}
-
-DR_MATHCALL transform_t transform_identity()
-{
- transform_t result;
- result.position = vec3_zero();
- result.rotation = quat_identity();
- result.scale = vec3_one();
-
- return result;
-}
-
-
-DR_MATHCALL transform_t transform_translate(transform_t transform, vec3 offset)
-{
- transform_t result = transform;
- result.position = vec3_add(transform.position, offset);
-
- return result;
-}
-
-
-
-
-///////////////////////////////////////////////
-//
-// SSE IMPLEMENTATION
-//
-///////////////////////////////////////////////
-
-// Not supporting SSE on x86/MSVC due to pass-by-value errors with aligned types.
-#if (defined(_MSC_VER) && defined(_M_X64)) || defined(__SSE2__)
-#define SUPPORTS_SSE
-#endif
-
-#if !defined(DISABLE_SSE) && defined(SUPPORTS_SSE)
-#define ENABLE_SSE
-#endif
-
-#ifdef ENABLE_SSE
-#if defined(__MINGW32__)
-#include
-#endif
-#include
-#endif
-
-
-
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif //dr_math_h
-
-/*
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-For more information, please refer to
-*/
diff --git a/source/engine/thirdparty/dr_libs/old/dr_mtl.h b/source/engine/thirdparty/dr_libs/old/dr_mtl.h
deleted file mode 100644
index cf97a98..0000000
--- a/source/engine/thirdparty/dr_libs/old/dr_mtl.h
+++ /dev/null
@@ -1,4512 +0,0 @@
-// Public Domain. See "unlicense" statement at the end of this file.
-
-// ABOUT
-//
-// dr_mtl is a library for loading materials for use in 2D or 3D graphics. But it's a bit different to
-// what you may expect. It does not reprsent materials as a static data, but rather as a series of
-// instructions that interface with a set of inputs. Indeed, this library is more like a compiler - you
-// specify an input file (such as a Wavefront MTL file), compile it into an intermediate bytecode
-// represenation, and then run it through a code generator to generate shader code such as GLSL, HLSL,
-// Spir-V, etc.
-//
-//
-//
-// USAGE
-//
-// This is a single-file library. To use it, do something like the following in one .c file.
-// #define DR_MTL_IMPLEMENTATION
-// #include "dr_mtl.h"
-//
-// You can then #include dr_mtl.h in other parts of the program as you would with any other header file.
-//
-//
-//
-// OPTIONS
-//
-// #define DRMTL_NO_MTL_COMPILER
-// Disables the Wavefront MTL compiler.
-//
-// #define DRMTL_NO_GLSL_CODEGEN
-// Disables the GLSL code generator.
-//
-//
-//
-// TODO
-// - Add more documentation.
-// - Add a demo to demonstrate the awesomeness of this library.
-// - Add trigonometric instructions.
-
-#ifndef dr_mtl_h
-#define dr_mtl_h
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include
-#include
-
-#if defined(_MSC_VER)
- #pragma warning(push)
- #pragma warning(disable:4201) // Non-standard extension used: nameless struct/union.
-#endif
-
-
-#define DRMTL_MAGIC_NUMBER 0x81DF7405
-#define DRMTL_CURRENT_VERSION 1
-
-#define DRMTL_INPUT_DESC_CONSTI ((unsigned char)-2)
-#define DRMTL_INPUT_DESC_CONSTF ((unsigned char)-1)
-#define DRMTL_INPUT_DESC_VARX ((unsigned char)0)
-#define DRMTL_INPUT_DESC_VARY ((unsigned char)1)
-#define DRMTL_INPUT_DESC_VARZ ((unsigned char)2)
-#define DRMTL_INPUT_DESC_VARW ((unsigned char)3)
-
-#define DRMTL_MAX_IDENTIFIER_NAME 28
-#define DRMTL_MAX_CHANNEL_NAME 28
-#define DRMTL_MAX_PROPERTY_NAME 28
-
-#define DRMTL_MAX_INPUT_PATH 252
-#define DRMTL_MAX_PROPERTY_PATH 224
-
-
-typedef unsigned char drmtl_uint8;
-typedef unsigned int drmtl_uint32;
-
-typedef struct drmtl_material drmtl_material;
-
-
-/// The various data type available to the material.
-typedef enum
-{
- drmtl_type_float = 1,
- drmtl_type_float2 = 2,
- drmtl_type_float3 = 3,
- drmtl_type_float4 = 4,
- drmtl_type_int = 5,
- drmtl_type_int2 = 6,
- drmtl_type_int3 = 7,
- drmtl_type_int4 = 8,
- drmtl_type_tex1d = 9,
- drmtl_type_tex2d = 10,
- drmtl_type_tex3d = 11,
- drmtl_type_texcube = 12,
- drmtl_type_bool = 13
-
-} drmtl_type;
-
-/// The various run-time opcodes.
-typedef enum
-{
- //////////////////////////
- // Assignment
-
- // mov
- drmtl_opcode_movf1 = 0x00000001,
- drmtl_opcode_movf2 = 0x00000002,
- drmtl_opcode_movf3 = 0x00000003,
- drmtl_opcode_movf4 = 0x00000004,
- drmtl_opcode_movi1 = 0x00000005,
- drmtl_opcode_movi2 = 0x00000006,
- drmtl_opcode_movi3 = 0x00000007,
- drmtl_opcode_movi4 = 0x00000008,
-
-
- //////////////////////////
- // Arithmetic
-
- // add
- drmtl_opcode_addf1 = 0x00001001,
- drmtl_opcode_addf2 = 0x00001002,
- drmtl_opcode_addf3 = 0x00001003,
- drmtl_opcode_addf4 = 0x00001004,
- drmtl_opcode_addi1 = 0x00001005,
- drmtl_opcode_addi2 = 0x00001006,
- drmtl_opcode_addi3 = 0x00001007,
- drmtl_opcode_addi4 = 0x00001008,
-
- // sub
- drmtl_opcode_subf1 = 0x00001101,
- drmtl_opcode_subf2 = 0x00001102,
- drmtl_opcode_subf3 = 0x00001103,
- drmtl_opcode_subf4 = 0x00001104,
- drmtl_opcode_subi1 = 0x00001105,
- drmtl_opcode_subi2 = 0x00001106,
- drmtl_opcode_subi3 = 0x00001107,
- drmtl_opcode_subi4 = 0x00001108,
-
- // mul
- drmtl_opcode_mulf1 = 0x00001201,
- drmtl_opcode_mulf2 = 0x00001202,
- drmtl_opcode_mulf3 = 0x00001203,
- drmtl_opcode_mulf4 = 0x00001204,
- drmtl_opcode_muli1 = 0x00001205,
- drmtl_opcode_muli2 = 0x00001206,
- drmtl_opcode_muli3 = 0x00001207,
- drmtl_opcode_muli4 = 0x00001208,
-
- // div
- drmtl_opcode_divf1 = 0x00001301,
- drmtl_opcode_divf2 = 0x00001302,
- drmtl_opcode_divf3 = 0x00001303,
- drmtl_opcode_divf4 = 0x00001304,
- drmtl_opcode_divi1 = 0x00001305,
- drmtl_opcode_divi2 = 0x00001306,
- drmtl_opcode_divi3 = 0x00001307,
- drmtl_opcode_divi4 = 0x00001308,
-
- // pow
- drmtl_opcode_powf1 = 0x00001401,
- drmtl_opcode_powf2 = 0x00001402,
- drmtl_opcode_powf3 = 0x00001403,
- drmtl_opcode_powf4 = 0x00001404,
- drmtl_opcode_powi1 = 0x00001405,
- drmtl_opcode_powi2 = 0x00001406,
- drmtl_opcode_powi3 = 0x00001407,
- drmtl_opcode_powi4 = 0x00001408,
-
-
- //////////////////////////
- // Textures
-
- // tex
- drmtl_opcode_tex1 = 0x00002001,
- drmtl_opcode_tex2 = 0x00002002,
- drmtl_opcode_tex3 = 0x00002003,
- drmtl_opcode_texcube = 0x00002004,
-
-
- //////////////////////////
- // Miscellaneous
-
- // var
- drmtl_opcode_var = 0x00003000,
-
- // ret
- drmtl_opcode_retf1 = 0x00003001,
- drmtl_opcode_retf2 = 0x00003002,
- drmtl_opcode_retf3 = 0x00003003,
- drmtl_opcode_retf4 = 0x00003004,
- drmtl_opcode_reti1 = 0x00003005,
- drmtl_opcode_reti2 = 0x00003006,
- drmtl_opcode_reti3 = 0x00003007,
- drmtl_opcode_reti4 = 0x00003008
-
-} drmtl_opcode;
-
-
-/// Structure containing information about an identifier. An identifier contains a type (float, float2, etc.) and a name. The
-/// total size of this structure is 32 bytes. The name is null terminated.
-typedef struct
-{
- /// The type of the identifier.
- drmtl_type type;
-
- /// The name of the identifier.
- char name[DRMTL_MAX_IDENTIFIER_NAME];
-
-} drmtl_identifier;
-
-
-/// Structure containing information about an input variable.
-typedef struct
-{
- /// The index into the identifier table that this input variable is identified by.
- unsigned int identifierIndex;
-
- /// The default value of the input variable.
- union
- {
- struct
- {
- float x;
- } f1;
- struct
- {
- float x;
- float y;
- } f2;
- struct
- {
- float x;
- float y;
- float z;
- } f3;
- struct
- {
- float x;
- float y;
- float z;
- float w;
- } f4;
-
- struct
- {
- int x;
- } i1;
- struct
- {
- int x;
- int y;
- } i2;
- struct
- {
- int x;
- int y;
- int z;
- } i3;
- struct
- {
- int x;
- int y;
- int z;
- int w;
- } i4;
-
- struct
- {
- char value[DRMTL_MAX_INPUT_PATH]; // Enough room for a path, but less to keep the total size of the structure at 256 bytes. Null terminated.
- } path;
-
-
- struct
- {
- drmtl_uint32 x;
- drmtl_uint32 y;
- drmtl_uint32 z;
- drmtl_uint32 w;
- } raw4;
- };
-
-} drmtl_input;
-
-
-typedef struct
-{
- /// The return type of the channel.
- drmtl_type type;
-
- /// The name of the channel. Null terminated.
- char name[DRMTL_MAX_CHANNEL_NAME];
-
-} drmtl_channel;
-
-
-/// An instruction input value. An input value to an instruction can usually be a constant or the identifier index of the
-/// applicable variable.
-typedef union
-{
- /// The identifier index of the applicable variable.
- unsigned int id;
-
- /// The constant value, as a float.
- float valuef;
-
- /// The constant value, as an integer.
- int valuei;
-
-}drmtl_instruction_input;
-
-
-/// Structure used for describing an instructions input data.
-typedef struct
-{
- unsigned char x;
- unsigned char y;
- unsigned char z;
- unsigned char w;
-
-} drmtl_instruction_input_descriptor;
-
-/// Structure containing information about an instruction.
-typedef struct
-{
- /// The instruction's opcode.
- drmtl_opcode opcode;
-
- /// The instruction's data.
- union
- {
- // mov data.
- struct
- {
- drmtl_instruction_input_descriptor inputDesc;
- drmtl_instruction_input inputX;
- drmtl_instruction_input inputY;
- drmtl_instruction_input inputZ;
- drmtl_instruction_input inputW;
- unsigned int output;
- } mov;
-
-
- // add data.
- struct
- {
- drmtl_instruction_input_descriptor inputDesc;
- drmtl_instruction_input inputX;
- drmtl_instruction_input inputY;
- drmtl_instruction_input inputZ;
- drmtl_instruction_input inputW;
- unsigned int output;
- } add;
-
- // sub data.
- struct
- {
- drmtl_instruction_input_descriptor inputDesc;
- drmtl_instruction_input inputX;
- drmtl_instruction_input inputY;
- drmtl_instruction_input inputZ;
- drmtl_instruction_input inputW;
- unsigned int output;
- } sub;
-
- // mul data.
- struct
- {
- drmtl_instruction_input_descriptor inputDesc;
- drmtl_instruction_input inputX;
- drmtl_instruction_input inputY;
- drmtl_instruction_input inputZ;
- drmtl_instruction_input inputW;
- unsigned int output;
- } mul;
-
- // div data.
- struct
- {
- drmtl_instruction_input_descriptor inputDesc;
- drmtl_instruction_input inputX;
- drmtl_instruction_input inputY;
- drmtl_instruction_input inputZ;
- drmtl_instruction_input inputW;
- unsigned int output;
- } div;
-
- // pow data.
- struct
- {
- drmtl_instruction_input_descriptor inputDesc;
- drmtl_instruction_input inputX;
- drmtl_instruction_input inputY;
- drmtl_instruction_input inputZ;
- drmtl_instruction_input inputW;
- unsigned int output;
- } pow;
-
-
- // tex data.
- struct
- {
- drmtl_instruction_input_descriptor inputDesc;
- drmtl_instruction_input inputX;
- drmtl_instruction_input inputY;
- drmtl_instruction_input inputZ;
- drmtl_instruction_input inputW;
- unsigned int texture;
- unsigned int output;
- } tex;
-
-
- // ret data.
- struct
- {
- drmtl_instruction_input_descriptor inputDesc;
- drmtl_instruction_input inputX;
- drmtl_instruction_input inputY;
- drmtl_instruction_input inputZ;
- drmtl_instruction_input inputW;
- } ret;
-
-
- // var data
- struct
- {
- //drmtl_type type;
- unsigned int identifierIndex;
- } var;
-
-
- // Ensures the size of the instruction is always 32 bytes.
- struct
- {
- drmtl_uint8 _unused[32];
- } unused;
- };
-
-} drmtl_instruction;
-
-
-typedef struct
-{
- /// The type of the property.
- drmtl_type type;
-
- /// The name of the property.
- char name[DRMTL_MAX_PROPERTY_NAME];
-
- /// The default value of the input variable.
- union
- {
- struct
- {
- float x;
- } f1;
- struct
- {
- float x;
- float y;
- } f2;
- struct
- {
- float x;
- float y;
- float z;
- } f3;
- struct
- {
- float x;
- float y;
- float z;
- float w;
- } f4;
-
- struct
- {
- int x;
- } i1;
- struct
- {
- int x;
- int y;
- } i2;
- struct
- {
- int x;
- int y;
- int z;
- } i3;
- struct
- {
- int x;
- int y;
- int z;
- int w;
- } i4;
-
- struct
- {
- char value[DRMTL_MAX_PROPERTY_PATH]; // Enough room for a path, but less to keep the total size of the structure at 256 bytes. Null terminated.
- } path;
-
- struct
- {
- int x;
- } b1;
- };
-
-} drmtl_property;
-
-
-/// Structure containing the header information of the material.
-typedef struct
-{
- /// The magic number: 0x81DF7405
- unsigned int magic;
-
- /// The version number. There is currently only a single version - version 1. In the future there may be other versions
- /// which will affect how the file is formatted.
- unsigned int version;
-
-
- /// The size in bytes of an identifier.
- unsigned int identifierSizeInBytes;
-
- /// The size in bytes of an input variable.
- unsigned int inputSizeInBytes;
-
- /// The size of a channel header, in bytes.
- unsigned int channelHeaderSizeInBytes;
-
- /// The size in bytes of an instruction.
- unsigned int instructionSizeInBytes;
-
- /// The size in bytes of a property.
- unsigned int propertySizeInBytes;
-
-
- /// The total number of identifiers.
- unsigned int identifierCount;
-
- /// The total number of private input variables.
- unsigned int privateInputCount;
-
- /// The total number of public input variables.
- unsigned int publicInputCount;
-
- /// The total number of channels.
- unsigned int channelCount;
-
- /// The total number of properties.
- unsigned int propertyCount;
-
-
- /// The offset of the identifiers.
- unsigned int identifiersOffset;
-
- /// The offset of the input variables.
- unsigned int inputsOffset;
-
- /// The offset of the channels.
- unsigned int channelsOffset;
-
- /// The offset of the properties.
- unsigned int propertiesOffset;
-
-
-} drmtl_header;
-
-typedef struct
-{
- /// The channel information.
- drmtl_channel channel;
-
- /// The instruction count of the channel.
- unsigned int instructionCount;
-
-} drmtl_channel_header;
-
-
-/// Structure containing the definition of the material.
-struct drmtl_material
-{
- /// A pointer to the raw data. This will at least be the size of drmtl_header (128 bytes, as of version 1).
- drmtl_uint8* pRawData;
-
- /// The size of the data, in bytes.
- unsigned int sizeInBytes;
-
- /// The size of the buffer, in bytes. This is used to determine when the buffer needs to be inflated.
- unsigned int bufferSizeInBytes;
-
-
- /// The current stage of the construction process of the material. A material must be constructed in order, so this
- /// keeps track of the current stage to ensure the proper errors are returned.
- unsigned int currentStage;
-
- /// The offset of the current channel. This is needed to we can determine which bytes need to be updated as
- /// instructions are added to the channel.
- unsigned int currentChannelOffset;
-
-
- /// Whether or not the material data is owned by this library. When this is set to false, the library will
- /// never modify the original pointer.
- bool ownsRawData;
-};
-
-
-
-////////////////////////////////////////////////////////
-// Low-Level APIs
-//
-// Use these APIs to work on materials directly. Note that these are intentionally restrictive in that things can
-// be added to the material, however they can never be removed. In addition, everything must be added in order which
-// means identifiers need to be added first, followed by private inputs, followed by public inputs, followed by
-// channels, followed by properties.
-//
-// Use these to construct a material after everything has been processed at a higher level.
-
-/// Initializes the given material.
-///
-/// @param pMaterial [in] A pointer to the material to initialize.
-///
-/// @return True if the material is initialized successfully; false otherwise.
-bool drmtl_init(drmtl_material* pMaterial);
-bool drmtl_initfromexisting(drmtl_material* pMaterial, const void* pRawData, unsigned int dataSizeInBytes);
-bool drmtl_initfromexisting_nocopy(drmtl_material* pMaterial, const void* pRawData, unsigned int dataSizeInBytes);
-
-/// Uninitializes the given material.
-///
-/// @param pMaterial [in] A pointer to the material to uninitialize.
-void drmtl_uninit(drmtl_material* pMaterial);
-
-
-/// Retrieve a pointer to the header information.
-drmtl_header* drmtl_getheader(drmtl_material* pMaterial);
-
-
-/// Appends an identifier to the end of the identifier list. Use drmtl_getidentifiercount() to determine it's index.
-///
-/// @param pMaterial [in] A pointer to the material to append the identifier to.
-bool drmtl_appendidentifier(drmtl_material* pMaterial, drmtl_identifier identifier, unsigned int* indexOut);
-
-/// Appends a private input variable.
-bool drmtl_appendprivateinput(drmtl_material* pMaterial, drmtl_input input);
-
-/// Appends a public input variable.
-bool drmtl_appendpublicinput(drmtl_material* pMaterial, drmtl_input input);
-
-/// Begins a new channel.
-///
-/// @remarks
-/// Any instructions that are appended from now on will be part of this channel until another channel is begun.
-/// @par
-/// The end of the channel is marked when a new channel is appended or a property begins.
-bool drmtl_appendchannel(drmtl_material* pMaterial, drmtl_channel channelHeader);
-
-/// Appends an instruction to the most recently appended channel.
-bool drmtl_appendinstruction(drmtl_material* pMaterial, drmtl_instruction instruction);
-
-/// Append a property.
-bool drmtl_appendproperty(drmtl_material* pMaterial, drmtl_property prop);
-
-
-/// Retrieves a pointer to the channel header by it's index.
-///
-/// @remarks
-/// This runs in linear time.
-drmtl_channel_header* drmtl_getchannelheaderbyindex(drmtl_material* pMaterial, unsigned int channelIndex);
-
-/// Retrieves a pointer to the channel header by it's name.
-///
-/// @remarks
-/// This runs in linear time.
-drmtl_channel_header* drmtl_getchannelheaderbyname(drmtl_material* pMaterial, const char* channelName);
-
-
-/// Retrieves a pointer to the buffer containing the list of identifiers.
-drmtl_identifier* drmtl_getidentifiers(drmtl_material* pMaterial);
-drmtl_identifier* drmtl_getidentifier(drmtl_material* pMaterial, unsigned int index);
-
-/// Retrieves the number of identifiers defined by the given material.
-unsigned int drmtl_getidentifiercount(drmtl_material* pMaterial);
-
-
-/// Retrieves the number of private + public input variables.
-unsigned int drmtl_getinputcount(drmtl_material* pMaterial);
-
-/// Retrieves the input variable by it's index.
-drmtl_input* drmtl_getinputbyindex(drmtl_material* pMaterial, unsigned int index);
-
-/// Retrieves the number of private input variables.
-unsigned int drmtl_getprivateinputcount(drmtl_material* pMaterial);
-
-/// Retrieves the private input variable by it's index.
-drmtl_input* drmtl_getprivateinputbyindex(drmtl_material* pMaterial, unsigned int index);
-
-/// Retrieves the number of public input variables.
-unsigned int drmtl_getpublicinputcount(drmtl_material* pMaterial);
-
-/// Retrieves the public input variable by it's index.
-drmtl_input* drmtl_getpublicinputbyindex(drmtl_material* pMaterial, unsigned int index);
-
-
-/// Retrieves the number of properties.
-unsigned int drmtl_getpropertycount(drmtl_material* pMaterial);
-
-/// Retrieves a property by it's index.
-drmtl_property* drmtl_getpropertybyindex(drmtl_material* pMaterial, unsigned int index);
-
-/// Retrieves a properties by it's name.
-///
-/// @remarks
-/// This is case-sensitive.
-drmtl_property* drmtl_getpropertybyname(drmtl_material* pMaterial, const char* name);
-
-
-
-////////////////////////////////////////////////////////
-// Mid-Level APIs
-
-/// Helper for creating an identifier.
-drmtl_identifier drmtl_identifier_float(const char* name);
-drmtl_identifier drmtl_identifier_float2(const char* name);
-drmtl_identifier drmtl_identifier_float3(const char* name);
-drmtl_identifier drmtl_identifier_float4(const char* name);
-drmtl_identifier drmtl_identifier_int(const char* name);
-drmtl_identifier drmtl_identifier_int2(const char* name);
-drmtl_identifier drmtl_identifier_int3(const char* name);
-drmtl_identifier drmtl_identifier_int4(const char* name);
-drmtl_identifier drmtl_identifier_tex2d(const char* name);
-
-/// Helper for creating an input variable.
-drmtl_input drmtl_input_float(unsigned int identifierIndex, float x);
-drmtl_input drmtl_input_float2(unsigned int identifierIndex, float x, float y);
-drmtl_input drmtl_input_float3(unsigned int identifierIndex, float x, float y, float z);
-drmtl_input drmtl_input_float4(unsigned int identifierIndex, float x, float y, float z, float w);
-drmtl_input drmtl_input_int(unsigned int identifierIndex, int x);
-drmtl_input drmtl_input_int2(unsigned int identifierIndex, int x, int y);
-drmtl_input drmtl_input_int3(unsigned int identifierIndex, int x, int y, int z);
-drmtl_input drmtl_input_int4(unsigned int identifierIndex, int x, int y, int z, int w);
-drmtl_input drmtl_input_tex(unsigned int identifierIndex, const char* path);
-
-/// Helper for creating a channel.
-drmtl_channel drmtl_channel_float(const char* name);
-drmtl_channel drmtl_channel_float2(const char* name);
-drmtl_channel drmtl_channel_float3(const char* name);
-drmtl_channel drmtl_channel_float4(const char* name);
-drmtl_channel drmtl_channel_int(const char* name);
-drmtl_channel drmtl_channel_int2(const char* name);
-drmtl_channel drmtl_channel_int3(const char* name);
-drmtl_channel drmtl_channel_int4(const char* name);
-
-/// Helper for creating an instruction. These are heavily simplified and more complex setups are possible using lower level APIs.
-drmtl_instruction drmtl_movf1_v1(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_movf1_c1(unsigned int outputIdentifierIndex, float x);
-drmtl_instruction drmtl_movf2_v2(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_movf2_c2(unsigned int outputIdentifierIndex, float x, float y);
-drmtl_instruction drmtl_movf3_v3(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_movf3_c3(unsigned int outputIdentifierIndex, float x, float y, float z);
-drmtl_instruction drmtl_movf4_v4(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_movf4_c4(unsigned int outputIdentifierIndex, float x, float y, float z, float w);
-
-drmtl_instruction drmtl_addf1_v1(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_addf1_c1(unsigned int outputIdentifierIndex, float x);
-drmtl_instruction drmtl_addf2_v2(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_addf2_c2(unsigned int outputIdentifierIndex, float x, float y);
-drmtl_instruction drmtl_addf3_v3(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_addf3_c3(unsigned int outputIdentifierIndex, float x, float y, float z);
-drmtl_instruction drmtl_addf4_v4(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_addf4_c4(unsigned int outputIdentifierIndex, float x, float y, float z, float w);
-
-drmtl_instruction drmtl_subf1_v1(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_subf1_c1(unsigned int outputIdentifierIndex, float x);
-drmtl_instruction drmtl_subf2_v2(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_subf2_c2(unsigned int outputIdentifierIndex, float x, float y);
-drmtl_instruction drmtl_subf3_v3(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_subf3_c3(unsigned int outputIdentifierIndex, float x, float y, float z);
-drmtl_instruction drmtl_subf4_v4(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_subf4_c4(unsigned int outputIdentifierIndex, float x, float y, float z, float w);
-
-drmtl_instruction drmtl_mulf1_v1(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_mulf1_c1(unsigned int outputIdentifierIndex, float x);
-drmtl_instruction drmtl_mulf2_v2(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_mulf2_c2(unsigned int outputIdentifierIndex, float x, float y);
-drmtl_instruction drmtl_mulf3_v3(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_mulf3_c3(unsigned int outputIdentifierIndex, float x, float y, float z);
-drmtl_instruction drmtl_mulf4_v4(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_mulf4_c4(unsigned int outputIdentifierIndex, float x, float y, float z, float w);
-drmtl_instruction drmtl_mulf4_v3v1(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndexXYZ, unsigned int inputIdentifierIndexW);
-drmtl_instruction drmtl_mulf4_v3c1(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex, float w);
-drmtl_instruction drmtl_mulf4_v2c2(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex, float z, float w);
-drmtl_instruction drmtl_mulf4_v1c3(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex, float y, float z, float w);
-drmtl_instruction drmtl_mulf4_v1v1v1v1(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndexX, unsigned int inputIdentifierIndexY, unsigned int inputIdentifierIndexZ, unsigned int inputIdentifierIndexW);
-
-drmtl_instruction drmtl_divf1_v1(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_divf1_c1(unsigned int outputIdentifierIndex, float x);
-drmtl_instruction drmtl_divf2_v2(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_divf2_c2(unsigned int outputIdentifierIndex, float x, float y);
-drmtl_instruction drmtl_divf3_v3(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_divf3_c3(unsigned int outputIdentifierIndex, float x, float y, float z);
-drmtl_instruction drmtl_divf4_v4(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex);
-drmtl_instruction drmtl_divf4_c4(unsigned int outputIdentifierIndex, float x, float y, float z, float w);
-
-drmtl_instruction drmtl_tex2(unsigned int outputIdentifierIndex, unsigned int textureIdentifierIndex, unsigned int texcoordIdentifierIndex);
-drmtl_instruction drmtl_var(unsigned int identifierIndex);
-drmtl_instruction drmtl_retf1(unsigned int identifierIndex);
-drmtl_instruction drmtl_retf2(unsigned int identifierIndex);
-drmtl_instruction drmtl_retf3(unsigned int identifierIndex);
-drmtl_instruction drmtl_retf4(unsigned int identifierIndex);
-drmtl_instruction drmtl_retf1_c1(float x);
-drmtl_instruction drmtl_retf2_c2(float x, float y);
-drmtl_instruction drmtl_retf3_c3(float x, float y, float z);
-drmtl_instruction drmtl_retf4_c4(float x, float y, float z, float w);
-drmtl_instruction drmtl_reti1(unsigned int identifierIndex);
-drmtl_instruction drmtl_reti2(unsigned int identifierIndex);
-drmtl_instruction drmtl_reti3(unsigned int identifierIndex);
-drmtl_instruction drmtl_reti4(unsigned int identifierIndex);
-drmtl_instruction drmtl_reti1_c1(int x);
-drmtl_instruction drmtl_reti2_c2(int x, int y);
-drmtl_instruction drmtl_reti3_c3(int x, int y, int z);
-drmtl_instruction drmtl_reti4_c4(int x, int y, int z, int w);
-
-/// Helper for creating a property.
-drmtl_property drmtl_property_float(const char* name, float x);
-drmtl_property drmtl_property_float2(const char* name, float x, float y);
-drmtl_property drmtl_property_float3(const char* name, float x, float y, float z);
-drmtl_property drmtl_property_float4(const char* name, float x, float y, float z, float w);
-drmtl_property drmtl_property_int(const char* name, int x);
-drmtl_property drmtl_property_int2(const char* name, int x, int y);
-drmtl_property drmtl_property_int3(const char* name, int x, int y, int z);
-drmtl_property drmtl_property_int4(const char* name, int x, int y, int z, int w);
-drmtl_property drmtl_property_bool(const char* name, bool value);
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-//
-// Compilers
-//
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-#ifndef DRMTL_NO_MTL_COMPILER
-/// Compiles a Wavefront MTL file.
-///
-/// @param pMaterial [in] A pointer to the destination material.
-///
-/// @remarks
-/// This will compile the material at the first occurance of the "newmtl" statement, and will end at either the next
-/// occurance of "newmtl" of when the input buffer has been exhausted.
-/// @par
-/// This will initialize the material, so ensure that you have not already initialized it before calling this. If this
-/// returns successfully, call drmtl_uninit() to uninitialize the material.
-/// @par
-/// MTL files require texture coordinates in order to know how to select the appropriate sample from textures. The
-/// of the variable to use is specified in "texcoordInputName", and assumed to have at least 2 components (x and y).
-bool drmtl_compile_wavefront_mtl(drmtl_material* pMaterial, const char* mtlData, size_t mtlDataSizeInBytes, const char* texcoordInputName);
-#endif
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-//
-// Code Generators
-//
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-#ifndef DRMTL_NO_GLSL_CODEGEN
-/// Generates GLSL code for the channel with the given name.
-bool drmtl_codegen_glsl_channel(drmtl_material* pMaterial, const char* channelName, char* codeOut, size_t codeOutSizeInBytes, size_t* pBytesWrittenOut);
-
-/// Generates GLSL code for the uniform variables as defined by the material's public input variables.
-bool drmtl_codegen_glsl_uniforms(drmtl_material* pMaterial, char* codeOut, size_t codeOutSizeInBytes, size_t* pBytesWritteOut);
-#endif
-
-
-
-
-
-#if defined(_MSC_VER)
- #pragma warning(pop)
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif //dr_mtl_h
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// IMPLEMENTATION
-//
-///////////////////////////////////////////////////////////////////////////////
-#ifdef DR_MTL_IMPLEMENTATION
-#include
-#include
-#include
-#include
-#include
-
-#if defined(__clang__)
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wcast-align"
-#endif
-
-
-// When constructing the material's raw data, memory is allocated in blocks of this amount. This must be at least 256.
-#define DRMTL_CHUNK_SIZE 4096
-
-#define DRMTL_STAGE_IDS 0
-#define DRMTL_STAGE_PRIVATE_INPUTS 1
-#define DRMTL_STAGE_PUBLIC_INPUTS 2
-#define DRMTL_STAGE_CHANNELS 3
-#define DRMTL_STAGE_PROPERTIES 4
-#define DRMTL_STAGE_COMPLETE UINT_MAX
-
-
-////////////////////////////////////////////////////////
-// Utilities
-
-// strcpy()
-static int drmtl_strcpy(char* dst, size_t dstSizeInBytes, const char* src)
-{
-#if defined(_MSC_VER)
- return strcpy_s(dst, dstSizeInBytes, src);
-#else
- if (dst == 0) {
- return EINVAL;
- }
- if (dstSizeInBytes == 0) {
- return ERANGE;
- }
- if (src == 0) {
- dst[0] = '\0';
- return EINVAL;
- }
-
- char* iDst = dst;
- const char* iSrc = src;
- size_t remainingSizeInBytes = dstSizeInBytes;
- while (remainingSizeInBytes > 0 && iSrc[0] != '\0')
- {
- iDst[0] = iSrc[0];
-
- iDst += 1;
- iSrc += 1;
- remainingSizeInBytes -= 1;
- }
-
- if (remainingSizeInBytes > 0) {
- iDst[0] = '\0';
- } else {
- dst[0] = '\0';
- return ERANGE;
- }
-
- return 0;
-#endif
-}
-
-
-
-/// Inflates the materials data buffer by DRMTL_CHUNK_SIZE.
-bool _drmtl_inflate(drmtl_material* pMaterial);
-
-
-bool drmtl_init(drmtl_material* pMaterial)
-{
- if (pMaterial != NULL)
- {
- assert(DRMTL_CHUNK_SIZE >= 256);
-
- pMaterial->pRawData = (drmtl_uint8*)malloc(DRMTL_CHUNK_SIZE);
- if (pMaterial->pRawData != NULL)
- {
- pMaterial->sizeInBytes = sizeof(drmtl_header);
- pMaterial->bufferSizeInBytes = DRMTL_CHUNK_SIZE;
- pMaterial->currentStage = DRMTL_STAGE_IDS;
- pMaterial->currentChannelOffset = 0;
- pMaterial->ownsRawData = 1;
-
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- assert(pHeader != NULL);
-
- pHeader->magic = DRMTL_MAGIC_NUMBER;
- pHeader->version = DRMTL_CURRENT_VERSION;
- pHeader->identifierSizeInBytes = sizeof(drmtl_identifier);
- pHeader->inputSizeInBytes = sizeof(drmtl_input);
- pHeader->channelHeaderSizeInBytes = sizeof(drmtl_channel_header);
- pHeader->instructionSizeInBytes = sizeof(drmtl_instruction);
- pHeader->propertySizeInBytes = sizeof(drmtl_property);
- pHeader->identifierCount = 0;
- pHeader->privateInputCount = 0;
- pHeader->publicInputCount = 0;
- pHeader->channelCount = 0;
- pHeader->propertyCount = 0;
- pHeader->identifiersOffset = pMaterial->sizeInBytes;
- pHeader->inputsOffset = pMaterial->sizeInBytes;
- pHeader->channelsOffset = pMaterial->sizeInBytes;
- pHeader->propertiesOffset = pMaterial->sizeInBytes;
-
- return 1;
- }
- }
-
- return 0;
-}
-
-bool drmtl_initfromexisting(drmtl_material* pMaterial, const void* pRawData, unsigned int dataSizeInBytes)
-{
- if (pMaterial != NULL)
- {
- if (pRawData != NULL && dataSizeInBytes >= sizeof(drmtl_header))
- {
- if (((drmtl_header*)pMaterial->pRawData)->magic == DRMTL_MAGIC_NUMBER)
- {
- pMaterial->pRawData = (drmtl_uint8*)malloc(DRMTL_CHUNK_SIZE);
- if (pMaterial->pRawData != NULL)
- {
- memcpy(pMaterial->pRawData, pRawData, dataSizeInBytes);
- pMaterial->sizeInBytes = dataSizeInBytes;
- pMaterial->bufferSizeInBytes = dataSizeInBytes;
- pMaterial->currentStage = DRMTL_STAGE_COMPLETE;
- pMaterial->currentChannelOffset = 0;
- pMaterial->ownsRawData = 1;
-
- return 1;
- }
- }
- }
- }
-
- return 0;
-}
-
-bool drmtl_initfromexisting_nocopy(drmtl_material* pMaterial, const void* pRawData, unsigned int dataSizeInBytes)
-{
- if (pMaterial != NULL)
- {
- if (pRawData != NULL && dataSizeInBytes >= sizeof(drmtl_header))
- {
- if (((const drmtl_header*)pRawData)->magic == DRMTL_MAGIC_NUMBER)
- {
- pMaterial->pRawData = (drmtl_uint8*)pRawData;
- pMaterial->sizeInBytes = dataSizeInBytes;
- pMaterial->bufferSizeInBytes = dataSizeInBytes;
- pMaterial->currentStage = DRMTL_STAGE_COMPLETE;
- pMaterial->currentChannelOffset = 0;
- pMaterial->ownsRawData = 0;
-
- return 1;
- }
- }
- }
-
- return 0;
-}
-
-void drmtl_uninit(drmtl_material* pMaterial)
-{
- if (pMaterial != NULL)
- {
- if (pMaterial->ownsRawData)
- {
- free(pMaterial->pRawData);
- }
-
- pMaterial->pRawData = NULL;
- }
-}
-
-
-drmtl_header* drmtl_getheader(drmtl_material* pMaterial)
-{
- if (pMaterial != NULL)
- {
- return (drmtl_header*)pMaterial->pRawData;
- }
-
- return NULL;
-}
-
-
-bool drmtl_appendidentifier(drmtl_material* pMaterial, drmtl_identifier identifier, unsigned int* indexOut)
-{
- if (pMaterial != NULL)
- {
- if (pMaterial->currentStage <= DRMTL_STAGE_IDS)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- if (pHeader != NULL)
- {
- if (pMaterial->sizeInBytes + pHeader->identifierSizeInBytes > pMaterial->bufferSizeInBytes)
- {
- if (!_drmtl_inflate(pMaterial))
- {
- // An error occured when trying to inflate the buffer. Might be out of memory.
- return 0;
- }
-
- pHeader = drmtl_getheader(pMaterial);
- assert(pMaterial->sizeInBytes + pHeader->identifierSizeInBytes <= pMaterial->bufferSizeInBytes);
- }
-
-
- memcpy(pMaterial->pRawData + pHeader->inputsOffset, &identifier, pHeader->identifierSizeInBytes);
- pMaterial->sizeInBytes += pHeader->identifierSizeInBytes;
-
- pHeader->identifierCount += 1;
- pHeader->inputsOffset += pHeader->identifierSizeInBytes;
- pHeader->channelsOffset += pHeader->identifierSizeInBytes;
- pHeader->propertiesOffset += pHeader->identifierSizeInBytes;
-
-
- if (indexOut != NULL)
- {
- *indexOut = pHeader->identifierCount - 1;
- }
-
- return 1;
- }
- }
- }
-
- return 0;
-}
-
-bool drmtl_appendprivateinput(drmtl_material* pMaterial, drmtl_input input)
-{
- if (pMaterial != NULL)
- {
- if (pMaterial->currentStage <= DRMTL_STAGE_PRIVATE_INPUTS)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- if (pHeader != NULL)
- {
- if (pMaterial->sizeInBytes + pHeader->inputSizeInBytes > pMaterial->bufferSizeInBytes)
- {
- if (!_drmtl_inflate(pMaterial))
- {
- // An error occured when trying to inflate the buffer. Might be out of memory.
- return 0;
- }
-
- pHeader = drmtl_getheader(pMaterial);
- assert(pMaterial->sizeInBytes + pHeader->inputSizeInBytes <= pMaterial->bufferSizeInBytes);
- }
-
-
- memcpy(pMaterial->pRawData + pHeader->channelsOffset, &input, pHeader->inputSizeInBytes);
- pMaterial->sizeInBytes += pHeader->inputSizeInBytes;
-
- pHeader->privateInputCount += 1;
- pHeader->channelsOffset += pHeader->inputSizeInBytes;
- pHeader->propertiesOffset += pHeader->inputSizeInBytes;
-
-
- pMaterial->currentStage = DRMTL_STAGE_PRIVATE_INPUTS;
- return 1;
- }
- }
- }
-
- return 0;
-}
-
-bool drmtl_appendpublicinput(drmtl_material* pMaterial, drmtl_input input)
-{
- if (pMaterial != NULL)
- {
- if (pMaterial->currentStage <= DRMTL_STAGE_PUBLIC_INPUTS)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- if (pHeader != NULL)
- {
- if (pMaterial->sizeInBytes + pHeader->inputSizeInBytes > pMaterial->bufferSizeInBytes)
- {
- if (!_drmtl_inflate(pMaterial))
- {
- // An error occured when trying to inflate the buffer. Might be out of memory.
- return 0;
- }
-
- pHeader = drmtl_getheader(pMaterial);
- assert(pMaterial->sizeInBytes + pHeader->inputSizeInBytes <= pMaterial->bufferSizeInBytes);
- }
-
-
- memcpy(pMaterial->pRawData + pHeader->channelsOffset, &input, pHeader->inputSizeInBytes);
- pMaterial->sizeInBytes += pHeader->inputSizeInBytes;
-
- pHeader->publicInputCount += 1;
- pHeader->channelsOffset += pHeader->inputSizeInBytes;
- pHeader->propertiesOffset += pHeader->inputSizeInBytes;
-
-
- pMaterial->currentStage = DRMTL_STAGE_PUBLIC_INPUTS;
- return 1;
- }
- }
- }
-
- return 0;
-}
-
-bool drmtl_appendchannel(drmtl_material* pMaterial, drmtl_channel channel)
-{
- if (pMaterial != NULL)
- {
- if (pMaterial->currentStage <= DRMTL_STAGE_CHANNELS)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- if (pHeader != NULL)
- {
- drmtl_channel_header channelHeader;
- channelHeader.channel = channel;
- channelHeader.instructionCount = 0;
-
- if (pMaterial->sizeInBytes + pHeader->channelHeaderSizeInBytes > pMaterial->bufferSizeInBytes)
- {
- if (!_drmtl_inflate(pMaterial))
- {
- // An error occured when trying to inflate the buffer. Might be out of memory.
- return 0;
- }
-
- pHeader = drmtl_getheader(pMaterial);
- assert(pMaterial->sizeInBytes + pHeader->channelHeaderSizeInBytes <= pMaterial->bufferSizeInBytes);
- }
-
-
- memcpy(pMaterial->pRawData + pHeader->propertiesOffset, &channelHeader, pHeader->channelHeaderSizeInBytes);
- pMaterial->currentChannelOffset = pMaterial->sizeInBytes;
- pMaterial->sizeInBytes += pHeader->channelHeaderSizeInBytes;
-
- pHeader->channelCount += 1;
- pHeader->propertiesOffset += pHeader->channelHeaderSizeInBytes;
-
-
- pMaterial->currentStage = DRMTL_STAGE_CHANNELS;
- return 1;
- }
- }
- }
-
- return 0;
-}
-
-bool drmtl_appendinstruction(drmtl_material* pMaterial, drmtl_instruction instruction)
-{
- if (pMaterial != NULL)
- {
- if (pMaterial->currentStage == DRMTL_STAGE_CHANNELS)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- if (pHeader != NULL)
- {
- if (pMaterial->sizeInBytes + pHeader->instructionSizeInBytes > pMaterial->bufferSizeInBytes)
- {
- if (!_drmtl_inflate(pMaterial))
- {
- // An error occured when trying to inflate the buffer. Might be out of memory.
- return 0;
- }
-
- pHeader = drmtl_getheader(pMaterial);
- assert(pMaterial->sizeInBytes + pHeader->instructionSizeInBytes <= pMaterial->bufferSizeInBytes);
- }
-
-
- memcpy(pMaterial->pRawData + pHeader->propertiesOffset, &instruction, pHeader->instructionSizeInBytes);
- pMaterial->sizeInBytes += pHeader->instructionSizeInBytes;
-
- drmtl_channel_header* pChannelHeader = (drmtl_channel_header*)(pMaterial->pRawData + pMaterial->currentChannelOffset);
- if (pChannelHeader != NULL)
- {
- pChannelHeader->instructionCount += 1;
- }
-
- pHeader->propertiesOffset += pHeader->instructionSizeInBytes;
-
-
- return 1;
- }
- }
- }
-
- return 0;
-}
-
-bool drmtl_appendproperty(drmtl_material* pMaterial, drmtl_property prop)
-{
- if (pMaterial != NULL)
- {
- if (pMaterial->currentStage <= DRMTL_STAGE_CHANNELS)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- if (pHeader != NULL)
- {
- if (pMaterial->sizeInBytes + pHeader->propertySizeInBytes > pMaterial->bufferSizeInBytes)
- {
- if (!_drmtl_inflate(pMaterial))
- {
- // An error occured when trying to inflate the buffer. Might be out of memory.
- return 0;
- }
-
- pHeader = drmtl_getheader(pMaterial);
- assert(pMaterial->sizeInBytes + pHeader->propertySizeInBytes <= pMaterial->bufferSizeInBytes);
- }
-
-
- memcpy(pMaterial->pRawData + pMaterial->sizeInBytes, &prop, pHeader->propertySizeInBytes);
- pMaterial->sizeInBytes += pHeader->propertySizeInBytes;
-
- pHeader->propertyCount += 1;
-
-
- pMaterial->currentStage = DRMTL_STAGE_PROPERTIES;
- return 1;
- }
- }
- }
-
- return 0;
-}
-
-
-drmtl_channel_header* drmtl_getchannelheaderbyindex(drmtl_material* pMaterial, unsigned int channelIndex)
-{
- if (pMaterial != NULL)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- assert(pHeader != NULL);
-
- if (channelIndex < pHeader->channelCount)
- {
- drmtl_uint8* pChannelHeader = pMaterial->pRawData + pHeader->channelsOffset;
- for (unsigned int iChannel = 0; iChannel < channelIndex; ++iChannel)
- {
- pChannelHeader += sizeof(drmtl_channel_header) + (sizeof(drmtl_instruction) * ((drmtl_channel_header*)pChannelHeader)->instructionCount);
- }
-
- return (drmtl_channel_header*)pChannelHeader;
- }
- }
-
- return NULL;
-}
-
-drmtl_channel_header* drmtl_getchannelheaderbyname(drmtl_material* pMaterial, const char* channelName)
-{
- if (pMaterial != NULL)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- assert(pHeader != NULL);
-
- drmtl_uint8* pChannelHeader = pMaterial->pRawData + pHeader->channelsOffset;
- for (unsigned int iChannel = 0; iChannel < pHeader->channelCount; ++iChannel)
- {
- if (strcmp(((drmtl_channel_header*)pChannelHeader)->channel.name, channelName) == 0)
- {
- return (drmtl_channel_header*)pChannelHeader;
- }
-
- pChannelHeader += sizeof(drmtl_channel_header) + (sizeof(drmtl_instruction) * ((drmtl_channel_header*)pChannelHeader)->instructionCount);
- }
- }
-
- return NULL;
-}
-
-drmtl_identifier* drmtl_getidentifiers(drmtl_material* pMaterial)
-{
- if (pMaterial != NULL)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- assert(pHeader != NULL);
-
- return (drmtl_identifier*)(pMaterial->pRawData + pHeader->identifiersOffset);
- }
-
- return NULL;
-}
-
-drmtl_identifier* drmtl_getidentifier(drmtl_material* pMaterial, unsigned int index)
-{
- if (pMaterial != NULL)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- assert(pHeader != NULL);
-
- if (index < pHeader->identifierCount)
- {
- drmtl_identifier* firstIdentifier = (drmtl_identifier*)(pMaterial->pRawData + pHeader->identifiersOffset);
- return firstIdentifier + index;
- }
- }
-
- return NULL;
-}
-
-unsigned int drmtl_getidentifiercount(drmtl_material* pMaterial)
-{
- if (pMaterial != NULL)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- assert(pHeader != NULL);
-
- return pHeader->identifierCount;
- }
-
- return 0;
-}
-
-
-unsigned int drmtl_getinputcount(drmtl_material* pMaterial)
-{
- if (pMaterial != NULL)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- assert(pHeader != NULL);
-
- return pHeader->privateInputCount + pHeader->publicInputCount;
- }
-
- return 0;
-}
-
-drmtl_input* drmtl_getinputbyindex(drmtl_material* pMaterial, unsigned int index)
-{
- if (pMaterial != NULL)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- assert(pHeader != NULL);
-
- if (index < (pHeader->privateInputCount + pHeader->publicInputCount))
- {
- drmtl_input* firstInput = (drmtl_input*)(pMaterial->pRawData + pHeader->inputsOffset);
- return firstInput + index;
- }
- }
-
- return NULL;
-}
-
-unsigned int drmtl_getprivateinputcount(drmtl_material* pMaterial)
-{
- if (pMaterial != NULL)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- assert(pHeader != NULL);
-
- return pHeader->privateInputCount;
- }
-
- return 0;
-}
-
-drmtl_input* drmtl_getprivateinputbyindex(drmtl_material* pMaterial, unsigned int index)
-{
- if (pMaterial != NULL)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- assert(pHeader != NULL);
-
- if (index < pHeader->privateInputCount)
- {
- drmtl_input* firstInput = (drmtl_input*)(pMaterial->pRawData + pHeader->inputsOffset);
- return firstInput + index;
- }
- }
-
- return NULL;
-}
-
-unsigned int drmtl_getpublicinputcount(drmtl_material* pMaterial)
-{
- if (pMaterial != NULL)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- assert(pHeader != NULL);
-
- return pHeader->publicInputCount;
- }
-
- return 0;
-}
-
-drmtl_input* drmtl_getpublicinputbyindex(drmtl_material* pMaterial, unsigned int index)
-{
- if (pMaterial != NULL)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- assert(pHeader != NULL);
-
- if (index < pHeader->publicInputCount)
- {
- drmtl_input* firstInput = (drmtl_input*)(pMaterial->pRawData + pHeader->inputsOffset);
- return firstInput + pHeader->privateInputCount + index;
- }
- }
-
- return NULL;
-}
-
-
-unsigned int drmtl_getpropertycount(drmtl_material* pMaterial)
-{
- if (pMaterial != NULL)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- assert(pHeader != NULL);
-
- return pHeader->propertyCount;
- }
-
- return 0;
-}
-
-drmtl_property* drmtl_getpropertybyindex(drmtl_material* pMaterial, unsigned int index)
-{
- if (pMaterial != NULL)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- assert(pHeader != NULL);
-
- if (index < pHeader->propertyCount)
- {
- drmtl_property* firstProperty = (drmtl_property*)(pMaterial->pRawData + pHeader->propertiesOffset);
- return firstProperty + index;
- }
- }
-
- return NULL;
-}
-
-drmtl_property* drmtl_getpropertybyname(drmtl_material* pMaterial, const char* name)
-{
- if (pMaterial != NULL)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- assert(pHeader != NULL);
-
- for (unsigned int i = 0; i < pHeader->propertyCount; ++i)
- {
- drmtl_property* pProperty = ((drmtl_property*)(pMaterial->pRawData + pHeader->propertiesOffset)) + i;
- assert(pProperty != NULL);
-
- if (strcmp(pProperty->name, name) == 0)
- {
- return pProperty;
- }
- }
- }
-
- return NULL;
-}
-
-
-//////////////////////////////////
-// Private low-level API.
-
-bool _drmtl_inflate(drmtl_material* pMaterial)
-{
- assert(pMaterial != NULL);
-
- drmtl_uint8* pOldBuffer = pMaterial->pRawData;
- drmtl_uint8* pNewBuffer = (drmtl_uint8*)malloc(pMaterial->bufferSizeInBytes + DRMTL_CHUNK_SIZE);
- if (pNewBuffer != NULL)
- {
- memcpy(pNewBuffer, pOldBuffer, pMaterial->sizeInBytes);
- pMaterial->pRawData = pNewBuffer;
- pMaterial->bufferSizeInBytes += DRMTL_CHUNK_SIZE;
-
- free(pOldBuffer);
- return 1;
- }
-
- return 0;
-}
-
-
-
-////////////////////////////////////////////////////////
-// Mid-Level APIs
-
-drmtl_identifier drmtl_identifier_float(const char* name)
-{
- drmtl_identifier identifier;
- identifier.type = drmtl_type_float;
- drmtl_strcpy(identifier.name, DRMTL_MAX_IDENTIFIER_NAME, name);
-
- return identifier;
-}
-
-drmtl_identifier drmtl_identifier_float2(const char* name)
-{
- drmtl_identifier identifier;
- identifier.type = drmtl_type_float2;
- drmtl_strcpy(identifier.name, DRMTL_MAX_IDENTIFIER_NAME, name);
-
- return identifier;
-}
-
-drmtl_identifier drmtl_identifier_float3(const char* name)
-{
- drmtl_identifier identifier;
- identifier.type = drmtl_type_float3;
- drmtl_strcpy(identifier.name, DRMTL_MAX_IDENTIFIER_NAME, name);
-
- return identifier;
-}
-
-drmtl_identifier drmtl_identifier_float4(const char* name)
-{
- drmtl_identifier identifier;
- identifier.type = drmtl_type_float4;
- drmtl_strcpy(identifier.name, DRMTL_MAX_IDENTIFIER_NAME, name);
-
- return identifier;
-}
-
-drmtl_identifier drmtl_identifier_int(const char* name)
-{
- drmtl_identifier identifier;
- identifier.type = drmtl_type_int;
- drmtl_strcpy(identifier.name, DRMTL_MAX_IDENTIFIER_NAME, name);
-
- return identifier;
-}
-
-drmtl_identifier drmtl_identifier_int2(const char* name)
-{
- drmtl_identifier identifier;
- identifier.type = drmtl_type_int2;
- drmtl_strcpy(identifier.name, DRMTL_MAX_IDENTIFIER_NAME, name);
-
- return identifier;
-}
-
-drmtl_identifier drmtl_identifier_int3(const char* name)
-{
- drmtl_identifier identifier;
- identifier.type = drmtl_type_int3;
- drmtl_strcpy(identifier.name, DRMTL_MAX_IDENTIFIER_NAME, name);
-
- return identifier;
-}
-
-drmtl_identifier drmtl_identifier_int4(const char* name)
-{
- drmtl_identifier identifier;
- identifier.type = drmtl_type_int4;
- drmtl_strcpy(identifier.name, DRMTL_MAX_IDENTIFIER_NAME, name);
-
- return identifier;
-}
-
-drmtl_identifier drmtl_identifier_tex2d(const char* name)
-{
- drmtl_identifier identifier;
- identifier.type = drmtl_type_tex2d;
- drmtl_strcpy(identifier.name, DRMTL_MAX_IDENTIFIER_NAME, name);
-
- return identifier;
-}
-
-
-drmtl_input drmtl_input_float(unsigned int identifierIndex, float x)
-{
- drmtl_input input;
- input.identifierIndex = identifierIndex;
- input.f1.x = x;
-
- return input;
-}
-
-drmtl_input drmtl_input_float2(unsigned int identifierIndex, float x, float y)
-{
- drmtl_input input;
- input.identifierIndex = identifierIndex;
- input.f2.x = x;
- input.f2.y = y;
-
- return input;
-}
-
-drmtl_input drmtl_input_float3(unsigned int identifierIndex, float x, float y, float z)
-{
- drmtl_input input;
- input.identifierIndex = identifierIndex;
- input.f3.x = x;
- input.f3.y = y;
- input.f3.z = z;
-
- return input;
-}
-
-drmtl_input drmtl_input_float4(unsigned int identifierIndex, float x, float y, float z, float w)
-{
- drmtl_input input;
- input.identifierIndex = identifierIndex;
- input.f4.x = x;
- input.f4.y = y;
- input.f4.z = z;
- input.f4.w = w;
-
- return input;
-}
-
-drmtl_input drmtl_input_int(unsigned int identifierIndex, int x)
-{
- drmtl_input input;
- input.identifierIndex = identifierIndex;
- input.i1.x = x;
-
- return input;
-}
-
-drmtl_input drmtl_input_int2(unsigned int identifierIndex, int x, int y)
-{
- drmtl_input input;
- input.identifierIndex = identifierIndex;
- input.i2.x = x;
- input.i2.y = y;
-
- return input;
-}
-
-drmtl_input drmtl_input_int3(unsigned int identifierIndex, int x, int y, int z)
-{
- drmtl_input input;
- input.identifierIndex = identifierIndex;
- input.i3.x = x;
- input.i3.y = y;
- input.i3.z = z;
-
- return input;
-}
-
-drmtl_input drmtl_input_int4(unsigned int identifierIndex, int x, int y, int z, int w)
-{
- drmtl_input input;
- input.identifierIndex = identifierIndex;
- input.i4.x = x;
- input.i4.y = y;
- input.i4.z = z;
- input.i4.w = w;
-
- return input;
-}
-
-drmtl_input drmtl_input_tex(unsigned int identifierIndex, const char* path)
-{
- drmtl_input input;
- input.identifierIndex = identifierIndex;
- drmtl_strcpy(input.path.value, DRMTL_MAX_INPUT_PATH, path);
-
- return input;
-}
-
-
-drmtl_channel drmtl_channel_float(const char* name)
-{
- drmtl_channel channel;
- channel.type = drmtl_type_float;
- drmtl_strcpy(channel.name, DRMTL_MAX_CHANNEL_NAME, name);
-
- return channel;
-}
-
-drmtl_channel drmtl_channel_float2(const char* name)
-{
- drmtl_channel channel;
- channel.type = drmtl_type_float2;
- drmtl_strcpy(channel.name, DRMTL_MAX_CHANNEL_NAME, name);
-
- return channel;
-}
-
-drmtl_channel drmtl_channel_float3(const char* name)
-{
- drmtl_channel channel;
- channel.type = drmtl_type_float3;
- drmtl_strcpy(channel.name, DRMTL_MAX_CHANNEL_NAME, name);
-
- return channel;
-}
-
-drmtl_channel drmtl_channel_float4(const char* name)
-{
- drmtl_channel channel;
- channel.type = drmtl_type_float4;
- drmtl_strcpy(channel.name, DRMTL_MAX_CHANNEL_NAME, name);
-
- return channel;
-}
-
-drmtl_channel drmtl_channel_int(const char* name)
-{
- drmtl_channel channel;
- channel.type = drmtl_type_int;
- drmtl_strcpy(channel.name, DRMTL_MAX_CHANNEL_NAME, name);
-
- return channel;
-}
-
-drmtl_channel drmtl_channel_int2(const char* name)
-{
- drmtl_channel channel;
- channel.type = drmtl_type_int2;
- drmtl_strcpy(channel.name, DRMTL_MAX_CHANNEL_NAME, name);
-
- return channel;
-}
-
-drmtl_channel drmtl_channel_int3(const char* name)
-{
- drmtl_channel channel;
- channel.type = drmtl_type_int3;
- drmtl_strcpy(channel.name, DRMTL_MAX_CHANNEL_NAME, name);
-
- return channel;
-}
-
-drmtl_channel drmtl_channel_int4(const char* name)
-{
- drmtl_channel channel;
- channel.type = drmtl_type_int4;
- drmtl_strcpy(channel.name, DRMTL_MAX_CHANNEL_NAME, name);
-
- return channel;
-}
-
-
-drmtl_instruction drmtl_movf1_v1(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_movf1;
- inst.mov.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.mov.inputX.id = inputIdentifierIndex;
- inst.mov.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_movf1_c1(unsigned int outputIdentifierIndex, float x)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_movf1;
- inst.mov.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.mov.inputX.valuef = x;
- inst.mov.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_movf2_v2(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_movf2;
- inst.mov.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.mov.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.mov.inputX.id = inputIdentifierIndex;
- inst.mov.inputY.id = inputIdentifierIndex;
- inst.mov.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_movf2_c2(unsigned int outputIdentifierIndex, float x, float y)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_movf2;
- inst.mov.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.mov.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.mov.inputX.valuef = x;
- inst.mov.inputY.valuef = y;
- inst.mov.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_movf3_v3(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_movf3;
- inst.mov.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.mov.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.mov.inputDesc.z = DRMTL_INPUT_DESC_VARZ;
- inst.mov.inputX.id = inputIdentifierIndex;
- inst.mov.inputY.id = inputIdentifierIndex;
- inst.mov.inputZ.id = inputIdentifierIndex;
- inst.mov.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_movf3_c3(unsigned int outputIdentifierIndex, float x, float y, float z)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_movf3;
- inst.mov.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.mov.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.mov.inputDesc.z = DRMTL_INPUT_DESC_CONSTF;
- inst.mov.inputX.valuef = x;
- inst.mov.inputY.valuef = y;
- inst.mov.inputZ.valuef = z;
- inst.mov.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_movf4_v4(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_movf4;
- inst.mov.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.mov.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.mov.inputDesc.z = DRMTL_INPUT_DESC_VARZ;
- inst.mov.inputDesc.w = DRMTL_INPUT_DESC_VARW;
- inst.mov.inputX.id = inputIdentifierIndex;
- inst.mov.inputY.id = inputIdentifierIndex;
- inst.mov.inputZ.id = inputIdentifierIndex;
- inst.mov.inputW.id = inputIdentifierIndex;
- inst.mov.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_movf4_c4(unsigned int outputIdentifierIndex, float x, float y, float z, float w)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_movf4;
- inst.mov.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.mov.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.mov.inputDesc.z = DRMTL_INPUT_DESC_CONSTF;
- inst.mov.inputDesc.w = DRMTL_INPUT_DESC_CONSTF;
- inst.mov.inputX.valuef = x;
- inst.mov.inputY.valuef = y;
- inst.mov.inputZ.valuef = z;
- inst.mov.inputW.valuef = w;
- inst.mov.output = outputIdentifierIndex;
-
- return inst;
-}
-
-
-drmtl_instruction drmtl_addf1_v1(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_addf1;
- inst.add.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.add.inputX.id = inputIdentifierIndex;
- inst.add.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_addf1_c1(unsigned int outputIdentifierIndex, float x)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_addf1;
- inst.add.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.add.inputX.valuef = x;
- inst.add.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_addf2_v2(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_addf2;
- inst.add.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.add.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.add.inputX.id = inputIdentifierIndex;
- inst.add.inputY.id = inputIdentifierIndex;
- inst.add.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_addf2_c2(unsigned int outputIdentifierIndex, float x, float y)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_addf2;
- inst.add.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.add.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.add.inputX.valuef = x;
- inst.add.inputY.valuef = y;
- inst.add.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_addf3_v3(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_addf3;
- inst.add.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.add.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.add.inputDesc.z = DRMTL_INPUT_DESC_VARZ;
- inst.add.inputX.id = inputIdentifierIndex;
- inst.add.inputY.id = inputIdentifierIndex;
- inst.add.inputZ.id = inputIdentifierIndex;
- inst.add.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_addf3_c3(unsigned int outputIdentifierIndex, float x, float y, float z)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_addf3;
- inst.add.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.add.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.add.inputDesc.z = DRMTL_INPUT_DESC_CONSTF;
- inst.add.inputX.valuef = x;
- inst.add.inputY.valuef = y;
- inst.add.inputZ.valuef = z;
- inst.add.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_addf4_v4(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_addf4;
- inst.add.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.add.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.add.inputDesc.z = DRMTL_INPUT_DESC_VARZ;
- inst.add.inputDesc.w = DRMTL_INPUT_DESC_VARW;
- inst.add.inputX.id = inputIdentifierIndex;
- inst.add.inputY.id = inputIdentifierIndex;
- inst.add.inputZ.id = inputIdentifierIndex;
- inst.add.inputW.id = inputIdentifierIndex;
- inst.add.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_addf4_c4(unsigned int outputIdentifierIndex, float x, float y, float z, float w)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_addf4;
- inst.add.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.add.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.add.inputDesc.z = DRMTL_INPUT_DESC_CONSTF;
- inst.add.inputDesc.w = DRMTL_INPUT_DESC_CONSTF;
- inst.add.inputX.valuef = x;
- inst.add.inputY.valuef = y;
- inst.add.inputZ.valuef = z;
- inst.add.inputW.valuef = w;
- inst.add.output = outputIdentifierIndex;
-
- return inst;
-}
-
-
-drmtl_instruction drmtl_subf1_v1(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_subf1;
- inst.sub.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.sub.inputX.id = inputIdentifierIndex;
- inst.sub.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_subf1_c1(unsigned int outputIdentifierIndex, float x)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_subf1;
- inst.sub.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.sub.inputX.valuef = x;
- inst.sub.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_subf2_v2(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_subf2;
- inst.sub.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.sub.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.sub.inputX.id = inputIdentifierIndex;
- inst.sub.inputY.id = inputIdentifierIndex;
- inst.sub.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_subf2_c2(unsigned int outputIdentifierIndex, float x, float y)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_subf2;
- inst.sub.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.sub.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.sub.inputX.valuef = x;
- inst.sub.inputY.valuef = y;
- inst.sub.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_subf3_v3(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_subf3;
- inst.sub.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.sub.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.sub.inputDesc.z = DRMTL_INPUT_DESC_VARZ;
- inst.sub.inputX.id = inputIdentifierIndex;
- inst.sub.inputY.id = inputIdentifierIndex;
- inst.sub.inputZ.id = inputIdentifierIndex;
- inst.sub.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_subf3_c3(unsigned int outputIdentifierIndex, float x, float y, float z)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_subf3;
- inst.sub.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.sub.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.sub.inputDesc.z = DRMTL_INPUT_DESC_CONSTF;
- inst.sub.inputX.valuef = x;
- inst.sub.inputY.valuef = y;
- inst.sub.inputZ.valuef = z;
- inst.sub.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_subf4_v4(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_subf4;
- inst.sub.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.sub.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.sub.inputDesc.z = DRMTL_INPUT_DESC_VARZ;
- inst.sub.inputDesc.w = DRMTL_INPUT_DESC_VARW;
- inst.sub.inputX.id = inputIdentifierIndex;
- inst.sub.inputY.id = inputIdentifierIndex;
- inst.sub.inputZ.id = inputIdentifierIndex;
- inst.sub.inputW.id = inputIdentifierIndex;
- inst.sub.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_subf4_c4(unsigned int outputIdentifierIndex, float x, float y, float z, float w)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_subf4;
- inst.sub.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.sub.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.sub.inputDesc.z = DRMTL_INPUT_DESC_CONSTF;
- inst.sub.inputDesc.w = DRMTL_INPUT_DESC_CONSTF;
- inst.sub.inputX.valuef = x;
- inst.sub.inputY.valuef = y;
- inst.sub.inputZ.valuef = z;
- inst.sub.inputW.valuef = w;
- inst.sub.output = outputIdentifierIndex;
-
- return inst;
-}
-
-
-drmtl_instruction drmtl_mulf1_v1(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_mulf1;
- inst.mul.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.mul.inputX.id = inputIdentifierIndex;
- inst.mul.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_mulf1_c1(unsigned int outputIdentifierIndex, float x)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_mulf1;
- inst.mul.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.mul.inputX.valuef = x;
- inst.mul.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_mulf2_v2(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_mulf2;
- inst.mul.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.mul.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.mul.inputX.id = inputIdentifierIndex;
- inst.mul.inputY.id = inputIdentifierIndex;
- inst.mul.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_mulf2_c2(unsigned int outputIdentifierIndex, float x, float y)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_mulf2;
- inst.mul.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.mul.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.mul.inputX.valuef = x;
- inst.mul.inputY.valuef = y;
- inst.mul.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_mulf3_v3(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_mulf3;
- inst.mul.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.mul.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.mul.inputDesc.z = DRMTL_INPUT_DESC_VARZ;
- inst.mul.inputX.id = inputIdentifierIndex;
- inst.mul.inputY.id = inputIdentifierIndex;
- inst.mul.inputZ.id = inputIdentifierIndex;
- inst.mul.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_mulf3_c3(unsigned int outputIdentifierIndex, float x, float y, float z)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_mulf3;
- inst.mul.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.mul.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.mul.inputDesc.z = DRMTL_INPUT_DESC_CONSTF;
- inst.mul.inputX.valuef = x;
- inst.mul.inputY.valuef = y;
- inst.mul.inputZ.valuef = z;
- inst.mul.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_mulf4_v4(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_mulf4;
- inst.mul.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.mul.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.mul.inputDesc.z = DRMTL_INPUT_DESC_VARZ;
- inst.mul.inputDesc.w = DRMTL_INPUT_DESC_VARW;
- inst.mul.inputX.id = inputIdentifierIndex;
- inst.mul.inputY.id = inputIdentifierIndex;
- inst.mul.inputZ.id = inputIdentifierIndex;
- inst.mul.inputW.id = inputIdentifierIndex;
- inst.mul.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_mulf4_c4(unsigned int outputIdentifierIndex, float x, float y, float z, float w)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_mulf4;
- inst.mul.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.mul.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.mul.inputDesc.z = DRMTL_INPUT_DESC_CONSTF;
- inst.mul.inputDesc.w = DRMTL_INPUT_DESC_CONSTF;
- inst.mul.inputX.valuef = x;
- inst.mul.inputY.valuef = y;
- inst.mul.inputZ.valuef = z;
- inst.mul.inputW.valuef = w;
- inst.mul.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_mulf4_v3v1(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndexXYZ, unsigned int inputIdentifierIndexW)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_mulf4;
- inst.mul.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.mul.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.mul.inputDesc.z = DRMTL_INPUT_DESC_VARZ;
- inst.mul.inputDesc.w = DRMTL_INPUT_DESC_VARX;
- inst.mul.inputX.id = inputIdentifierIndexXYZ;
- inst.mul.inputY.id = inputIdentifierIndexXYZ;
- inst.mul.inputZ.id = inputIdentifierIndexXYZ;
- inst.mul.inputW.id = inputIdentifierIndexW;
- inst.mul.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_mulf4_v3c1(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex, float w)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_mulf4;
- inst.mul.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.mul.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.mul.inputDesc.z = DRMTL_INPUT_DESC_VARZ;
- inst.mul.inputDesc.w = DRMTL_INPUT_DESC_CONSTF;
- inst.mul.inputX.id = inputIdentifierIndex;
- inst.mul.inputY.id = inputIdentifierIndex;
- inst.mul.inputZ.id = inputIdentifierIndex;
- inst.mul.inputW.valuef = w;
- inst.mul.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_mulf4_v2c2(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex, float z, float w)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_mulf4;
- inst.mul.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.mul.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.mul.inputDesc.z = DRMTL_INPUT_DESC_CONSTF;
- inst.mul.inputDesc.w = DRMTL_INPUT_DESC_CONSTF;
- inst.mul.inputX.id = inputIdentifierIndex;
- inst.mul.inputY.id = inputIdentifierIndex;
- inst.mul.inputZ.valuef = z;
- inst.mul.inputW.valuef = w;
- inst.mul.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_mulf4_v1c3(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex, float y, float z, float w)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_mulf4;
- inst.mul.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.mul.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.mul.inputDesc.z = DRMTL_INPUT_DESC_CONSTF;
- inst.mul.inputDesc.w = DRMTL_INPUT_DESC_CONSTF;
- inst.mul.inputX.id = inputIdentifierIndex;
- inst.mul.inputY.valuef = y;
- inst.mul.inputZ.valuef = z;
- inst.mul.inputW.valuef = w;
- inst.mul.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_mulf4_v1v1v1v1(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndexX, unsigned int inputIdentifierIndexY, unsigned int inputIdentifierIndexZ, unsigned int inputIdentifierIndexW)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_mulf4;
- inst.mul.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.mul.inputDesc.y = DRMTL_INPUT_DESC_VARX;
- inst.mul.inputDesc.z = DRMTL_INPUT_DESC_VARX;
- inst.mul.inputDesc.w = DRMTL_INPUT_DESC_VARX;
- inst.mul.inputX.id = inputIdentifierIndexX;
- inst.mul.inputY.id = inputIdentifierIndexY;
- inst.mul.inputZ.id = inputIdentifierIndexZ;
- inst.mul.inputW.id = inputIdentifierIndexW;
- inst.mul.output = outputIdentifierIndex;
-
- return inst;
-}
-
-
-drmtl_instruction drmtl_divf1_v1(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_divf1;
- inst.div.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.div.inputX.id = inputIdentifierIndex;
- inst.div.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_divf1_c1(unsigned int outputIdentifierIndex, float x)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_divf1;
- inst.div.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.div.inputX.valuef = x;
- inst.div.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_divf2_v2(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_divf2;
- inst.div.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.div.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.div.inputX.id = inputIdentifierIndex;
- inst.div.inputY.id = inputIdentifierIndex;
- inst.div.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_divf2_c2(unsigned int outputIdentifierIndex, float x, float y)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_divf2;
- inst.div.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.div.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.div.inputX.valuef = x;
- inst.div.inputY.valuef = y;
- inst.div.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_divf3_v3(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_divf3;
- inst.div.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.div.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.div.inputDesc.z = DRMTL_INPUT_DESC_VARZ;
- inst.div.inputX.id = inputIdentifierIndex;
- inst.div.inputY.id = inputIdentifierIndex;
- inst.div.inputZ.id = inputIdentifierIndex;
- inst.div.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_divf3_c3(unsigned int outputIdentifierIndex, float x, float y, float z)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_divf3;
- inst.div.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.div.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.div.inputDesc.z = DRMTL_INPUT_DESC_CONSTF;
- inst.div.inputX.valuef = x;
- inst.div.inputY.valuef = y;
- inst.div.inputZ.valuef = z;
- inst.div.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_divf4_v4(unsigned int outputIdentifierIndex, unsigned int inputIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_divf4;
- inst.div.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.div.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.div.inputDesc.z = DRMTL_INPUT_DESC_VARZ;
- inst.div.inputDesc.w = DRMTL_INPUT_DESC_VARW;
- inst.div.inputX.id = inputIdentifierIndex;
- inst.div.inputY.id = inputIdentifierIndex;
- inst.div.inputZ.id = inputIdentifierIndex;
- inst.div.inputW.id = inputIdentifierIndex;
- inst.div.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_divf4_c4(unsigned int outputIdentifierIndex, float x, float y, float z, float w)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_divf4;
- inst.div.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.div.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.div.inputDesc.z = DRMTL_INPUT_DESC_CONSTF;
- inst.div.inputDesc.w = DRMTL_INPUT_DESC_CONSTF;
- inst.div.inputX.valuef = x;
- inst.div.inputY.valuef = y;
- inst.div.inputZ.valuef = z;
- inst.div.inputW.valuef = w;
- inst.div.output = outputIdentifierIndex;
-
- return inst;
-}
-
-
-drmtl_instruction drmtl_tex2(unsigned int outputIdentifierIndex, unsigned int textureIdentifierIndex, unsigned int texcoordIdentifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_tex2;
- inst.tex.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.tex.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.tex.inputX.id = texcoordIdentifierIndex;
- inst.tex.inputY.id = texcoordIdentifierIndex;
- inst.tex.texture = textureIdentifierIndex;
- inst.tex.output = outputIdentifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_var(unsigned int identifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_var;
- inst.var.identifierIndex = identifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_retf1(unsigned int identifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_retf1;
- inst.ret.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.ret.inputX.id = identifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_retf2(unsigned int identifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_retf2;
- inst.ret.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.ret.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.ret.inputX.id = identifierIndex;
- inst.ret.inputY.id = identifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_retf3(unsigned int identifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_retf3;
- inst.ret.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.ret.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.ret.inputDesc.z = DRMTL_INPUT_DESC_VARZ;
- inst.ret.inputX.id = identifierIndex;
- inst.ret.inputY.id = identifierIndex;
- inst.ret.inputZ.id = identifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_retf4(unsigned int identifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_retf4;
- inst.ret.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.ret.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.ret.inputDesc.z = DRMTL_INPUT_DESC_VARZ;
- inst.ret.inputDesc.w = DRMTL_INPUT_DESC_VARW;
- inst.ret.inputX.id = identifierIndex;
- inst.ret.inputY.id = identifierIndex;
- inst.ret.inputZ.id = identifierIndex;
- inst.ret.inputW.id = identifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_retf1_c1(float x)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_retf1;
- inst.ret.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.ret.inputX.valuef = x;
-
- return inst;
-}
-
-drmtl_instruction drmtl_retf2_c2(float x, float y)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_retf2;
- inst.ret.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.ret.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.ret.inputX.valuef = x;
- inst.ret.inputY.valuef = y;
-
- return inst;
-}
-
-drmtl_instruction drmtl_retf3_c3(float x, float y, float z)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_retf3;
- inst.ret.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.ret.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.ret.inputDesc.z = DRMTL_INPUT_DESC_CONSTF;
- inst.ret.inputX.valuef = x;
- inst.ret.inputY.valuef = y;
- inst.ret.inputZ.valuef = z;
-
- return inst;
-}
-
-drmtl_instruction drmtl_retf4_c4(float x, float y, float z, float w)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_retf4;
- inst.ret.inputDesc.x = DRMTL_INPUT_DESC_CONSTF;
- inst.ret.inputDesc.y = DRMTL_INPUT_DESC_CONSTF;
- inst.ret.inputDesc.z = DRMTL_INPUT_DESC_CONSTF;
- inst.ret.inputDesc.w = DRMTL_INPUT_DESC_CONSTF;
- inst.ret.inputX.valuef = x;
- inst.ret.inputY.valuef = y;
- inst.ret.inputZ.valuef = z;
- inst.ret.inputW.valuef = w;
-
- return inst;
-}
-
-drmtl_instruction drmtl_reti1(unsigned int identifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_reti1;
- inst.ret.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.ret.inputX.id = identifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_reti2(unsigned int identifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_reti2;
- inst.ret.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.ret.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.ret.inputX.id = identifierIndex;
- inst.ret.inputY.id = identifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_reti3(unsigned int identifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_reti3;
- inst.ret.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.ret.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.ret.inputDesc.z = DRMTL_INPUT_DESC_VARZ;
- inst.ret.inputX.id = identifierIndex;
- inst.ret.inputY.id = identifierIndex;
- inst.ret.inputZ.id = identifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_reti4(unsigned int identifierIndex)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_reti4;
- inst.ret.inputDesc.x = DRMTL_INPUT_DESC_VARX;
- inst.ret.inputDesc.y = DRMTL_INPUT_DESC_VARY;
- inst.ret.inputDesc.z = DRMTL_INPUT_DESC_VARZ;
- inst.ret.inputDesc.w = DRMTL_INPUT_DESC_VARW;
- inst.ret.inputX.id = identifierIndex;
- inst.ret.inputY.id = identifierIndex;
- inst.ret.inputZ.id = identifierIndex;
- inst.ret.inputW.id = identifierIndex;
-
- return inst;
-}
-
-drmtl_instruction drmtl_reti1_c1(int x)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_reti1;
- inst.ret.inputDesc.x = DRMTL_INPUT_DESC_CONSTI;
- inst.ret.inputX.valuei = x;
-
- return inst;
-}
-
-drmtl_instruction drmtl_reti2_c2(int x, int y)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_reti2;
- inst.ret.inputDesc.x = DRMTL_INPUT_DESC_CONSTI;
- inst.ret.inputDesc.y = DRMTL_INPUT_DESC_CONSTI;
- inst.ret.inputX.valuei = x;
- inst.ret.inputY.valuei = y;
-
- return inst;
-}
-
-drmtl_instruction drmtl_reti3_c3(int x, int y, int z)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_reti3;
- inst.ret.inputDesc.x = DRMTL_INPUT_DESC_CONSTI;
- inst.ret.inputDesc.y = DRMTL_INPUT_DESC_CONSTI;
- inst.ret.inputDesc.z = DRMTL_INPUT_DESC_CONSTI;
- inst.ret.inputX.valuei = x;
- inst.ret.inputY.valuei = y;
- inst.ret.inputZ.valuei = z;
-
- return inst;
-}
-
-drmtl_instruction drmtl_reti4_c4(int x, int y, int z, int w)
-{
- drmtl_instruction inst;
- inst.opcode = drmtl_opcode_reti4;
- inst.ret.inputDesc.x = DRMTL_INPUT_DESC_CONSTI;
- inst.ret.inputDesc.y = DRMTL_INPUT_DESC_CONSTI;
- inst.ret.inputDesc.z = DRMTL_INPUT_DESC_CONSTI;
- inst.ret.inputDesc.w = DRMTL_INPUT_DESC_CONSTI;
- inst.ret.inputX.valuei = x;
- inst.ret.inputY.valuei = y;
- inst.ret.inputZ.valuei = z;
- inst.ret.inputW.valuei = w;
-
- return inst;
-}
-
-
-
-drmtl_property drmtl_property_float(const char* name, float x)
-{
- drmtl_property prop;
- prop.type = drmtl_type_float;
- drmtl_strcpy(prop.name, DRMTL_MAX_PROPERTY_NAME, name);
- prop.f1.x = x;
-
- return prop;
-}
-
-drmtl_property drmtl_property_float2(const char* name, float x, float y)
-{
- drmtl_property prop;
- prop.type = drmtl_type_float2;
- drmtl_strcpy(prop.name, DRMTL_MAX_PROPERTY_NAME, name);
- prop.f2.x = x;
- prop.f2.y = y;
-
- return prop;
-}
-
-drmtl_property drmtl_property_float3(const char* name, float x, float y, float z)
-{
- drmtl_property prop;
- prop.type = drmtl_type_float3;
- drmtl_strcpy(prop.name, DRMTL_MAX_PROPERTY_NAME, name);
- prop.f3.x = x;
- prop.f3.y = y;
- prop.f3.z = z;
-
- return prop;
-}
-
-drmtl_property drmtl_property_float4(const char* name, float x, float y, float z, float w)
-{
- drmtl_property prop;
- prop.type = drmtl_type_float4;
- drmtl_strcpy(prop.name, DRMTL_MAX_PROPERTY_NAME, name);
- prop.f4.x = x;
- prop.f4.y = y;
- prop.f4.z = z;
- prop.f4.w = w;
-
- return prop;
-}
-
-drmtl_property drmtl_property_int(const char* name, int x)
-{
- drmtl_property prop;
- prop.type = drmtl_type_int;
- drmtl_strcpy(prop.name, DRMTL_MAX_PROPERTY_NAME, name);
- prop.i1.x = x;
-
- return prop;
-}
-
-drmtl_property drmtl_property_int2(const char* name, int x, int y)
-{
- drmtl_property prop;
- prop.type = drmtl_type_int2;
- drmtl_strcpy(prop.name, DRMTL_MAX_PROPERTY_NAME, name);
- prop.i2.x = x;
- prop.i2.y = y;
-
- return prop;
-}
-
-drmtl_property drmtl_property_int3(const char* name, int x, int y, int z)
-{
- drmtl_property prop;
- prop.type = drmtl_type_int3;
- drmtl_strcpy(prop.name, DRMTL_MAX_PROPERTY_NAME, name);
- prop.i3.x = x;
- prop.i3.y = y;
- prop.i3.z = z;
-
- return prop;
-}
-
-drmtl_property drmtl_property_int4(const char* name, int x, int y, int z, int w)
-{
- drmtl_property prop;
- prop.type = drmtl_type_int4;
- drmtl_strcpy(prop.name, DRMTL_MAX_PROPERTY_NAME, name);
- prop.i4.x = x;
- prop.i4.y = y;
- prop.i4.z = z;
- prop.i4.w = w;
-
- return prop;
-}
-
-drmtl_property drmtl_property_bool(const char* name, bool value)
-{
- drmtl_property prop;
- prop.type = drmtl_type_bool;
- drmtl_strcpy(prop.name, DRMTL_MAX_PROPERTY_NAME, name);
- prop.b1.x = value;
-
- return prop;
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-//
-// Compilers
-//
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-#ifndef DRMTL_NO_MTL_COMPILER
-typedef struct
-{
- /// A pointer to the buffer containing the Wavefront MTL data.
- const char* pData;
-
- /// The size of the data buffer size.
- unsigned int dataSizeInBytes;
-
- /// A pointer to the next bytes to read.
- const char* pDataCur;
-
- /// A pointer to the end of the buffer.
- const char* pDataEnd;
-
-
- /// The diffuse colour.
- float diffuse[3];
-
- /// The diffuse map.
- char diffuseMap[DRMTL_MAX_INPUT_PATH];
-
-
- /// The specular colour.
- float specular[3];
-
- /// The specular map.
- char specularMap[DRMTL_MAX_INPUT_PATH];
-
-
- /// The specular exponent.
- float specularExponent;
-
- /// The specular exponent map.
- char specularExponentMap[DRMTL_MAX_INPUT_PATH];
-
-
- /// The alpha transparency value.
- float alpha;
-
- /// The alpha transparency map.
- char alphaMap[DRMTL_MAX_INPUT_PATH];
-
-
-} drmtl_wavefront;
-
-bool drmtl_wavefront_is_whitespace(char c)
-{
- return c == ' ' || c == '\t';
-}
-
-bool drmtl_wavefront_is_valid_digit(char c)
-{
- return c >= '0' && c <= '9';
-}
-
-bool drmtl_wavefront_atof(const char* str, const char* strEnd, const char** strEndOut, float* valueOut)
-{
- // Skip leading whitespace.
- while (str < strEnd && drmtl_wavefront_is_whitespace(*str))
- {
- str += 1;
- }
-
-
- // Check that we have a string after moving the whitespace.
- if (str < strEnd)
- {
- float sign = 1.0f;
- float value = 0.0f;
-
- // Sign.
- if (*str == '-')
- {
- sign = -1.0f;
- str += 1;
- }
- else if (*str == '+')
- {
- sign = 1.0f;
- str += 1;
- }
-
-
- // Digits before the decimal point.
- while (str < strEnd && drmtl_wavefront_is_valid_digit(*str))
- {
- value = value * 10.0f + (*str - '0');
-
- str += 1;
- }
-
- // Digits after the decimal point.
- if (*str == '.')
- {
- float pow10 = 10.0f;
-
- str += 1;
- while (str < strEnd && drmtl_wavefront_is_valid_digit(*str))
- {
- value += (*str - '0') / pow10;
- pow10 *= 10.0f;
-
- str += 1;
- }
- }
-
-
- if (strEndOut != NULL)
- {
- *strEndOut = str;
- }
-
- if (valueOut != NULL)
- {
- *valueOut = sign * value;
- }
-
- return 1;
- }
- else
- {
- // Empty string. Leave output untouched and return 0.
- return 0;
- }
-}
-
-bool drmtl_wavefront_atof_3(const char* str, const char* strEnd, const char** strEndOut, float valueOut[3])
-{
- float value[3];
- if (drmtl_wavefront_atof(str, strEnd, &str, &value[0]))
- {
- value[1] = value[0];
- value[2] = value[0];
-
- if (drmtl_wavefront_atof(str, strEnd, &str, &value[1]))
- {
- // We got two numbers which means we must have the third for this to be successful.
- if (!drmtl_wavefront_atof(str, strEnd, strEndOut, &value[2]))
- {
- // Failed to get the third number. We only found 2 which is not valid. Error.
- return 0;
- }
- }
-
-
- valueOut[0] = value[0];
- valueOut[1] = value[1];
- valueOut[2] = value[2];
-
- return 1;
- }
-
- return 0;
-}
-
-const char* drmtl_wavefront_find_end_of_line(const char* pDataCur, const char* pDataEnd)
-{
- assert(pDataCur != NULL);
- assert(pDataEnd != NULL);
-
- while (pDataCur < pDataEnd)
- {
- if (pDataCur[0] == '\n')
- {
- return pDataCur;
- }
- else
- {
- if (pDataCur + 1 < pDataEnd)
- {
- if (pDataCur[0] == '\r' && pDataCur[1] == '\n')
- {
- return pDataCur;
- }
- }
- }
-
- pDataCur += 1;
- }
-
- // If we get here it means we hit the end of the file before find a new-line character.
- return pDataEnd;
-}
-
-const char* drmtl_wavefront_find_next_line(const char* pDataCur, const char* pDataEnd)
-{
- assert(pDataCur != NULL);
- assert(pDataEnd != NULL);
-
- pDataCur = drmtl_wavefront_find_end_of_line(pDataCur, pDataEnd);
- if (pDataCur != NULL)
- {
- if (pDataCur < pDataEnd)
- {
- if (pDataCur[0] == '\n')
- {
- return pDataCur + 1;
- }
- else
- {
- if (pDataCur + 1 < pDataEnd)
- {
- if (pDataCur[0] == '\r' && pDataCur[1] == '\n')
- {
- return pDataCur + 2;
- }
- }
- }
- }
- }
-
- return NULL;
-}
-
-const char* drmtl_wavefront_find_next_newmtl(const char* pDataCur, const char* pDataEnd)
-{
- assert(pDataCur != NULL);
- assert(pDataEnd != NULL);
-
- while (pDataCur + 7 < pDataEnd) // +7 for "newmtl" + a whitespace character.
- {
- if (pDataCur[0] == 'n' && pDataCur[1] == 'e' && pDataCur[2] == 'w' && pDataCur[3] == 'm' && pDataCur[4] == 't' && pDataCur[5] == 'l')
- {
- // We found "newmtl", however the next line must be whitespace.
- if (drmtl_wavefront_is_whitespace(pDataCur[6]))
- {
- // We found it.
- return pDataCur;
- }
- }
-
-
- const char* nextLineStart = drmtl_wavefront_find_next_line(pDataCur, pDataEnd);
- if (nextLineStart != NULL)
- {
- pDataCur = nextLineStart;
- }
- else
- {
- // Reached the end before finding "newmtl". Return null.
- return NULL;
- }
- }
-
- return NULL;
-}
-
-const char* drmtl_wavefront_find_next_nonwhitespace(const char* pDataCur, const char* pDataEnd)
-{
- assert(pDataCur != NULL);
- assert(pDataEnd != NULL);
-
- while (pDataCur < pDataEnd)
- {
- if (!drmtl_wavefront_is_whitespace(pDataCur[0]))
- {
- return pDataCur;
- }
-
- pDataCur += 1;
- }
-
- return NULL;
-}
-
-
-bool drmtl_wavefront_parse_K(const char* pDataCur, const char* pDataEnd, float valueOut[3])
-{
- assert(pDataCur != NULL);
- assert(pDataEnd != NULL);
-
- return drmtl_wavefront_atof_3(pDataCur, pDataEnd, &pDataEnd, valueOut);
-}
-
-bool drmtl_wavefront_parse_N(const char* pDataCur, const char* pDataEnd, float* valueOut)
-{
- assert(pDataCur != NULL);
- assert(pDataEnd != NULL);
-
- return drmtl_wavefront_atof(pDataCur, pDataEnd, &pDataEnd, valueOut);
-}
-
-bool drmtl_wavefront_parse_map(const char* pDataCur, const char* pDataEnd, char* pathOut, unsigned int pathSizeInBytes)
-{
- assert(pDataCur != NULL);
- assert(pDataEnd != NULL);
-
- // For now we're not supporting options, however support for that will be added later.
-
- const char* pPathStart = drmtl_wavefront_find_next_nonwhitespace(pDataCur, pDataEnd);
- if (pPathStart != NULL)
- {
- if (pPathStart < pDataEnd)
- {
- if (pPathStart[0] != '#')
- {
- // Find the last non-whitespace, making sure we don't include comments.
- pDataCur = pPathStart;
- const char* pPathEnd = pDataCur;
- while (pDataCur < pDataEnd && pDataCur[0] != '#')
- {
- if (!drmtl_wavefront_is_whitespace(pDataCur[0]))
- {
- pPathEnd = pDataCur + 1;
- }
-
- pDataCur += 1;
- }
-
- assert(pPathStart < pPathEnd);
-
- ptrdiff_t pathLength = pPathEnd - pPathStart;
- if ((size_t)pathLength + 1 < pathSizeInBytes)
- {
- memcpy(pathOut, pPathStart, (size_t)pathLength);
- pathOut[pathLength] = '\0';
-
- return 1;
- }
- }
- }
- }
-
- return 0;
-}
-
-
-bool drmtl_wavefront_seek_to_next_line(drmtl_wavefront* pWavefront)
-{
- assert(pWavefront != NULL);
-
- const char* lineStart = drmtl_wavefront_find_next_line(pWavefront->pDataCur, pWavefront->pDataEnd);
- if (lineStart != NULL)
- {
- pWavefront->pDataCur = lineStart;
- return 1;
- }
-
- return 0;
-}
-
-bool drmtl_wavefront_seek_to_newmtl(drmtl_wavefront* pWavefront)
-{
- assert(pWavefront != NULL);
-
- const char* usemtl = drmtl_wavefront_find_next_newmtl(pWavefront->pDataCur, pWavefront->pDataEnd);
- if (usemtl != NULL)
- {
- pWavefront->pDataCur = usemtl;
- return 1;
- }
-
- return 0;
-}
-
-bool drmtl_wavefront_parse(drmtl_wavefront* pWavefront)
-{
- assert(pWavefront != NULL);
-
- if (drmtl_wavefront_seek_to_newmtl(pWavefront) && drmtl_wavefront_seek_to_next_line(pWavefront))
- {
- // Set the end of the material to the start of the second usemtl statement, if it exists.
- const char* usemtl2 = drmtl_wavefront_find_next_newmtl(pWavefront->pDataCur, pWavefront->pDataEnd);
- if (usemtl2 != NULL)
- {
- pWavefront->pDataEnd = usemtl2;
- }
-
-
- while (pWavefront->pDataCur < pWavefront->pDataEnd)
- {
- const char* lineCur = pWavefront->pDataCur;
- const char* lineEnd = drmtl_wavefront_find_end_of_line(lineCur, pWavefront->pDataEnd);
-
- lineCur = drmtl_wavefront_find_next_nonwhitespace(lineCur, lineEnd);
- if (lineCur != NULL && (lineCur + 2 < lineEnd))
- {
- if (lineCur[0] == 'K' && lineCur[1] == 'd' && drmtl_wavefront_is_whitespace(lineCur[2])) // Diffuse colour
- {
- lineCur += 3;
- drmtl_wavefront_parse_K(lineCur, lineEnd, pWavefront->diffuse);
- }
- else if (lineCur[0] == 'K' && lineCur[1] == 's' && drmtl_wavefront_is_whitespace(lineCur[2])) // Specular colour
- {
- lineCur += 3;
- drmtl_wavefront_parse_K(lineCur, lineEnd, pWavefront->specular);
- }
- else if (lineCur[0] == 'N' && lineCur[1] == 's' && drmtl_wavefront_is_whitespace(lineCur[2])) // Specular exponent
- {
- lineCur += 3;
- drmtl_wavefront_parse_N(lineCur, lineEnd, &pWavefront->specularExponent);
- }
- else if (lineCur[0] == 'd' && drmtl_wavefront_is_whitespace(lineCur[1])) // Opacity/Alpha
- {
- lineCur += 2;
- drmtl_wavefront_parse_N(lineCur, lineEnd, &pWavefront->alpha);
- }
- else
- {
- // Check for maps.
- if (lineCur + 6 < lineEnd)
- {
- if (lineCur[0] == 'm' && lineCur[1] == 'a' && lineCur[2] == 'p' && lineCur[3] == '_')
- {
- if (lineCur[4] == 'K' && lineCur[5] == 'd' && drmtl_wavefront_is_whitespace(lineCur[6])) // Diffuse map
- {
- lineCur += 7;
- drmtl_wavefront_parse_map(lineCur, lineEnd, pWavefront->diffuseMap, DRMTL_MAX_INPUT_PATH);
- }
- else if (lineCur[4] == 'K' && lineCur[5] == 's' && drmtl_wavefront_is_whitespace(lineCur[6])) // Specular map
- {
- lineCur += 7;
- drmtl_wavefront_parse_map(lineCur, lineEnd, pWavefront->specularMap, DRMTL_MAX_INPUT_PATH);
- }
- else if (lineCur[4] == 'N' && lineCur[5] == 's' && drmtl_wavefront_is_whitespace(lineCur[6])) // Specular exponent map
- {
- lineCur += 7;
- drmtl_wavefront_parse_map(lineCur, lineEnd, pWavefront->specularExponentMap, DRMTL_MAX_INPUT_PATH);
- }
- else if (lineCur[4] == 'd' && drmtl_wavefront_is_whitespace(lineCur[5])) // Opacity/Alpha map
- {
- lineCur += 6;
- drmtl_wavefront_parse_map(lineCur, lineEnd, pWavefront->alphaMap, DRMTL_MAX_INPUT_PATH);
- }
- }
- }
- }
- }
-
-
- // Move to the end of the line.
- pWavefront->pDataCur = lineEnd;
-
- // Move to the start of the next line. If this fails it probably means we've reached the end of the data so we just break from the loop
- if (!drmtl_wavefront_seek_to_next_line(pWavefront))
- {
- break;
- }
- }
-
-
- return 1;
- }
-
- return 0;
-}
-
-bool drmtl_wavefront_compile(drmtl_material* pMaterial, drmtl_wavefront* pWavefront, const char* texcoordInputName)
-{
- assert(pMaterial != NULL);
- assert(pWavefront != NULL);
-
- unsigned int texCoordID; // Private input for texture coordinates.
- unsigned int diffuseID;
- unsigned int specularID;
- unsigned int specularExponentID;
- unsigned int alphaID;
- unsigned int diffuseMapID = (unsigned int)-1;
- unsigned int specularMapID = (unsigned int)-1;
- unsigned int specularExponentMapID = (unsigned int)-1;
- unsigned int alphaMapID = (unsigned int)-1;
- unsigned int diffuseResultID = (unsigned int)-1;
- unsigned int specularResultID = (unsigned int)-1;
- unsigned int specularExponentResultID = (unsigned int)-1;
- unsigned int alphaResultID = (unsigned int)-1;
-
-
- // Identifiers.
- drmtl_appendidentifier(pMaterial, drmtl_identifier_float2(texcoordInputName), &texCoordID);
- drmtl_appendidentifier(pMaterial, drmtl_identifier_float4("DiffuseColor"), &diffuseID);
- drmtl_appendidentifier(pMaterial, drmtl_identifier_float3("SpecularColor"), &specularID);
- drmtl_appendidentifier(pMaterial, drmtl_identifier_float("SpecularExponent"), &specularExponentID);
- drmtl_appendidentifier(pMaterial, drmtl_identifier_float("Alpha"), &alphaID);
-
- if (pWavefront->diffuseMap[0] != '\0') {
- drmtl_appendidentifier(pMaterial, drmtl_identifier_tex2d("DiffuseMap"), &diffuseMapID);
- drmtl_appendidentifier(pMaterial, drmtl_identifier_float4("DiffuseResult"), &diffuseResultID);
- }
- if (pWavefront->specularMap[0] != '\0') {
- drmtl_appendidentifier(pMaterial, drmtl_identifier_tex2d("SpecularMap"), &specularMapID);
- drmtl_appendidentifier(pMaterial, drmtl_identifier_float4("SpecularResult"), &specularResultID);
- }
- if (pWavefront->specularExponentMap[0] != '\0') {
- drmtl_appendidentifier(pMaterial, drmtl_identifier_tex2d("SpecularExponentMap"), &specularExponentMapID);
- drmtl_appendidentifier(pMaterial, drmtl_identifier_float4("SpecularExponentResult"), &specularExponentResultID);
- }
- if (pWavefront->alphaMap[0] != '\0') {
- drmtl_appendidentifier(pMaterial, drmtl_identifier_tex2d("AlphaMap"), &alphaMapID);
- drmtl_appendidentifier(pMaterial, drmtl_identifier_float4("AlphaResult"), &alphaResultID);
- }
-
-
- // Inputs.
- drmtl_appendprivateinput(pMaterial, drmtl_input_float2(texCoordID, 0, 0));
- drmtl_appendpublicinput(pMaterial, drmtl_input_float4(diffuseID, pWavefront->diffuse[0], pWavefront->diffuse[1], pWavefront->diffuse[2], 1.0f));
- drmtl_appendpublicinput(pMaterial, drmtl_input_float3(specularID, pWavefront->specular[0], pWavefront->specular[1], pWavefront->specular[2]));
- drmtl_appendpublicinput(pMaterial, drmtl_input_float(specularExponentID, pWavefront->specularExponent));
- drmtl_appendpublicinput(pMaterial, drmtl_input_float(alphaID, pWavefront->alpha));
-
- if (pWavefront->diffuseMap[0] != '\0') {
- drmtl_appendpublicinput(pMaterial, drmtl_input_tex(diffuseMapID, pWavefront->diffuseMap));
- }
- if (pWavefront->specularMap[0] != '\0') {
- drmtl_appendpublicinput(pMaterial, drmtl_input_tex(specularMapID, pWavefront->specularMap));
- }
- if (pWavefront->specularExponentMap[0] != '\0') {
- drmtl_appendpublicinput(pMaterial, drmtl_input_tex(specularExponentMapID, pWavefront->specularExponentMap));
- }
- if (pWavefront->alphaMap[0] != '\0') {
- drmtl_appendpublicinput(pMaterial, drmtl_input_tex(alphaMapID, pWavefront->alphaMap));
- }
-
-
- // Channels.
- drmtl_appendchannel(pMaterial, drmtl_channel_float4("DiffuseChannel"));
- if (pWavefront->diffuseMap[0] != '\0') {
- drmtl_appendinstruction(pMaterial, drmtl_var(diffuseResultID));
- drmtl_appendinstruction(pMaterial, drmtl_tex2(diffuseResultID, diffuseMapID, texCoordID));
- drmtl_appendinstruction(pMaterial, drmtl_mulf4_v3c1(diffuseResultID, diffuseID, 1.0f));
- drmtl_appendinstruction(pMaterial, drmtl_retf4(diffuseResultID));
- } else {
- drmtl_appendinstruction(pMaterial, drmtl_retf4(diffuseID));
- }
-
- drmtl_appendchannel(pMaterial, drmtl_channel_float3("SpecularChannel"));
- if (pWavefront->specularMap[0] != '\0') {
- drmtl_appendinstruction(pMaterial, drmtl_var(specularResultID));
- drmtl_appendinstruction(pMaterial, drmtl_tex2(specularResultID, specularMapID, texCoordID));
- drmtl_appendinstruction(pMaterial, drmtl_mulf4_v3c1(specularResultID, specularID, 1.0f));
- drmtl_appendinstruction(pMaterial, drmtl_retf3(specularResultID));
- } else {
- drmtl_appendinstruction(pMaterial, drmtl_retf3(specularID));
- }
-
- drmtl_appendchannel(pMaterial, drmtl_channel_float("SpecularExponentChannel"));
- if (pWavefront->specularExponentMap[0] != '\0') {
- drmtl_appendinstruction(pMaterial, drmtl_var(specularExponentResultID));
- drmtl_appendinstruction(pMaterial, drmtl_tex2(specularResultID, specularMapID, texCoordID));
- drmtl_appendinstruction(pMaterial, drmtl_mulf4_v1c3(specularResultID, specularID, 1.0f, 1.0f, 1.0f));
- drmtl_appendinstruction(pMaterial, drmtl_retf1(specularResultID));
- } else {
- drmtl_appendinstruction(pMaterial, drmtl_retf1(specularExponentID));
- }
-
- drmtl_appendchannel(pMaterial, drmtl_channel_float("AlphaChannel"));
- if (pWavefront->alphaMap[0] != '\0') {
- drmtl_appendinstruction(pMaterial, drmtl_var(alphaResultID));
- drmtl_appendinstruction(pMaterial, drmtl_tex2(alphaResultID, alphaMapID, texCoordID));
- drmtl_appendinstruction(pMaterial, drmtl_mulf4_v1c3(alphaResultID, alphaID, 1.0f, 1.0f, 1.0f));
- drmtl_appendinstruction(pMaterial, drmtl_retf1(alphaResultID));
- } else {
- drmtl_appendinstruction(pMaterial, drmtl_retf1(alphaID));
- }
-
-
-
- // Properties.
- if (pWavefront->alphaMap[0] != '\0' || pWavefront->alpha < 1)
- {
- drmtl_appendproperty(pMaterial, drmtl_property_bool("IsTransparent", 1));
- }
-
- return 1;
-}
-
-
-bool drmtl_compile_wavefront_mtl(drmtl_material* pMaterial, const char* mtlData, size_t mtlDataSizeInBytes, const char* texcoordInputName)
-{
- if (pMaterial != NULL && mtlData != NULL && mtlDataSizeInBytes > 0)
- {
- if (drmtl_init(pMaterial))
- {
- drmtl_wavefront wavefront;
- wavefront.pData = mtlData;
- wavefront.dataSizeInBytes = mtlDataSizeInBytes;
- wavefront.pDataCur = wavefront.pData;
- wavefront.pDataEnd = wavefront.pData + wavefront.dataSizeInBytes;
- wavefront.diffuse[0] = 1; wavefront.diffuse[1] = 1; wavefront.diffuse[2] = 1;
- wavefront.diffuseMap[0] = '\0';
- wavefront.specular[0] = 1; wavefront.specular[1] = 1; wavefront.specular[2] = 1;
- wavefront.specularMap[0] = '\0';
- wavefront.specularExponent = 10;
- wavefront.specularExponentMap[0] = '\0';
- wavefront.alpha = 1;
- wavefront.alphaMap[0] = '\0';
-
- if (drmtl_wavefront_parse(&wavefront))
- {
- if (drmtl_wavefront_compile(pMaterial, &wavefront, texcoordInputName))
- {
- return 1;
- }
- else
- {
- // Failed to compile.
- drmtl_uninit(pMaterial);
- }
- }
- else
- {
- // Failed to parse the file.
- drmtl_uninit(pMaterial);
- }
- }
- }
-
- return 0;
-}
-#endif
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-//
-// Code Generators
-//
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-#ifndef DRMTL_NO_GLSL_CODEGEN
-#include
-
-#if defined(__clang__)
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wswitch-enum"
- #pragma GCC diagnostic ignored "-Wcovered-switch-default"
- #pragma GCC diagnostic ignored "-Wused-but-marked-unused" // This ie emitted for snprintf() for some reason. Need to investigate...
-#endif
-
-typedef struct
-{
- /// The output buffer. Can be null, in which case nothing is written.
- char* pBufferOut;
-
- /// The size of the output buffer.
- unsigned int bufferOutSizeInBytes;
-
- /// The current length of the string copied to the output buffer.
- unsigned int runningLength;
-
-
- /// A pointer to the material that is being used as the source of the code generation.
- drmtl_material* pMaterial;
-
- /// A pointer to the buffer containing the material's identifiers.
- drmtl_identifier* pIdentifiers;
-
- /// The number of identifiers.
- unsigned int identifierCount;
-
-
- /// The current indentation level, in spaces.
- unsigned int indentationLevel;
-
-} drmtl_codegen_glsl;
-
-bool drmtl_codegen_glsl_write(drmtl_codegen_glsl* pCodegen, const char* src)
-{
- assert(pCodegen != NULL);
- assert(src != NULL);
-
- if (pCodegen->pBufferOut != NULL)
- {
- unsigned int dstSizeInBytes = (pCodegen->bufferOutSizeInBytes - pCodegen->runningLength);
- while (dstSizeInBytes > 0 && src[0] != '\0')
- {
- pCodegen->pBufferOut[pCodegen->runningLength + 0] = src[0];
-
- pCodegen->runningLength += 1;
- src += 1;
- dstSizeInBytes -= 1;
- }
-
- if (dstSizeInBytes > 0)
- {
- // There's enough room for the null terminator which means there was enough room in the buffer. All good.
- pCodegen->pBufferOut[pCodegen->runningLength] = '\0';
- return 1;
- }
- else
- {
- // There's not enough room for the null terminator which means there was NOT enough room in the buffer. Error.
- return 0;
- }
- }
- else
- {
- // We're just measuring.
- pCodegen->runningLength += (unsigned int)strlen(src);
- return 1;
- }
-}
-
-bool drmtl_codegen_glsl_write_float(drmtl_codegen_glsl* pCodegen, float src)
-{
- assert(pCodegen != NULL);
-
- char str[32];
- if (snprintf(str, 32, "%f", src) > 0)
- {
- return drmtl_codegen_glsl_write(pCodegen, str);
- }
- else
- {
- return 0;
- }
-}
-
-bool drmtl_codegen_glsl_write_int(drmtl_codegen_glsl* pCodegen, int src)
-{
- assert(pCodegen != NULL);
-
- char str[32];
- if (snprintf(str, 32, "%d", src) > 0)
- {
- return drmtl_codegen_glsl_write(pCodegen, str);
- }
- else
- {
- return 0;
- }
-}
-
-bool drmtl_codegen_glsl_write_indentation(drmtl_codegen_glsl* pCodegen)
-{
- assert(pCodegen != NULL);
-
- for (unsigned int i = 0; i < pCodegen->indentationLevel; ++i)
- {
- drmtl_codegen_glsl_write(pCodegen, " ");
- }
-
- return 1;
-}
-
-bool drmtl_codegen_glsl_write_type(drmtl_codegen_glsl* pCodegen, drmtl_type type)
-{
- assert(pCodegen != NULL);
-
- switch (type)
- {
- case drmtl_type_float:
- {
- if (!drmtl_codegen_glsl_write(pCodegen, "float"))
- {
- return 0;
- }
-
- break;
- }
- case drmtl_type_float2:
- {
- if (!drmtl_codegen_glsl_write(pCodegen, "vec2"))
- {
- return 0;
- }
-
- break;
- }
- case drmtl_type_float3:
- {
- if (!drmtl_codegen_glsl_write(pCodegen, "vec3"))
- {
- return 0;
- }
-
- break;
- }
- case drmtl_type_float4:
- {
- if (!drmtl_codegen_glsl_write(pCodegen, "vec4"))
- {
- return 0;
- }
-
- break;
- }
-
- case drmtl_type_int:
- {
- if (!drmtl_codegen_glsl_write(pCodegen, "int"))
- {
- return 0;
- }
-
- break;
- }
- case drmtl_type_int2:
- {
- if (!drmtl_codegen_glsl_write(pCodegen, "ivec2"))
- {
- return 0;
- }
-
- break;
- }
- case drmtl_type_int3:
- {
- if (!drmtl_codegen_glsl_write(pCodegen, "ivec3"))
- {
- return 0;
- }
-
- break;
- }
- case drmtl_type_int4:
- {
- if (!drmtl_codegen_glsl_write(pCodegen, "ivec4"))
- {
- return 0;
- }
-
- break;
- }
-
- case drmtl_type_tex1d:
- {
- if (!drmtl_codegen_glsl_write(pCodegen, "sampler1D"))
- {
- return 0;
- }
-
- break;
- }
- case drmtl_type_tex2d:
- {
- if (!drmtl_codegen_glsl_write(pCodegen, "sampler2D"))
- {
- return 0;
- }
-
- break;
- }
- case drmtl_type_tex3d:
- {
- if (!drmtl_codegen_glsl_write(pCodegen, "sampler3D"))
- {
- return 0;
- }
-
- break;
- }
- case drmtl_type_texcube:
- {
- if (!drmtl_codegen_glsl_write(pCodegen, "samplerCube"))
- {
- return 0;
- }
-
- break;
- }
-
- default:
- {
- // Unsupported return type.
- return 0;
- }
- }
-
- return 1;
-}
-
-bool drmtl_codegen_glsl_write_instruction_input_scalar(drmtl_codegen_glsl* pCodegen, unsigned char descriptor, drmtl_instruction_input* pInput)
-{
- assert(pCodegen != NULL);
- assert(pInput != NULL);
-
- if (descriptor == DRMTL_INPUT_DESC_CONSTF)
- {
- // It's a constant float.
- return drmtl_codegen_glsl_write_float(pCodegen, pInput->valuef);
- }
- else if (descriptor == DRMTL_INPUT_DESC_CONSTI)
- {
- // It's a constant int.
- return drmtl_codegen_glsl_write_int(pCodegen, pInput->valuei);
- }
- else
- {
- // It's a variable.
- if (pInput->id < pCodegen->identifierCount)
- {
- drmtl_identifier* pIdentifier = pCodegen->pIdentifiers + pInput->id;
- assert(pIdentifier != NULL);
-
- if (pIdentifier->type == drmtl_type_float)
- {
- // The input variable is a float, so we don't want to use any selectors.
- return drmtl_codegen_glsl_write(pCodegen, pIdentifier->name);
- }
- else
- {
- if (drmtl_codegen_glsl_write(pCodegen, pIdentifier->name) && drmtl_codegen_glsl_write(pCodegen, "."))
- {
- switch (descriptor)
- {
- case 0: return drmtl_codegen_glsl_write(pCodegen, "x");
- case 1: return drmtl_codegen_glsl_write(pCodegen, "y");
- case 2: return drmtl_codegen_glsl_write(pCodegen, "z");
- case 3: return drmtl_codegen_glsl_write(pCodegen, "w");
- default: return 0;
- }
- }
- }
- }
- }
-
- return 0;
-}
-
-bool drmtl_codegen_glsl_write_instruction_input_initializer(drmtl_codegen_glsl* pCodegen, drmtl_type type, drmtl_instruction_input_descriptor inputDesc, drmtl_instruction_input* pInputs)
-{
- assert(pCodegen != NULL);
- assert(pInputs != NULL);
-
- switch (type)
- {
- case drmtl_type_float:
- {
- return drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.x, pInputs + 0);
- }
-
- case drmtl_type_float2:
- {
- if (drmtl_codegen_glsl_write(pCodegen, "vec2("))
- {
- if (drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.x, pInputs + 0) && drmtl_codegen_glsl_write(pCodegen, ", ") &&
- drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.y, pInputs + 1))
- {
- return drmtl_codegen_glsl_write(pCodegen, ")");
- }
- }
-
- break;
- }
-
- case drmtl_type_float3:
- {
- if (drmtl_codegen_glsl_write(pCodegen, "vec3("))
- {
- if (drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.x, pInputs + 0) && drmtl_codegen_glsl_write(pCodegen, ", ") &&
- drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.y, pInputs + 1) && drmtl_codegen_glsl_write(pCodegen, ", ") &&
- drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.z, pInputs + 2))
- {
- return drmtl_codegen_glsl_write(pCodegen, ")");
- }
- }
-
- break;
- }
-
- case drmtl_type_float4:
- {
- if (drmtl_codegen_glsl_write(pCodegen, "vec4("))
- {
- if (drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.x, pInputs + 0) && drmtl_codegen_glsl_write(pCodegen, ", ") &&
- drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.y, pInputs + 1) && drmtl_codegen_glsl_write(pCodegen, ", ") &&
- drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.z, pInputs + 2) && drmtl_codegen_glsl_write(pCodegen, ", ") &&
- drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.w, pInputs + 3))
- {
- return drmtl_codegen_glsl_write(pCodegen, ")");
- }
- }
-
- break;
- }
-
-
- case drmtl_type_int:
- {
- return drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.x, pInputs + 0);
- }
-
- case drmtl_type_int2:
- {
- if (drmtl_codegen_glsl_write(pCodegen, "ivec2("))
- {
- if (drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.x, pInputs + 0) && drmtl_codegen_glsl_write(pCodegen, ", ") &&
- drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.y, pInputs + 1))
- {
- return drmtl_codegen_glsl_write(pCodegen, ")");
- }
- }
-
- break;
- }
-
- case drmtl_type_int3:
- {
- if (drmtl_codegen_glsl_write(pCodegen, "ivec3("))
- {
- if (drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.x, pInputs + 0) && drmtl_codegen_glsl_write(pCodegen, ", ") &&
- drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.y, pInputs + 1) && drmtl_codegen_glsl_write(pCodegen, ", ") &&
- drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.z, pInputs + 2))
- {
- return drmtl_codegen_glsl_write(pCodegen, ")");
- }
- }
-
- break;
- }
-
- case drmtl_type_int4:
- {
- if (drmtl_codegen_glsl_write(pCodegen, "ivec4("))
- {
- if (drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.x, pInputs + 0) && drmtl_codegen_glsl_write(pCodegen, ", ") &&
- drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.y, pInputs + 1) && drmtl_codegen_glsl_write(pCodegen, ", ") &&
- drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.z, pInputs + 2) && drmtl_codegen_glsl_write(pCodegen, ", ") &&
- drmtl_codegen_glsl_write_instruction_input_scalar(pCodegen, inputDesc.w, pInputs + 3))
- {
- return drmtl_codegen_glsl_write(pCodegen, ")");
- }
- }
-
- break;
- }
-
-
- default:
- {
- // Unsupported return type.
- return 0;
- }
- }
-
- return 0;
-}
-
-
-bool drmtl_codegen_glsl_write_instruction_mov(drmtl_codegen_glsl* pCodegen, drmtl_instruction* pInstruction)
-{
- assert(pCodegen != NULL);
- assert(pInstruction != NULL);
-
- if (pInstruction->mov.output < pCodegen->identifierCount)
- {
- drmtl_identifier* pOutputIdentifier = pCodegen->pIdentifiers + pInstruction->mov.output;
- assert(pOutputIdentifier != NULL);
-
- if (drmtl_codegen_glsl_write(pCodegen, pOutputIdentifier->name) && drmtl_codegen_glsl_write(pCodegen, " = "))
- {
- drmtl_type type;
- switch (pInstruction->opcode)
- {
- case drmtl_opcode_movf1: type = drmtl_type_float; break;
- case drmtl_opcode_movf2: type = drmtl_type_float2; break;
- case drmtl_opcode_movf3: type = drmtl_type_float3; break;
- case drmtl_opcode_movf4: type = drmtl_type_float4; break;
- case drmtl_opcode_movi1: type = drmtl_type_int; break;
- case drmtl_opcode_movi2: type = drmtl_type_int2; break;
- case drmtl_opcode_movi3: type = drmtl_type_int3; break;
- case drmtl_opcode_movi4: type = drmtl_type_int4; break;
- default: return 0;
- }
-
- return drmtl_codegen_glsl_write_instruction_input_initializer(pCodegen, type, pInstruction->mov.inputDesc, &pInstruction->mov.inputX) && drmtl_codegen_glsl_write(pCodegen, ";\n");
- }
- }
-
- return 0;
-}
-
-bool drmtl_codegen_glsl_write_instruction_add(drmtl_codegen_glsl* pCodegen, drmtl_instruction* pInstruction)
-{
- assert(pCodegen != NULL);
- assert(pInstruction != NULL);
-
- if (pInstruction->add.output < pCodegen->identifierCount)
- {
- drmtl_identifier* pOutputIdentifier = pCodegen->pIdentifiers + pInstruction->add.output;
- assert(pOutputIdentifier != NULL);
-
- if (drmtl_codegen_glsl_write(pCodegen, pOutputIdentifier->name) && drmtl_codegen_glsl_write(pCodegen, " += "))
- {
- drmtl_type type;
- switch (pInstruction->opcode)
- {
- case drmtl_opcode_addf1: type = drmtl_type_float; break;
- case drmtl_opcode_addf2: type = drmtl_type_float2; break;
- case drmtl_opcode_addf3: type = drmtl_type_float3; break;
- case drmtl_opcode_addf4: type = drmtl_type_float4; break;
- case drmtl_opcode_addi1: type = drmtl_type_int; break;
- case drmtl_opcode_addi2: type = drmtl_type_int2; break;
- case drmtl_opcode_addi3: type = drmtl_type_int3; break;
- case drmtl_opcode_addi4: type = drmtl_type_int4; break;
- default: return 0;
- }
-
- return drmtl_codegen_glsl_write_instruction_input_initializer(pCodegen, type, pInstruction->add.inputDesc, &pInstruction->add.inputX) && drmtl_codegen_glsl_write(pCodegen, ";\n");
- }
- }
-
- return 0;
-}
-
-bool drmtl_codegen_glsl_write_instruction_sub(drmtl_codegen_glsl* pCodegen, drmtl_instruction* pInstruction)
-{
- assert(pCodegen != NULL);
- assert(pInstruction != NULL);
-
- if (pInstruction->add.output < pCodegen->identifierCount)
- {
- drmtl_identifier* pOutputIdentifier = pCodegen->pIdentifiers + pInstruction->sub.output;
- assert(pOutputIdentifier != NULL);
-
- if (drmtl_codegen_glsl_write(pCodegen, pOutputIdentifier->name) && drmtl_codegen_glsl_write(pCodegen, " -= "))
- {
- drmtl_type type;
- switch (pInstruction->opcode)
- {
- case drmtl_opcode_subf1: type = drmtl_type_float; break;
- case drmtl_opcode_subf2: type = drmtl_type_float2; break;
- case drmtl_opcode_subf3: type = drmtl_type_float3; break;
- case drmtl_opcode_subf4: type = drmtl_type_float4; break;
- case drmtl_opcode_subi1: type = drmtl_type_int; break;
- case drmtl_opcode_subi2: type = drmtl_type_int2; break;
- case drmtl_opcode_subi3: type = drmtl_type_int3; break;
- case drmtl_opcode_subi4: type = drmtl_type_int4; break;
- default: return 0;
- }
-
- return drmtl_codegen_glsl_write_instruction_input_initializer(pCodegen, type, pInstruction->sub.inputDesc, &pInstruction->sub.inputX) && drmtl_codegen_glsl_write(pCodegen, ";\n");
- }
- }
-
- return 0;
-}
-
-bool drmtl_codegen_glsl_write_instruction_mul(drmtl_codegen_glsl* pCodegen, drmtl_instruction* pInstruction)
-{
- assert(pCodegen != NULL);
- assert(pInstruction != NULL);
-
- if (pInstruction->mul.output < pCodegen->identifierCount)
- {
- drmtl_identifier* pOutputIdentifier = pCodegen->pIdentifiers + pInstruction->mul.output;
- assert(pOutputIdentifier != NULL);
-
- if (drmtl_codegen_glsl_write(pCodegen, pOutputIdentifier->name) && drmtl_codegen_glsl_write(pCodegen, " *= "))
- {
- drmtl_type type;
- switch (pInstruction->opcode)
- {
- case drmtl_opcode_mulf1: type = drmtl_type_float; break;
- case drmtl_opcode_mulf2: type = drmtl_type_float2; break;
- case drmtl_opcode_mulf3: type = drmtl_type_float3; break;
- case drmtl_opcode_mulf4: type = drmtl_type_float4; break;
- case drmtl_opcode_muli1: type = drmtl_type_int; break;
- case drmtl_opcode_muli2: type = drmtl_type_int2; break;
- case drmtl_opcode_muli3: type = drmtl_type_int3; break;
- case drmtl_opcode_muli4: type = drmtl_type_int4; break;
- default: return 0;
- }
-
- return drmtl_codegen_glsl_write_instruction_input_initializer(pCodegen, type, pInstruction->mul.inputDesc, &pInstruction->mul.inputX) && drmtl_codegen_glsl_write(pCodegen, ";\n");
- }
- }
-
- return 0;
-}
-
-bool drmtl_codegen_glsl_write_instruction_div(drmtl_codegen_glsl* pCodegen, drmtl_instruction* pInstruction)
-{
- assert(pCodegen != NULL);
- assert(pInstruction != NULL);
-
- if (pInstruction->div.output < pCodegen->identifierCount)
- {
- drmtl_identifier* pOutputIdentifier = pCodegen->pIdentifiers + pInstruction->div.output;
- assert(pOutputIdentifier != NULL);
-
- if (drmtl_codegen_glsl_write(pCodegen, pOutputIdentifier->name) && drmtl_codegen_glsl_write(pCodegen, " = "))
- {
- drmtl_type type;
- switch (pInstruction->opcode)
- {
- case drmtl_opcode_divf1: type = drmtl_type_float; break;
- case drmtl_opcode_divf2: type = drmtl_type_float2; break;
- case drmtl_opcode_divf3: type = drmtl_type_float3; break;
- case drmtl_opcode_divf4: type = drmtl_type_float4; break;
- case drmtl_opcode_divi1: type = drmtl_type_int; break;
- case drmtl_opcode_divi2: type = drmtl_type_int2; break;
- case drmtl_opcode_divi3: type = drmtl_type_int3; break;
- case drmtl_opcode_divi4: type = drmtl_type_int4; break;
- default: return 0;
- }
-
- return drmtl_codegen_glsl_write_instruction_input_initializer(pCodegen, type, pInstruction->div.inputDesc, &pInstruction->div.inputX) && drmtl_codegen_glsl_write(pCodegen, ";\n");
- }
- }
-
- return 0;
-}
-
-bool drmtl_codegen_glsl_write_instruction_pow(drmtl_codegen_glsl* pCodegen, drmtl_instruction* pInstruction)
-{
- assert(pCodegen != NULL);
- assert(pInstruction != NULL);
-
- if (pInstruction->pow.output < pCodegen->identifierCount)
- {
- drmtl_identifier* pOutputIdentifier = pCodegen->pIdentifiers + pInstruction->pow.output;
- assert(pOutputIdentifier != NULL);
-
- if (drmtl_codegen_glsl_write(pCodegen, pOutputIdentifier->name) && drmtl_codegen_glsl_write(pCodegen, " = pow(") && drmtl_codegen_glsl_write(pCodegen, pOutputIdentifier->name) && drmtl_codegen_glsl_write(pCodegen, ", "))
- {
- drmtl_type type;
- switch (pInstruction->opcode)
- {
- case drmtl_opcode_powf1: type = drmtl_type_float; break;
- case drmtl_opcode_powf2: type = drmtl_type_float2; break;
- case drmtl_opcode_powf3: type = drmtl_type_float3; break;
- case drmtl_opcode_powf4: type = drmtl_type_float4; break;
- case drmtl_opcode_powi1: type = drmtl_type_int; break;
- case drmtl_opcode_powi2: type = drmtl_type_int2; break;
- case drmtl_opcode_powi3: type = drmtl_type_int3; break;
- case drmtl_opcode_powi4: type = drmtl_type_int4; break;
- default: return 0;
- }
-
- return drmtl_codegen_glsl_write_instruction_input_initializer(pCodegen, type, pInstruction->pow.inputDesc, &pInstruction->pow.inputX) && drmtl_codegen_glsl_write(pCodegen, ");\n");
- }
- }
-
- return 0;
-}
-
-bool drmtl_codegen_glsl_write_instruction_tex(drmtl_codegen_glsl* pCodegen, drmtl_instruction* pInstruction)
-{
- assert(pCodegen != NULL);
- assert(pInstruction != NULL);
-
- if (pInstruction->tex.output < pCodegen->identifierCount && pInstruction->tex.texture < pCodegen->identifierCount)
- {
- drmtl_identifier* pOutputIdentifier = pCodegen->pIdentifiers + pInstruction->tex.output;
- assert(pOutputIdentifier != NULL);
-
- drmtl_identifier* pTextureIdentifier = pCodegen->pIdentifiers + pInstruction->tex.texture;
- assert(pTextureIdentifier != NULL);
-
- if (drmtl_codegen_glsl_write(pCodegen, pOutputIdentifier->name) && drmtl_codegen_glsl_write(pCodegen, " = "))
- {
- drmtl_type type;
- switch (pInstruction->opcode)
- {
- case drmtl_opcode_tex1:
- {
- type = drmtl_type_float;
- if (!drmtl_codegen_glsl_write(pCodegen, "texture1D("))
- {
- return 0;
- }
-
- break;
- }
-
- case drmtl_opcode_tex2:
- {
- type = drmtl_type_float2;
- if (!drmtl_codegen_glsl_write(pCodegen, "texture2D("))
- {
- return 0;
- }
-
- break;
- }
-
- case drmtl_opcode_tex3:
- {
- type = drmtl_type_float3;
- if (!drmtl_codegen_glsl_write(pCodegen, "texture3D("))
- {
- return 0;
- }
-
- break;
- }
-
- case drmtl_opcode_texcube:
- {
- type = drmtl_type_float3;
- if (!drmtl_codegen_glsl_write(pCodegen, "textureCube("))
- {
- return 0;
- }
-
- break;
- }
-
- default: return 0;
- }
-
- return
- drmtl_codegen_glsl_write(pCodegen, pTextureIdentifier->name) &&
- drmtl_codegen_glsl_write(pCodegen, ", ") &&
- drmtl_codegen_glsl_write_instruction_input_initializer(pCodegen, type, pInstruction->tex.inputDesc, &pInstruction->tex.inputX) &&
- drmtl_codegen_glsl_write(pCodegen, ");\n");
- }
- }
-
- return 0;
-}
-
-bool drmtl_codegen_glsl_write_instruction_var(drmtl_codegen_glsl* pCodegen, drmtl_instruction* pInstruction)
-{
- assert(pCodegen != NULL);
- assert(pInstruction != NULL);
-
- if (pInstruction->var.identifierIndex < pCodegen->identifierCount)
- {
- drmtl_identifier* pIdentifier = pCodegen->pIdentifiers + pInstruction->var.identifierIndex;
- assert(pIdentifier != NULL);
-
- return drmtl_codegen_glsl_write_type(pCodegen, pIdentifier->type) && drmtl_codegen_glsl_write(pCodegen, " ") && drmtl_codegen_glsl_write(pCodegen, pIdentifier->name) && drmtl_codegen_glsl_write(pCodegen, ";\n");
- }
-
- return 0;
-}
-
-bool drmtl_codegen_glsl_write_instruction_ret(drmtl_codegen_glsl* pCodegen, drmtl_instruction* pInstruction)
-{
- assert(pCodegen != NULL);
- assert(pInstruction != NULL);
-
- if (drmtl_codegen_glsl_write(pCodegen, "return "))
- {
- drmtl_type type;
- switch (pInstruction->opcode)
- {
- case drmtl_opcode_retf1: type = drmtl_type_float; break;
- case drmtl_opcode_retf2: type = drmtl_type_float2; break;
- case drmtl_opcode_retf3: type = drmtl_type_float3; break;
- case drmtl_opcode_retf4: type = drmtl_type_float4; break;
- case drmtl_opcode_reti1: type = drmtl_type_int; break;
- case drmtl_opcode_reti2: type = drmtl_type_int2; break;
- case drmtl_opcode_reti3: type = drmtl_type_int3; break;
- case drmtl_opcode_reti4: type = drmtl_type_int4; break;
- default: return 0;
- }
-
- return drmtl_codegen_glsl_write_instruction_input_initializer(pCodegen, type, pInstruction->ret.inputDesc, &pInstruction->ret.inputX) && drmtl_codegen_glsl_write(pCodegen, ";\n");
- }
-
- return 0;
-}
-
-bool drmtl_codegen_glsl_write_instruction(drmtl_codegen_glsl* pCodegen, drmtl_instruction* pInstruction)
-{
- assert(pCodegen != NULL);
- assert(pInstruction != NULL);
-
- if (drmtl_codegen_glsl_write_indentation(pCodegen))
- {
- switch (pInstruction->opcode)
- {
- case drmtl_opcode_movf1:
- case drmtl_opcode_movf2:
- case drmtl_opcode_movf3:
- case drmtl_opcode_movf4:
- case drmtl_opcode_movi1:
- case drmtl_opcode_movi2:
- case drmtl_opcode_movi3:
- case drmtl_opcode_movi4:
- {
- return drmtl_codegen_glsl_write_instruction_mov(pCodegen, pInstruction);
- }
-
-
- case drmtl_opcode_addf1:
- case drmtl_opcode_addf2:
- case drmtl_opcode_addf3:
- case drmtl_opcode_addf4:
- case drmtl_opcode_addi1:
- case drmtl_opcode_addi2:
- case drmtl_opcode_addi3:
- case drmtl_opcode_addi4:
- {
- return drmtl_codegen_glsl_write_instruction_add(pCodegen, pInstruction);
- }
-
- case drmtl_opcode_subf1:
- case drmtl_opcode_subf2:
- case drmtl_opcode_subf3:
- case drmtl_opcode_subf4:
- case drmtl_opcode_subi1:
- case drmtl_opcode_subi2:
- case drmtl_opcode_subi3:
- case drmtl_opcode_subi4:
- {
- return drmtl_codegen_glsl_write_instruction_sub(pCodegen, pInstruction);
- }
-
- case drmtl_opcode_mulf1:
- case drmtl_opcode_mulf2:
- case drmtl_opcode_mulf3:
- case drmtl_opcode_mulf4:
- case drmtl_opcode_muli1:
- case drmtl_opcode_muli2:
- case drmtl_opcode_muli3:
- case drmtl_opcode_muli4:
- {
- return drmtl_codegen_glsl_write_instruction_mul(pCodegen, pInstruction);
- }
-
- case drmtl_opcode_divf1:
- case drmtl_opcode_divf2:
- case drmtl_opcode_divf3:
- case drmtl_opcode_divf4:
- case drmtl_opcode_divi1:
- case drmtl_opcode_divi2:
- case drmtl_opcode_divi3:
- case drmtl_opcode_divi4:
- {
- return drmtl_codegen_glsl_write_instruction_div(pCodegen, pInstruction);
- }
-
- case drmtl_opcode_powf1:
- case drmtl_opcode_powf2:
- case drmtl_opcode_powf3:
- case drmtl_opcode_powf4:
- case drmtl_opcode_powi1:
- case drmtl_opcode_powi2:
- case drmtl_opcode_powi3:
- case drmtl_opcode_powi4:
- {
- return drmtl_codegen_glsl_write_instruction_pow(pCodegen, pInstruction);
- }
-
- case drmtl_opcode_tex1:
- case drmtl_opcode_tex2:
- case drmtl_opcode_tex3:
- case drmtl_opcode_texcube:
- {
- return drmtl_codegen_glsl_write_instruction_tex(pCodegen, pInstruction);
- }
-
-
- case drmtl_opcode_var:
- {
- return drmtl_codegen_glsl_write_instruction_var(pCodegen, pInstruction);
- }
-
- case drmtl_opcode_retf1:
- case drmtl_opcode_retf2:
- case drmtl_opcode_retf3:
- case drmtl_opcode_retf4:
- case drmtl_opcode_reti1:
- case drmtl_opcode_reti2:
- case drmtl_opcode_reti3:
- case drmtl_opcode_reti4:
- {
- return drmtl_codegen_glsl_write_instruction_ret(pCodegen, pInstruction);
- }
-
-
- default:
- {
- // Unknown or unsupported opcode.
- break;
- }
- }
- }
-
- return 0;
-}
-
-bool drmtl_codegen_glsl_write_instructions(drmtl_codegen_glsl* pCodegen, drmtl_instruction* pInstructions, unsigned int instructionCount)
-{
- assert(pCodegen != NULL);
- assert(pInstructions != NULL);
-
- for (unsigned int iInstruction = 0; iInstruction < instructionCount; ++iInstruction)
- {
- drmtl_instruction* pInstruction = pInstructions + iInstruction;
- assert(pInstruction != NULL);
-
- if (!drmtl_codegen_glsl_write_instruction(pCodegen, pInstruction))
- {
- return 0;
- }
- }
-
- return 1;
-}
-
-bool drmtl_codegen_glsl_channel_function_begin(drmtl_codegen_glsl* pCodegen, drmtl_channel_header* pChannelHeader)
-{
- assert(pCodegen != NULL);
- assert(pChannelHeader != NULL);
-
- // {\n
- bool result =
- drmtl_codegen_glsl_write_type(pCodegen, pChannelHeader->channel.type) &&
- drmtl_codegen_glsl_write(pCodegen, " ") &&
- drmtl_codegen_glsl_write(pCodegen, pChannelHeader->channel.name) &&
- drmtl_codegen_glsl_write(pCodegen, "() {\n");
- if (result)
- {
- pCodegen->indentationLevel += 4;
- }
-
- return result;
-}
-
-bool drmtl_codegen_glsl_channel_function_close(drmtl_codegen_glsl* pCodegen)
-{
- assert(pCodegen != NULL);
-
- if (pCodegen->indentationLevel > 4) {
- pCodegen->indentationLevel -= 4;
- } else {
- pCodegen->indentationLevel = 0;
- }
-
- return drmtl_codegen_glsl_write(pCodegen, "}\n");
-}
-
-bool drmtl_codegen_glsl_channel(drmtl_material* pMaterial, const char* channelName, char* codeOut, size_t codeOutSizeInBytes, size_t* pBytesWrittenOut)
-{
- if (pMaterial != NULL)
- {
- drmtl_header* pHeader = drmtl_getheader(pMaterial);
- if (pHeader != NULL)
- {
- drmtl_channel_header* pChannelHeader = drmtl_getchannelheaderbyname(pMaterial, channelName);
- if (pChannelHeader != NULL)
- {
- drmtl_codegen_glsl codegen;
- codegen.pBufferOut = codeOut;
- codegen.bufferOutSizeInBytes = codeOutSizeInBytes;
- codegen.runningLength = 0;
- codegen.pMaterial = pMaterial;
- codegen.pIdentifiers = drmtl_getidentifiers(pMaterial);
- codegen.identifierCount = drmtl_getidentifiercount(pMaterial);
- codegen.indentationLevel = 0;
-
- if (drmtl_codegen_glsl_channel_function_begin(&codegen, pChannelHeader))
- {
- drmtl_instruction* pInstructions = (drmtl_instruction*)(pChannelHeader + 1);
- assert(pInstructions != NULL);
-
- if (drmtl_codegen_glsl_write_instructions(&codegen, pInstructions, pChannelHeader->instructionCount))
- {
- bool result = drmtl_codegen_glsl_channel_function_close(&codegen);
- if (result)
- {
- if (pBytesWrittenOut != NULL)
- {
- *pBytesWrittenOut = codegen.runningLength + 1;
- }
- }
-
- return result;
- }
- }
- }
- }
- }
-
- return 0;
-}
-
-
-
-bool drmtl_codegen_glsl_uniform(drmtl_codegen_glsl* pCodegen, drmtl_input* pInput)
-{
- assert(pCodegen != NULL);
- assert(pInput != NULL);
-
- if (pInput->identifierIndex < pCodegen->identifierCount)
- {
- drmtl_identifier* pIdentifier = pCodegen->pIdentifiers + pInput->identifierIndex;
- assert(pIdentifier != NULL);
-
- // uniform ;
- return
- drmtl_codegen_glsl_write(pCodegen, "uniform ") &&
- drmtl_codegen_glsl_write_type(pCodegen, pIdentifier->type) &&
- drmtl_codegen_glsl_write(pCodegen, " ") &&
- drmtl_codegen_glsl_write(pCodegen, pIdentifier->name) &&
- drmtl_codegen_glsl_write(pCodegen, ";\n");
- }
-
- return 0;
-}
-
-bool drmtl_codegen_glsl_uniforms(drmtl_material* pMaterial, char* codeOut, size_t codeOutSizeInBytes, size_t* pBytesWritteOut)
-{
- if (pMaterial != NULL)
- {
- drmtl_codegen_glsl codegen;
- codegen.pBufferOut = codeOut;
- codegen.bufferOutSizeInBytes = codeOutSizeInBytes;
- codegen.runningLength = 0;
- codegen.pMaterial = pMaterial;
- codegen.pIdentifiers = drmtl_getidentifiers(pMaterial);
- codegen.identifierCount = drmtl_getidentifiercount(pMaterial);
- codegen.indentationLevel = 0;
-
-
- unsigned int inputCount = drmtl_getpublicinputcount(pMaterial);
- if (inputCount > 0)
- {
- for (unsigned int iInput = 0; iInput < inputCount; ++iInput)
- {
- drmtl_input* pInput = drmtl_getpublicinputbyindex(pMaterial, iInput);
- assert(pInput != NULL);
-
- if (!drmtl_codegen_glsl_uniform(&codegen, pInput))
- {
- // There was an error writing one of the uniforms. Return false.
- return 0;
- }
- }
- }
- else
- {
- // No inputs. Just write an empty string.
- drmtl_codegen_glsl_write(&codegen, "");
- }
-
- if (pBytesWritteOut != NULL)
- {
- *pBytesWritteOut = codegen.runningLength + 1;
- }
-
- return 1;
- }
-
- return 0;
-}
-
-#if defined(__clang__)
- #pragma GCC diagnostic pop
-#endif
-#endif
-
-#if defined(__clang__)
- #pragma GCC diagnostic pop
-#endif
-#endif
-
-/*
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-For more information, please refer to
-*/
diff --git a/source/engine/thirdparty/dr_libs/old/dr_pcx.h b/source/engine/thirdparty/dr_libs/old/dr_pcx.h
deleted file mode 100644
index 04f9adc..0000000
--- a/source/engine/thirdparty/dr_libs/old/dr_pcx.h
+++ /dev/null
@@ -1,793 +0,0 @@
-// PCX image loader. Public domain. See "unlicense" statement at the end of this file.
-// dr_pcx - v0.3.1 - 2018-09-11
-//
-// David Reid - mackron@gmail.com
-
-// USAGE
-//
-// dr_pcx is a single-file library. To use it, do something like the following in one .c file.
-// #define DR_PCX_IMPLEMENTATION
-// #include "dr_pcx.h"
-//
-// You can then #include this file in other parts of the program as you would with any other header file. Do something like
-// the following to load and decode an image:
-//
-// int width;
-// int height;
-// int components
-// drpcx_uint8* pImageData = drpcx_load_file("my_image.pcx", DRPCX_FALSE, &width, &height, &components, 0);
-// if (pImageData == NULL) {
-// // Failed to load image.
-// }
-//
-// ...
-//
-// drpcx_free(pImageData);
-//
-// The boolean parameter (second argument in the above example) is whether or not the image should be flipped upside down.
-//
-//
-//
-// OPTIONS
-// #define these options before including this file.
-//
-// #define DR_PCX_NO_STDIO
-// Disable drpcx_load_file().
-//
-//
-//
-// QUICK NOTES
-// - 2-bpp/4-plane and 4-bpp/1-plane formats have not been tested.
-
-#ifndef dr_pcx_h
-#define dr_pcx_h
-
-#include
-
-#if defined(_MSC_VER) && _MSC_VER < 1600
-typedef signed char drpcx_int8;
-typedef unsigned char drpcx_uint8;
-typedef signed short drpcx_int16;
-typedef unsigned short drpcx_uint16;
-typedef signed int drpcx_int32;
-typedef unsigned int drpcx_uint32;
-typedef signed __int64 drpcx_int64;
-typedef unsigned __int64 drpcx_uint64;
-#else
-#include
-typedef int8_t drpcx_int8;
-typedef uint8_t drpcx_uint8;
-typedef int16_t drpcx_int16;
-typedef uint16_t drpcx_uint16;
-typedef int32_t drpcx_int32;
-typedef uint32_t drpcx_uint32;
-typedef int64_t drpcx_int64;
-typedef uint64_t drpcx_uint64;
-#endif
-typedef drpcx_uint8 drpcx_bool8;
-typedef drpcx_uint32 drpcx_bool32;
-#define DRPCX_TRUE 1
-#define DRPCX_FALSE 0
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Callback for when data is read. Return value is the number of bytes actually read.
-typedef size_t (* drpcx_read_proc)(void* userData, void* bufferOut, size_t bytesToRead);
-
-
-// Loads a PCX file using the given callbacks.
-drpcx_uint8* drpcx_load(drpcx_read_proc onRead, void* pUserData, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents);
-
-// Frees memory returned by drpcx_load() and family.
-void drpcx_free(void* pReturnValueFromLoad);
-
-
-#ifndef DR_PCX_NO_STDIO
-// Loads an PCX file from an actual file.
-drpcx_uint8* drpcx_load_file(const char* filename, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents);
-#endif
-
-// Helper for loading an PCX file from a block of memory.
-drpcx_uint8* drpcx_load_memory(const void* data, size_t dataSize, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // dr_pcx_h
-
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// IMPLEMENTATION
-//
-///////////////////////////////////////////////////////////////////////////////
-#ifdef DR_PCX_IMPLEMENTATION
-#include
-#include
-#include
-
-#ifndef DR_PCX_NO_STDIO
-#include
-
-static size_t drpcx__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
-{
- return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData);
-}
-
-drpcx_uint8* drpcx_load_file(const char* filename, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents)
-{
- FILE* pFile;
-#ifdef _MSC_VER
- if (fopen_s(&pFile, filename, "rb") != 0) {
- return NULL;
- }
-#else
- pFile = fopen(filename, "rb");
- if (pFile == NULL) {
- return NULL;
- }
-#endif
-
- drpcx_uint8* pImageData = drpcx_load(drpcx__on_read_stdio, pFile, flipped, x, y, internalComponents, desiredComponents);
-
- fclose(pFile);
- return pImageData;
-}
-#endif // DR_PCX_NO_STDIO
-
-
-typedef struct
-{
- // A pointer to the beginning of the data. We use a char as the type here for easy offsetting.
- const unsigned char* data;
- size_t dataSize;
- size_t currentReadPos;
-} drpcx_memory;
-
-static size_t drpcx__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead)
-{
- drpcx_memory* memory = (drpcx_memory*)pUserData;
- assert(memory != NULL);
- assert(memory->dataSize >= memory->currentReadPos);
-
- size_t bytesRemaining = memory->dataSize - memory->currentReadPos;
- if (bytesToRead > bytesRemaining) {
- bytesToRead = bytesRemaining;
- }
-
- if (bytesToRead > 0) {
- memcpy(bufferOut, memory->data + memory->currentReadPos, bytesToRead);
- memory->currentReadPos += bytesToRead;
- }
-
- return bytesToRead;
-}
-
-drpcx_uint8* drpcx_load_memory(const void* data, size_t dataSize, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents)
-{
- drpcx_memory memory;
- memory.data = (const unsigned char*)data;
- memory.dataSize = dataSize;
- memory.currentReadPos = 0;
- return drpcx_load(drpcx__on_read_memory, &memory, flipped, x, y, internalComponents, desiredComponents);
-}
-
-
-typedef struct
-{
- drpcx_uint8 header;
- drpcx_uint8 version;
- drpcx_uint8 encoding;
- drpcx_uint8 bpp;
- drpcx_uint16 left;
- drpcx_uint16 top;
- drpcx_uint16 right;
- drpcx_uint16 bottom;
- drpcx_uint16 hres;
- drpcx_uint16 vres;
- drpcx_uint8 palette16[48];
- drpcx_uint8 reserved1;
- drpcx_uint8 bitPlanes;
- drpcx_uint16 bytesPerLine;
- drpcx_uint16 paletteType;
- drpcx_uint16 screenSizeH;
- drpcx_uint16 screenSizeV;
- drpcx_uint8 reserved2[54];
-} drpcx_header;
-
-typedef struct
-{
- drpcx_read_proc onRead;
- void* pUserData;
- drpcx_bool32 flipped;
- drpcx_header header;
-
- drpcx_uint32 width;
- drpcx_uint32 height;
- drpcx_uint32 components; // 3 = RGB; 4 = RGBA. Only 3 and 4 are supported.
- drpcx_uint8* pImageData;
-} drpcx;
-
-
-static drpcx_uint8 drpcx__read_byte(drpcx* pPCX)
-{
- drpcx_uint8 byte = 0;
- pPCX->onRead(pPCX->pUserData, &byte, 1);
-
- return byte;
-}
-
-static drpcx_uint8* drpcx__row_ptr(drpcx* pPCX, drpcx_uint32 row)
-{
- drpcx_uint32 stride = pPCX->width * pPCX->components;
-
- drpcx_uint8* pRow = pPCX->pImageData;
- if (pPCX->flipped) {
- pRow += (pPCX->height - row - 1) * stride;
- } else {
- pRow += row * stride;
- }
-
- return pRow;
-}
-
-static drpcx_uint8 drpcx__rle(drpcx* pPCX, drpcx_uint8* pRLEValueOut)
-{
- drpcx_uint8 rleCount;
- drpcx_uint8 rleValue;
-
- rleValue = drpcx__read_byte(pPCX);
- if ((rleValue & 0xC0) == 0xC0) {
- rleCount = rleValue & 0x3F;
- rleValue = drpcx__read_byte(pPCX);
- } else {
- rleCount = 1;
- }
-
-
- *pRLEValueOut = rleValue;
- return rleCount;
-}
-
-
-drpcx_bool32 drpcx__decode_1bit(drpcx* pPCX)
-{
- drpcx_uint8 rleCount = 0;
- drpcx_uint8 rleValue = 0;
-
- switch (pPCX->header.bitPlanes)
- {
- case 1:
- {
- for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
- drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
- for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
- if (rleCount == 0) {
- rleCount = drpcx__rle(pPCX, &rleValue);
- }
- rleCount -= 1;
-
- for (int bit = 0; (bit < 8) && ((x*8 + bit) < pPCX->width); ++bit) {
- drpcx_uint8 mask = (1 << (7 - bit));
- drpcx_uint8 paletteIndex = (rleValue & mask) >> (7 - bit);
-
- pRow[0] = paletteIndex * 255;
- pRow[1] = paletteIndex * 255;
- pRow[2] = paletteIndex * 255;
- pRow += 3;
- }
- }
- }
-
- return DRPCX_TRUE;
-
- } break;
-
- case 2:
- case 3:
- case 4:
- {
- for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
- for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
- drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
- for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
- if (rleCount == 0) {
- rleCount = drpcx__rle(pPCX, &rleValue);
- }
- rleCount -= 1;
-
- for (int bit = 0; (bit < 8) && ((x*8 + bit) < pPCX->width); ++bit) {
- drpcx_uint8 mask = (1 << (7 - bit));
- drpcx_uint8 paletteIndex = (rleValue & mask) >> (7 - bit);
-
- pRow[0] |= ((paletteIndex & 0x01) << c);
- pRow += pPCX->components;
- }
- }
- }
-
-
- drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
- for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
- drpcx_uint8 paletteIndex = pRow[0];
- for (drpcx_uint32 c = 0; c < pPCX->components; ++c) {
- pRow[c] = pPCX->header.palette16[paletteIndex*3 + c];
- }
-
- pRow += pPCX->components;
- }
- }
-
- return DRPCX_TRUE;
- }
-
- default: return DRPCX_FALSE;
- }
-}
-
-drpcx_bool32 drpcx__decode_2bit(drpcx* pPCX)
-{
- drpcx_uint8 rleCount = 0;
- drpcx_uint8 rleValue = 0;
-
- switch (pPCX->header.bitPlanes)
- {
- case 1:
- {
- drpcx_uint8 paletteCGA[48];
- paletteCGA[ 0] = 0x00; paletteCGA[ 1] = 0x00; paletteCGA[ 2] = 0x00; // #000000
- paletteCGA[ 3] = 0x00; paletteCGA[ 4] = 0x00; paletteCGA[ 5] = 0xAA; // #0000AA
- paletteCGA[ 6] = 0x00; paletteCGA[ 7] = 0xAA; paletteCGA[ 8] = 0x00; // #00AA00
- paletteCGA[ 9] = 0x00; paletteCGA[10] = 0xAA; paletteCGA[11] = 0xAA; // #00AAAA
- paletteCGA[12] = 0xAA; paletteCGA[13] = 0x00; paletteCGA[14] = 0x00; // #AA0000
- paletteCGA[15] = 0xAA; paletteCGA[16] = 0x00; paletteCGA[17] = 0xAA; // #AA00AA
- paletteCGA[18] = 0xAA; paletteCGA[19] = 0x55; paletteCGA[20] = 0x00; // #AA5500
- paletteCGA[21] = 0xAA; paletteCGA[22] = 0xAA; paletteCGA[23] = 0xAA; // #AAAAAA
- paletteCGA[24] = 0x55; paletteCGA[25] = 0x55; paletteCGA[26] = 0x55; // #555555
- paletteCGA[27] = 0x55; paletteCGA[28] = 0x55; paletteCGA[29] = 0xFF; // #5555FF
- paletteCGA[30] = 0x55; paletteCGA[31] = 0xFF; paletteCGA[32] = 0x55; // #55FF55
- paletteCGA[33] = 0x55; paletteCGA[34] = 0xFF; paletteCGA[35] = 0xFF; // #55FFFF
- paletteCGA[36] = 0xFF; paletteCGA[37] = 0x55; paletteCGA[38] = 0x55; // #FF5555
- paletteCGA[39] = 0xFF; paletteCGA[40] = 0x55; paletteCGA[41] = 0xFF; // #FF55FF
- paletteCGA[42] = 0xFF; paletteCGA[43] = 0xFF; paletteCGA[44] = 0x55; // #FFFF55
- paletteCGA[45] = 0xFF; paletteCGA[46] = 0xFF; paletteCGA[47] = 0xFF; // #FFFFFF
-
- drpcx_uint8 cgaBGColor = pPCX->header.palette16[0] >> 4;
- drpcx_uint8 i = (pPCX->header.palette16[3] & 0x20) >> 5;
- drpcx_uint8 p = (pPCX->header.palette16[3] & 0x40) >> 6;
- //drpcx_uint8 c = (pPCX->header.palette16[3] & 0x80) >> 7; // Color or monochrome. How is monochrome handled?
-
- for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
- drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
- for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
- if (rleCount == 0) {
- rleCount = drpcx__rle(pPCX, &rleValue);
- }
- rleCount -= 1;
-
- for (int bit = 0; bit < 4; ++bit) {
- if (x*4 + bit < pPCX->width) {
- drpcx_uint8 mask = (3 << ((3 - bit) * 2));
- drpcx_uint8 paletteIndex = (rleValue & mask) >> ((3 - bit) * 2);
-
- drpcx_uint8 cgaIndex;
- if (paletteIndex == 0) { // Background.
- cgaIndex = cgaBGColor;
- } else { // Foreground
- cgaIndex = (((paletteIndex << 1) + p) + (i << 3));
- }
-
- pRow[0] = paletteCGA[cgaIndex*3 + 0];
- pRow[1] = paletteCGA[cgaIndex*3 + 1];
- pRow[2] = paletteCGA[cgaIndex*3 + 2];
- pRow += 3;
- }
- }
- }
- }
-
- // TODO: According to http://www.fysnet.net/pcxfile.htm, we should use the palette at the end of the file
- // instead of the standard CGA palette if the version is equal to 5. With my test files the palette
- // at the end of the file does not exist. Research this one.
- if (pPCX->header.version == 5) {
- drpcx_uint8 paletteMarker = drpcx__read_byte(pPCX);
- if (paletteMarker == 0x0C) {
- // TODO: Implement Me.
- }
- }
-
- return DRPCX_TRUE;
- };
-
- case 4:
- {
- // NOTE: This is completely untested. If anybody knows where I can get a test file please let me know or send it through to me!
- // TODO: Test Me.
-
- for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
- for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
- drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
- for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
- if (rleCount == 0) {
- rleCount = drpcx__rle(pPCX, &rleValue);
- }
- rleCount -= 1;
-
- for (int bitpair = 0; (bitpair < 4) && ((x*4 + bitpair) < pPCX->width); ++bitpair) {
- drpcx_uint8 mask = (4 << (3 - bitpair));
- drpcx_uint8 paletteIndex = (rleValue & mask) >> (3 - bitpair);
-
- pRow[0] |= ((paletteIndex & 0x03) << (c*2));
- pRow += pPCX->components;
- }
- }
- }
-
-
- drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
- for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
- drpcx_uint8 paletteIndex = pRow[0];
- for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
- pRow[c] = pPCX->header.palette16[paletteIndex*3 + c];
- }
-
- pRow += pPCX->components;
- }
- }
-
- return DRPCX_TRUE;
- };
-
- default: return DRPCX_FALSE;
- }
-}
-
-drpcx_bool32 drpcx__decode_4bit(drpcx* pPCX)
-{
- // NOTE: This is completely untested. If anybody knows where I can get a test file please let me know or send it through to me!
- // TODO: Test Me.
-
- if (pPCX->header.bitPlanes > 1) {
- return DRPCX_FALSE;
- }
-
- drpcx_uint8 rleCount = 0;
- drpcx_uint8 rleValue = 0;
-
- for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
- for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
- drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
- for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
- if (rleCount == 0) {
- rleCount = drpcx__rle(pPCX, &rleValue);
- }
- rleCount -= 1;
-
- for (int nibble = 0; (nibble < 2) && ((x*2 + nibble) < pPCX->width); ++nibble)
- {
- drpcx_uint8 mask = (4 << (1 - nibble));
- drpcx_uint8 paletteIndex = (rleValue & mask) >> (1 - nibble);
-
- pRow[0] |= ((paletteIndex & 0x0F) << (c*4));
- pRow += pPCX->components;
- }
- }
- }
-
-
- drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
- for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
- drpcx_uint8 paletteIndex = pRow[0];
- for (drpcx_uint32 c = 0; c < pPCX->components; ++c) {
- pRow[c] = pPCX->header.palette16[paletteIndex*3 + c];
- }
-
- pRow += pPCX->components;
- }
- }
-
- return DRPCX_TRUE;
-}
-
-drpcx_bool32 drpcx__decode_8bit(drpcx* pPCX)
-{
- drpcx_uint8 rleCount = 0;
- drpcx_uint8 rleValue = 0;
- drpcx_uint32 stride = pPCX->width * pPCX->components;
-
- switch (pPCX->header.bitPlanes)
- {
- case 1:
- {
- for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
- drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
- for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
- if (rleCount == 0) {
- rleCount = drpcx__rle(pPCX, &rleValue);
- }
- rleCount -= 1;
-
- if (x < pPCX->width) {
- pRow[0] = rleValue;
- pRow[1] = rleValue;
- pRow[2] = rleValue;
- pRow += 3;
- }
- }
- }
-
- // At this point we can know if we are dealing with a palette or a grayscale image by checking the next byte. If it's equal to 0x0C, we
- // need to do a simple palette lookup.
- drpcx_uint8 paletteMarker = drpcx__read_byte(pPCX);
- if (paletteMarker == 0x0C) {
- // A palette is present - we need to do a second pass.
- drpcx_uint8 palette256[768];
- if (pPCX->onRead(pPCX->pUserData, palette256, sizeof(palette256)) != sizeof(palette256)) {
- return DRPCX_FALSE;
- }
-
- for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
- drpcx_uint8* pRow = pPCX->pImageData + (y * stride);
- for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
- drpcx_uint8 index = pRow[0];
- pRow[0] = palette256[index*3 + 0];
- pRow[1] = palette256[index*3 + 1];
- pRow[2] = palette256[index*3 + 2];
- pRow += 3;
- }
- }
- }
-
- return DRPCX_TRUE;
- }
-
- case 3:
- case 4:
- {
- for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
- for (drpcx_uint32 c = 0; c < pPCX->components; ++c) {
- drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
- for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
- if (rleCount == 0) {
- rleCount = drpcx__rle(pPCX, &rleValue);
- }
- rleCount -= 1;
-
- if (x < pPCX->width) {
- pRow[c] = rleValue;
- pRow += pPCX->components;
- }
- }
- }
- }
-
- return DRPCX_TRUE;
- }
- }
-
- return DRPCX_TRUE;
-}
-
-drpcx_uint8* drpcx_load(drpcx_read_proc onRead, void* pUserData, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents)
-{
- if (onRead == NULL) return NULL;
- if (desiredComponents > 4) return NULL;
-
- drpcx pcx;
- pcx.onRead = onRead;
- pcx.pUserData = pUserData;
- pcx.flipped = flipped;
- if (onRead(pUserData, &pcx.header, sizeof(pcx.header)) != sizeof(pcx.header)) {
- return NULL; // Failed to read the header.
- }
-
- if (pcx.header.header != 10) {
- return NULL; // Not a PCX file.
- }
-
- if (pcx.header.encoding != 1) {
- return NULL; // Not supporting non-RLE encoding. Would assume a value of 0 indicates raw, unencoded, but that is apparently never used.
- }
-
- if (pcx.header.bpp != 1 && pcx.header.bpp != 2 && pcx.header.bpp != 4 && pcx.header.bpp != 8) {
- return NULL; // Unsupported pixel format.
- }
-
-
- if (pcx.header.left > pcx.header.right) {
- drpcx_uint16 temp = pcx.header.left;
- pcx.header.left = pcx.header.right;
- pcx.header.right = temp;
- }
- if (pcx.header.top > pcx.header.bottom) {
- drpcx_uint16 temp = pcx.header.top;
- pcx.header.top = pcx.header.bottom;
- pcx.header.bottom = temp;
- }
-
- pcx.width = pcx.header.right - pcx.header.left + 1;
- pcx.height = pcx.header.bottom - pcx.header.top + 1;
- pcx.components = (pcx.header.bpp == 8 && pcx.header.bitPlanes == 4) ? 4 : 3;
-
- size_t dataSize = pcx.width * pcx.height * pcx.components;
- pcx.pImageData = (drpcx_uint8*)calloc(1, dataSize); // <-- Clearing to zero is important! Required for proper decoding.
- if (pcx.pImageData == NULL) {
- return NULL; // Failed to allocate memory.
- }
-
- drpcx_bool32 result = DRPCX_FALSE;
- switch (pcx.header.bpp)
- {
- case 1:
- {
- result = drpcx__decode_1bit(&pcx);
- } break;
-
- case 2:
- {
- result = drpcx__decode_2bit(&pcx);
- } break;
-
- case 4:
- {
- result = drpcx__decode_4bit(&pcx);
- } break;
-
- case 8:
- {
- result = drpcx__decode_8bit(&pcx);
- } break;
- }
-
- if (!result) {
- free(pcx.pImageData);
- return NULL;
- }
-
- // There's an annoying amount of branching when loading PCX files so for simplicity I'm doing the component conversion as
- // a second pass.
- if (desiredComponents == 0) desiredComponents = pcx.components;
- if (desiredComponents != (int)pcx.components) {
- drpcx_uint8* pNewImageData = (drpcx_uint8*)malloc(pcx.width * pcx.height * desiredComponents);
- if (pNewImageData == NULL) {
- free(pcx.pImageData);
- return NULL;
- }
-
- drpcx_uint8* pSrcData = pcx.pImageData;
- drpcx_uint8* pDstData = pNewImageData;
- if (desiredComponents < (int)pcx.components) {
- // We're reducing the number of components. Just drop the excess.
- for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
- for (int c = 0; c < desiredComponents; ++c) {
- pDstData[c] = pSrcData[c];
- }
-
- pSrcData += pcx.components;
- pDstData += desiredComponents;
- }
- } else {
- // We're increasing the number of components. Always ensure the alpha channel is set to 0xFF.
- if (pcx.components == 1) {
- for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
- for (int c = 0; c < desiredComponents; ++c) {
- pDstData[c] = pSrcData[0];
- }
-
- pSrcData += pcx.components;
- pDstData += desiredComponents;
- }
- } else if (pcx.components == 2) {
- for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
- pDstData[0] = pSrcData[0];
- pDstData[1] = pSrcData[1];
- pDstData[2] = 0x00;
- if (desiredComponents == 4) pDstData[3] = 0xFF;
-
- pSrcData += pcx.components;
- pDstData += desiredComponents;
- }
- } else {
- assert(pcx.components == 3);
- assert(desiredComponents == 4);
- for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
- pDstData[0] = pSrcData[0];
- pDstData[1] = pSrcData[1];
- pDstData[2] = pSrcData[2];
- pDstData[3] = 0xFF;
-
- pSrcData += pcx.components;
- pDstData += desiredComponents;
- }
- }
- }
-
- free(pcx.pImageData);
- pcx.pImageData = pNewImageData;
- }
-
- if (x) *x = pcx.width;
- if (y) *y = pcx.height;
- if (internalComponents) *internalComponents = pcx.components;
- return pcx.pImageData;
-}
-
-void drpcx_free(void* pReturnValueFromLoad)
-{
- free(pReturnValueFromLoad);
-}
-
-#endif // DR_PCX_IMPLEMENTATION
-
-
-// REVISION HISTORY
-//
-// v0.3.1 - 2018-09-11
-// - Styling fixes.
-// - Fix a typo.
-//
-// v0.3 - 2018-02-08
-// - API CHANGE: Rename dr_* types to drpcx_*.
-//
-// v0.2c - 2018-02-07
-// - Fix a crash.
-//
-// v0.2b - 2018-02-02
-// - Fix compilation error.
-//
-// v0.2a - 2017-07-16
-// - Change underlying type for booleans to unsigned.
-//
-// v0.2 - 2016-10-28
-// - API CHANGE: Add a parameter to drpcx_load() and family to control the number of output components.
-// - Use custom sized types rather than built-in ones to improve support for older MSVC compilers.
-//
-// v0.1c - 2016-10-23
-// - A minor change to drpcx_bool8 and drpcx_bool32 types.
-//
-// v0.1b - 2016-10-11
-// - Use drpcx_bool32 instead of the built-in "bool" type. The reason for this change is that it helps maintain API/ABI consistency
-// between C and C++ builds.
-//
-// v0.1a - 2016-09-18
-// - Change date format to ISO 8601 (YYYY-MM-DD)
-//
-// v0.1 - 2016-05-04
-// - Initial versioned release.
-
-
-// TODO
-// - Test 2-bpp/4-plane and 4-bpp/1-plane formats.
-
-
-/*
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-For more information, please refer to
-*/
-
diff --git a/source/engine/thirdparty/dr_libs/tests/README.md b/source/engine/thirdparty/dr_libs/tests/README.md
deleted file mode 100644
index 9a0eb4b..0000000
--- a/source/engine/thirdparty/dr_libs/tests/README.md
+++ /dev/null
@@ -1,49 +0,0 @@
-Building
-========
-Move into this directory and run the build script for the relevant platform. Run from this directory.
-
- clear && ./build_opus && ./bin/dr_opus_test_0
-
-Alternatively you can compile a specific test manually:
-
- clear && gcc ./opus/dr_opus_test_0 -o ./bin/dr_opus_test_0 && ./bin/dr_opus_test_0
-
-Test vectors will be loaded from the "testvectors" folder, relative to this directory. Therefore, you need to run
-each test program from this directory:
-
- ./bin/dr_opus_test_0
-
-
-Building and Running WAV Tests
-------------------------------
-The WAV tests use libsndfile as a benchmark. The tests programs dynamically link to libsndfile at runtime which
-means you don't need to link to it at compile time. However, you will need the headers installed in a standard
-location. The batch files for the Windows build will allow you to customize the include path. On the Windows build
-you will need to drop two versions of libsndfile-1.dll into the bin directory. For the 32-bit build you need to
-name it libsndfile-1-x86.dll and for the 64-bit build you need to name it libsndfile-1-x64.dll.
-
-
-Test Vectors
-============
-In order to run certain tests you will need to download test vectors for the relevant project and place them into the
-"testvectors" folder.
-
-Opus
-----
-- Download both the original and new test vectors from https://opus-codec.org/testvectors/ and place them into
- the "testvectors/opus" folder.
-- Download the Ogg Opus test vectors from https://wiki.xiph.org/OggOpus/testvectors and place them into the
- "testvectors/opus" folder.
-- The folder structure should like like the following:
- - testvectors
- - opus
- - opus_testvectors
- - opus_newvectors
- - oggopus
- - failure_cases
- - opus_multichannel_examples
-
-FLAC
-----
-- Download the FLAC testbench from https://wiki.hydrogenaud.io/index.php?title=FLAC_decoder_testbench and place
- them into the "testvectors/flac/testbench" folder.
diff --git a/source/engine/thirdparty/dr_libs/tests/bin/DO_NOT_DELETE.md b/source/engine/thirdparty/dr_libs/tests/bin/DO_NOT_DELETE.md
deleted file mode 100644
index e69de29..0000000
diff --git a/source/engine/thirdparty/dr_libs/tests/build_flac_linux.sh b/source/engine/thirdparty/dr_libs/tests/build_flac_linux.sh
deleted file mode 100755
index 21297f0..0000000
--- a/source/engine/thirdparty/dr_libs/tests/build_flac_linux.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-gcc ./flac/dr_flac_test_0.c -o ./bin/dr_flac_test_0 -std=c89 -ansi -pedantic -O3 -s -Wall -ldl
-gcc ./flac/dr_flac_decoding.c -o ./bin/dr_flac_decoding -std=c89 -ansi -pedantic -Wall -O3 -s -lFLAC -ldl
-gcc ./flac/dr_flac_seeking.c -o ./bin/dr_flac_seeking -std=c89 -ansi -pedantic -Wall -O3 -s -lFLAC -ldl
diff --git a/source/engine/thirdparty/dr_libs/tests/build_flac_win32.bat b/source/engine/thirdparty/dr_libs/tests/build_flac_win32.bat
deleted file mode 100644
index 56c2a67..0000000
--- a/source/engine/thirdparty/dr_libs/tests/build_flac_win32.bat
+++ /dev/null
@@ -1,37 +0,0 @@
-:: NOTES
-::
-:: These tests use libFLAC as a benchmark. Since Windows doesn't have good standard paths for library files, this
-:: script will use "flac/include" as an additional search path for headers and "flac/lib/win32" as an additional
-:: search path for libraries. You will need to place FLAC headers in the "flac/include/FLAC" directory and libogg
-:: headers int the "flac/include/ogg" directory.
-::
-:: Examples are linked against "-lFLAC" and "-logg". These need to be placed in a standard directory or "flac/lib/win32".
-
-@echo off
-SET c_compiler=gcc
-SET cpp_compiler=g++
-
-:: Configure the "arch" option to test different instruction sets.
-SET arch=
-SET arch=-msse4.1
-::SET arch=-mfpu=neon
-
-:: libFLAC and libogg are required for benchmarking.
-SET libFLAC=-I./flac/include -L./flac/lib/win32 -lFLAC -logg
-
-:: C options
-SET c_options=-std=c89 -ansi
-
-:: C++ options
-SET cpp_options=
-
-SET options=-Wall -Wpedantic -pedantic -O3 -s -DNDEBUG %arch% %libFLAC%
-
-SET buildc=%c_compiler% %c_options%
-SET buildcpp=%cpp_compiler% %cpp_options%
-@echo on
-
-%buildc% ./flac/dr_flac_test_0.c -o ./bin/dr_flac_test_0.exe %options%
-%buildcpp% ./flac/dr_flac_test_0.cpp -o ./bin/dr_flac_test_0_cpp.exe %options%
-%buildc% ./flac/dr_flac_decoding.c -o ./bin/dr_flac_decoding.exe %options%
-%buildcpp% ./flac/dr_flac_decoding.cpp -o ./bin/dr_flac_decoding_cpp.exe %options%
\ No newline at end of file
diff --git a/source/engine/thirdparty/dr_libs/tests/build_mp3_win32.bat b/source/engine/thirdparty/dr_libs/tests/build_mp3_win32.bat
deleted file mode 100644
index 2692cff..0000000
--- a/source/engine/thirdparty/dr_libs/tests/build_mp3_win32.bat
+++ /dev/null
@@ -1,2 +0,0 @@
-gcc ./mp3/dr_mp3_test_0.c -o ./bin/dr_mp3_test_0.exe -std=c89 -ansi -pedantic -Wall
-g++ ./mp3/dr_mp3_test_0.cpp -o ./bin/dr_mp3_test_0.exe -pedantic -Wall
\ No newline at end of file
diff --git a/source/engine/thirdparty/dr_libs/tests/build_opus_win32.bat b/source/engine/thirdparty/dr_libs/tests/build_opus_win32.bat
deleted file mode 100644
index dbb8a85..0000000
--- a/source/engine/thirdparty/dr_libs/tests/build_opus_win32.bat
+++ /dev/null
@@ -1 +0,0 @@
-gcc ./opus/dr_opus_decoding.c -o ./bin/dr_opus_decoding.exe -std=c89 -ansi -pedantic -Wall -Wextra
\ No newline at end of file
diff --git a/source/engine/thirdparty/dr_libs/tests/build_wav_win32.bat b/source/engine/thirdparty/dr_libs/tests/build_wav_win32.bat
deleted file mode 100644
index e0f948c..0000000
--- a/source/engine/thirdparty/dr_libs/tests/build_wav_win32.bat
+++ /dev/null
@@ -1,38 +0,0 @@
-:: NOTES
-::
-:: These tests use libsndfile as a benchmark. Since Windows doesn't have good standard paths for library files, this
-:: script will use "wav/include" as an additional search path for headers. You will need to place sndfile.h to the
-:: "wav/include" directory. Tests will link to libsndfile dynamically at run-time. On the Windows build you'll just
-:: need to put a copy of the 32- and 64-bit versions of libsndfile-1.dll into the "bin" directory, with the names
-:: libsndfile-1-x86.dll and libsndfile-1-x64.dll respectively. Both versions are required so that both the 32- and
-:: 64-bit builds can be tested and benchmarked.
-
-@echo off
-
-SET c_compiler=gcc
-SET cpp_compiler=g++
-
-:: Configure the "arch" option to test different instruction sets.
-SET arch=
-SET arch=-msse4.1
-::SET arch=-mfpu=neon
-
-:: libsndfile is required for benchmarking.
-SET libsndfile=-I./wav/include
-
-:: C options
-SET c_options=-std=c89 -ansi
-
-:: C++ options
-SET cpp_options=
-
-SET options=-Wall -Wpedantic -pedantic -O3 -s -DNDEBUG %arch% %libsndfile%
-
-SET buildc=%c_compiler% %c_options% %options%
-SET buildcpp=%cpp_compiler% %cpp_options% %options%
-@echo on
-
-%buildc% ./wav/dr_wav_test_0.c -o ./bin/dr_wav_test_0.exe
-%buildcpp% ./wav/dr_wav_test_0.cpp -o ./bin/dr_wav_test_0_cpp.exe
-%buildc% ./wav/dr_wav_decoding.c -o ./bin/dr_wav_decoding.exe
-%buildcpp% ./wav/dr_wav_decoding.cpp -o ./bin/dr_wav_decoding_cpp.exe
\ No newline at end of file
diff --git a/source/engine/thirdparty/dr_libs/tests/common/dr_common.c b/source/engine/thirdparty/dr_libs/tests/common/dr_common.c
deleted file mode 100644
index 46b34b8..0000000
--- a/source/engine/thirdparty/dr_libs/tests/common/dr_common.c
+++ /dev/null
@@ -1,957 +0,0 @@
-
-#ifdef _WIN32
-#include
-#endif
-
-#if defined(_MSC_VER) || defined(__DMC__)
-#else
-#include
-#endif
-
-#include
-#include
-#include /* So we can seed the random number generator based on time. */
-#include
-
-#if !defined(_WIN32)
-#include
-#include
-#include
-#include
-#include
-#endif
-
-#include /* For size_t. */
-
-/* Sized types. Prefer built-in types. Fall back to stdint. */
-#ifdef _MSC_VER
- #if defined(__clang__)
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wlanguage-extension-token"
- #pragma GCC diagnostic ignored "-Wlong-long"
- #pragma GCC diagnostic ignored "-Wc++11-long-long"
- #endif
- typedef signed __int8 dr_int8;
- typedef unsigned __int8 dr_uint8;
- typedef signed __int16 dr_int16;
- typedef unsigned __int16 dr_uint16;
- typedef signed __int32 dr_int32;
- typedef unsigned __int32 dr_uint32;
- typedef signed __int64 dr_int64;
- typedef unsigned __int64 dr_uint64;
- #if defined(__clang__)
- #pragma GCC diagnostic pop
- #endif
-#else
- #define MA_HAS_STDINT
- #include
- typedef int8_t dr_int8;
- typedef uint8_t dr_uint8;
- typedef int16_t dr_int16;
- typedef uint16_t dr_uint16;
- typedef int32_t dr_int32;
- typedef uint32_t dr_uint32;
- typedef int64_t dr_int64;
- typedef uint64_t dr_uint64;
-#endif
-
-#ifdef MA_HAS_STDINT
- typedef uintptr_t dr_uintptr;
-#else
- #if defined(_WIN32)
- #if defined(_WIN64)
- typedef dr_uint64 dr_uintptr;
- #else
- typedef dr_uint32 dr_uintptr;
- #endif
- #elif defined(__GNUC__)
- #if defined(__LP64__)
- typedef dr_uint64 dr_uintptr;
- #else
- typedef dr_uint32 dr_uintptr;
- #endif
- #else
- typedef dr_uint64 dr_uintptr; /* Fallback. */
- #endif
-#endif
-
-typedef dr_uint8 dr_bool8;
-typedef dr_uint32 dr_bool32;
-#define DR_TRUE 1
-#define DR_FALSE 0
-
-typedef void* dr_handle;
-typedef void* dr_ptr;
-typedef void (* dr_proc)(void);
-
-#if defined(SIZE_MAX)
- #define DR_SIZE_MAX SIZE_MAX
-#else
- #define DR_SIZE_MAX 0xFFFFFFFF
-#endif
-
-/*
-Return Values:
- 0: Success
- 22: EINVAL
- 34: ERANGE
-
-Not using symbolic constants for errors because I want to avoid #including errno.h
-*/
-int dr_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
-{
- size_t i;
-
- if (dst == 0) {
- return 22;
- }
- if (dstSizeInBytes == 0) {
- return 34;
- }
- if (src == 0) {
- dst[0] = '\0';
- return 22;
- }
-
- for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
- dst[i] = src[i];
- }
-
- if (i < dstSizeInBytes) {
- dst[i] = '\0';
- return 0;
- }
-
- dst[0] = '\0';
- return 34;
-}
-
-int dr_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
-{
- size_t maxcount;
- size_t i;
-
- if (dst == 0) {
- return 22;
- }
- if (dstSizeInBytes == 0) {
- return 34;
- }
- if (src == 0) {
- dst[0] = '\0';
- return 22;
- }
-
- maxcount = count;
- if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */
- maxcount = dstSizeInBytes - 1;
- }
-
- for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
- dst[i] = src[i];
- }
-
- if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
- dst[i] = '\0';
- return 0;
- }
-
- dst[0] = '\0';
- return 34;
-}
-
-int dr_strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
-{
- char* dstorig;
-
- if (dst == 0) {
- return 22;
- }
- if (dstSizeInBytes == 0) {
- return 34;
- }
- if (src == 0) {
- dst[0] = '\0';
- return 22;
- }
-
- dstorig = dst;
-
- while (dstSizeInBytes > 0 && dst[0] != '\0') {
- dst += 1;
- dstSizeInBytes -= 1;
- }
-
- if (dstSizeInBytes == 0) {
- return 22; /* Unterminated. */
- }
-
-
- while (dstSizeInBytes > 0 && src[0] != '\0') {
- *dst++ = *src++;
- dstSizeInBytes -= 1;
- }
-
- if (dstSizeInBytes > 0) {
- dst[0] = '\0';
- } else {
- dstorig[0] = '\0';
- return 34;
- }
-
- return 0;
-}
-
-int dr_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
-{
- char* dstorig;
-
- if (dst == 0) {
- return 22;
- }
- if (dstSizeInBytes == 0) {
- return 34;
- }
- if (src == 0) {
- return 22;
- }
-
- dstorig = dst;
-
- while (dstSizeInBytes > 0 && dst[0] != '\0') {
- dst += 1;
- dstSizeInBytes -= 1;
- }
-
- if (dstSizeInBytes == 0) {
- return 22; /* Unterminated. */
- }
-
-
- if (count == ((size_t)-1)) { /* _TRUNCATE */
- count = dstSizeInBytes - 1;
- }
-
- while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) {
- *dst++ = *src++;
- dstSizeInBytes -= 1;
- count -= 1;
- }
-
- if (dstSizeInBytes > 0) {
- dst[0] = '\0';
- } else {
- dstorig[0] = '\0';
- return 34;
- }
-
- return 0;
-}
-
-/*
-String Helpers
-*/
-int dr_append_path(char* dst, size_t dstSize, const char* base, const char* other)
-{
- int err;
- size_t len;
-
- /* TODO: Return the correct error codes here. */
- if (dst == NULL) {
- return -1;
- }
- if (base == NULL || other == NULL) {
- return -1;
- }
-
- err = dr_strcpy_s(dst, dstSize, base);
- if (err != 0) {
- return err;
- }
-
- len = strlen(dst);
- if (len > 0) {
- /* Append the slash if required. */
- if (dst[len-1] != '/' && dst[len-1] != '\\') {
- err = dr_strcat_s(dst, dstSize, "/");
- if (err != 0) {
- dst[0] = '\0';
- return err;
- }
-
- len += 1; /* +1 to the length to account for the slash. */
- }
- }
-
- err = dr_strcat_s(dst, dstSize, other);
- if (err != 0) {
- dst[0] = '\0';
- return err;
- }
-
- /* Success. */
- return 0;
-}
-
-const char* dr_path_file_name(const char* path)
-{
- const char* fileName = path;
-
- if (path == NULL) {
- return NULL;
- }
-
- /* We just loop through the path until we find the last slash. */
- while (path[0] != '\0') {
- if (path[0] == '/' || path[0] == '\\') {
- fileName = path;
- }
-
- path += 1;
- }
-
- /* At this point the file name is sitting on a slash, so just move forward. */
- while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
- fileName += 1;
- }
-
- return fileName;
-}
-
-const char* dr_extension(const char* path)
-{
- const char* extension = path;
- const char* lastoccurance = NULL;
-
- if (path == NULL) {
- return NULL;
- }
-
- /* Just find the last '.' and return. */
- while (extension[0] != '\0') {
- if (extension[0] == '.') {
- extension += 1;
- lastoccurance = extension;
- }
-
- extension += 1;
- }
-
- return (lastoccurance != 0) ? lastoccurance : extension;
-}
-
-dr_bool32 dr_extension_equal(const char* path, const char* extension)
-{
- const char* ext1;
- const char* ext2;
-
- if (path == NULL || extension == NULL) {
- return 0;
- }
-
- ext1 = extension;
- ext2 = dr_extension(path);
-
-#if defined(_MSC_VER) || defined(__DMC__)
- return _stricmp(ext1, ext2) == 0;
-#else
- return strcasecmp(ext1, ext2) == 0;
-#endif
-}
-
-
-
-
-/*
-File Iterator
-
-dr_file_iterator state;
-dr_file_iterator* pFile = dr_file_iterator_begin("the/folder/path", &state);
-while (pFile != NULL) {
- // Do something with pFile.
- pFile = dr_file_iterator_next(pFile);
-}
-
-Limitations:
- - Only supports file paths up to 256 characters.
-*/
-typedef struct
-{
- char folderPath[256];
- char relativePath[256]; /* Relative to the original folder path. */
- char absolutePath[256]; /* Concatenation of folderPath and relativePath. */
- dr_bool32 isDirectory;
-#ifdef _WIN32
- HANDLE hFind;
-#else
- DIR* dir;
-#endif
-} dr_file_iterator;
-
-dr_file_iterator* dr_file_iterator_begin(const char* pFolderPath, dr_file_iterator* pState)
-{
-#ifdef _WIN32
- char searchQuery[MAX_PATH];
- unsigned int searchQueryLength;
- WIN32_FIND_DATAA ffd;
- HANDLE hFind;
-#else
- DIR* dir;
-#endif
-
- if (pState == NULL) {
- return NULL;
- }
-
- memset(pState, 0, sizeof(*pState));
-
- if (pFolderPath == NULL) {
- return NULL;
- }
-
-#ifdef _WIN32
- dr_strcpy_s(searchQuery, sizeof(searchQuery), pFolderPath);
-
- searchQueryLength = (unsigned int)strlen(searchQuery);
- if (searchQueryLength >= MAX_PATH - 3) {
- return NULL; /* Path is too long. */
- }
-
- searchQuery[searchQueryLength + 0] = '\\';
- searchQuery[searchQueryLength + 1] = '*';
- searchQuery[searchQueryLength + 2] = '\0';
-
- hFind = FindFirstFileA(searchQuery, &ffd);
- if (hFind == INVALID_HANDLE_VALUE) {
- return NULL; /* Failed to begin search. */
- }
-
- /* Skip past "." and ".." directories. */
- while (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) {
- if (!FindNextFileA(hFind, &ffd)) {
- FindClose(hFind);
- return NULL; /* Couldn't find anything. */
- }
- }
-
- pState->hFind = hFind;
-
-
- if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
- pState->isDirectory = 1;
- } else {
- pState->isDirectory = 0;
- }
-
- dr_strcpy_s(pState->relativePath, sizeof(pState->relativePath), ffd.cFileName);
-#else
- dir = opendir(pFolderPath);
- if (dir == NULL) {
- return NULL; /* Couldn't find anything. */
- }
-
- /* Select the first file. */
- for (;;) {
- struct dirent* info;
- struct stat fileinfo;
- char filePath[4096];
-
- info = readdir(dir);
- if (info == NULL) {
- closedir(dir);
- return NULL;
- }
-
- if (strcmp(info->d_name, ".") == 0 || strcmp(info->d_name, "..") == 0) {
- continue; /* Skip past "." and ".." directories. */
- }
-
- dr_strcpy_s(pState->relativePath, sizeof(pState->relativePath), info->d_name);
- dr_append_path(filePath, sizeof(filePath), pFolderPath, pState->relativePath);
-
- if (stat(filePath, &fileinfo) != 0) {
- continue;
- }
-
- if (S_ISDIR(fileinfo.st_mode)) {
- pState->isDirectory = 1;
- } else {
- pState->isDirectory = 0;
- }
-
- break;
- }
-
- pState->dir = dir;
-#endif
-
- /* Getting here means everything was successful. We can now set some state before returning. */
- dr_strcpy_s(pState->folderPath, sizeof(pState->folderPath), pFolderPath);
- dr_append_path(pState->absolutePath, sizeof(pState->absolutePath), pState->folderPath, pState->relativePath);
-
- return pState;
-}
-
-dr_file_iterator* dr_file_iterator_next(dr_file_iterator* pState)
-{
-#ifdef _WIN32
- WIN32_FIND_DATAA ffd;
-#endif
-
- if (pState == NULL) {
- return NULL;
- }
-
-#ifdef _WIN32
- if (!FindNextFileA(pState->hFind, &ffd)) {
- /* Couldn't find anything else. */
- FindClose(pState->hFind);
- pState->hFind = NULL;
- return NULL;
- }
-
- /* Found something. */
- if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
- pState->isDirectory = 1;
- } else {
- pState->isDirectory = 0;
- }
-
- dr_strcpy_s(pState->relativePath, sizeof(pState->relativePath), ffd.cFileName);
-#else
- /* Enter a loop here so we can skip past "." and ".." directories. */
- for (;;) {
- struct dirent* info;
- struct stat fileinfo;
- char filePath[4096];
-
- info = readdir(pState->dir);
- if (info == NULL) {
- closedir(pState->dir);
- pState->dir = NULL;
- return NULL;
- }
-
- if (strcmp(info->d_name, ".") == 0 || strcmp(info->d_name, "..") == 0) {
- continue; /* Skip past "." and ".." directories. */
- }
-
- dr_strcpy_s(pState->relativePath, sizeof(pState->relativePath), info->d_name);
- dr_append_path(filePath, sizeof(filePath), pState->folderPath, pState->relativePath);
-
- /*printf("Done: %s\n", pState->relativePath);*/
-
- if (stat(filePath, &fileinfo) != 0) {
- continue;
- }
-
- if (S_ISDIR(fileinfo.st_mode)) {
- pState->isDirectory = 1;
- } else {
- pState->isDirectory = 0;
- }
-
- break;
- }
-#endif
-
- /* Success */
- dr_append_path(pState->absolutePath, sizeof(pState->absolutePath), pState->folderPath, pState->relativePath);
- return pState;
-}
-
-void dr_file_iterator_end(dr_file_iterator* pState)
-{
- if (pState == NULL) {
- return;
- }
-
-#ifdef _WIN32
- FindClose(pState->hFind);
- pState->hFind = NULL;
-#else
- closedir(pState->dir);
- pState->dir = NULL;
-#endif
-}
-
-
-/*
-File Management
-
-Free file data with free().
-*/
-static int dr_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
-{
-#if defined(_MSC_VER) && _MSC_VER >= 1400
- errno_t err;
-#endif
-
- if (ppFile != NULL) {
- *ppFile = NULL; /* Safety. */
- }
-
- if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
- return -1; /* Invalid args. */
- }
-
-#if defined(_MSC_VER) && _MSC_VER >= 1400
- err = fopen_s(ppFile, pFilePath, pOpenMode);
- if (err != 0) {
- return err;
- }
-#else
-#if defined(_WIN32) || defined(__APPLE__)
- *ppFile = fopen(pFilePath, pOpenMode);
-#else
- #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
- *ppFile = fopen64(pFilePath, pOpenMode);
- #else
- *ppFile = fopen(pFilePath, pOpenMode);
- #endif
-#endif
- if (*ppFile == NULL) {
- return errno;
- }
-#endif
-
- return 0;
-}
-
-void* dr_open_and_read_file_with_extra_data(const char* pFilePath, size_t* pFileSizeOut, size_t extraBytes)
-{
- FILE* pFile;
- size_t fileSize;
- size_t bytesRead;
- void* pFileData;
-
- /* Safety. */
- if (pFileSizeOut) {
- *pFileSizeOut = 0;
- }
-
- if (pFilePath == NULL) {
- return NULL;
- }
-
- /* TODO: Use 64-bit versions of the FILE APIs. */
-
- if (dr_fopen(&pFile, pFilePath, "rb") != 0) {
- return NULL;
- }
-
- fseek(pFile, 0, SEEK_END);
- fileSize = ftell(pFile);
- fseek(pFile, 0, SEEK_SET);
-
- /* Need to make sure we have enough room for the extra bytes, if any. */
- if (fileSize == DR_SIZE_MAX && extraBytes > 0) {
- fclose(pFile);
- return NULL; /* File is too big. */
- }
-
- pFileData = malloc((size_t)fileSize + extraBytes); /* <-- Safe cast due to the check above. */
- if (pFileData == NULL) {
- fclose(pFile);
- return NULL; /* Failed to allocate memory for the file. Good chance the file is too big. */
- }
-
- bytesRead = fread(pFileData, 1, (size_t)fileSize, pFile);
- if (bytesRead != fileSize) {
- free(pFileData);
- fclose(pFile);
- return NULL; /* Failed to read every byte from the file. */
- }
-
- fclose(pFile);
-
- if (pFileSizeOut) {
- *pFileSizeOut = (size_t)fileSize;
- }
-
- return pFileData;
-}
-
-void* dr_open_and_read_file(const char* pFilePath, size_t* pFileSizeOut)
-{
- return dr_open_and_read_file_with_extra_data(pFilePath, pFileSizeOut, 0);
-}
-
-
-dr_bool32 dr_argv_is_set(int argc, char** argv, const char* value)
-{
- int iarg;
- for (iarg = 0; iarg < argc; ++iarg) {
- if (strcmp(argv[iarg], value) == 0) {
- return DR_TRUE;
- }
- }
-
- return DR_FALSE;
-}
-
-
-int dr_vprintf_fixed(int width, const char* const format, va_list args)
-{
- int i;
- int len;
- char buffer[4096];
-
- if (width <= 0) {
- return -1; /* Width cannot be negative or 0. */
- }
-
- if ((unsigned int)width > sizeof(buffer)) {
- return -1; /* Width is too big. */
- }
-
- /* We need to print this into a string (truncated). */
-#if (defined(_MSC_VER) && _MSC_VER > 1200) || ((defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500) || defined(_ISOC99_SOURCE) || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L))
- len = vsnprintf(buffer, width+1, format, args);
-#else
- len = vsprintf(buffer, format, args);
- if (len > width) {
- len = width;
- }
-
- buffer[len] = '\0';
-#endif
-
- printf("%s", buffer);
- for (i = len; i < width; ++i) {
- printf(" ");
- }
-
-
- return len;
-}
-
-int dr_printf_fixed(int width, const char* const format, ...)
-{
- int result;
-
- va_list args;
- va_start(args, format);
- {
- result = dr_vprintf_fixed(width, format, args);
- }
- va_end(args);
-
- return result;
-}
-
-int dr_vprintf_fixed_with_margin(int width, int margin, const char* const format, va_list args)
-{
- int i;
-
- /* Margin. */
- for (i = 0; i < margin; ++i) {
- printf(" ");
- }
-
- return dr_vprintf_fixed(width - margin, format, args);
-}
-
-int dr_printf_fixed_with_margin(int width, int margin, const char* const format, ...)
-{
- int result;
-
- va_list args;
- va_start(args, format);
- {
- result = dr_vprintf_fixed_with_margin(width, margin, format, args);
- }
- va_end(args);
-
- return result;
-}
-
-
-
-#ifdef _WIN32
-static LARGE_INTEGER g_DRTimerFrequency = {{0}};
-double dr_timer_now()
-{
- LARGE_INTEGER counter;
-
- if (g_DRTimerFrequency.QuadPart == 0) {
- QueryPerformanceFrequency(&g_DRTimerFrequency);
- }
-
- QueryPerformanceCounter(&counter);
-
- return counter.QuadPart / (double)g_DRTimerFrequency.QuadPart;
-}
-#else
-#if _POSIX_C_SOURCE >= 199309L
- #if defined(CLOCK_MONOTONIC)
- #define MA_CLOCK_ID CLOCK_MONOTONIC
- #else
- #define MA_CLOCK_ID CLOCK_REALTIME
- #endif
- double dr_timer_now()
- {
- struct timespec newTime;
- clock_gettime(CLOCK_MONOTONIC, &newTime);
-
- return ((newTime.tv_sec * 1000000000LL) + newTime.tv_nsec) / 1000000000.0;
- }
-#else
- double dr_timer_now()
- {
- struct timeval newTime;
- gettimeofday(&newTime, NULL);
-
- return ((newTime.tv_sec * 1000000) + newTime.tv_usec) / 1000000.0;
- }
-#endif
-#endif
-
-
-float dr_scale_to_range_f32(float x, float lo, float hi)
-{
- return lo + x*(hi-lo);
-}
-
-
-#define DR_LCG_M 2147483647
-#define DR_LCG_A 48271
-#define DR_LCG_C 0
-static int g_drLCG;
-
-void dr_seed(int seed)
-{
- g_drLCG = seed;
-}
-
-int dr_rand_s32()
-{
- int lcg = g_drLCG;
- int r = (DR_LCG_A * lcg + DR_LCG_C) % DR_LCG_M;
- g_drLCG = r;
- return r;
-}
-
-unsigned int dr_rand_u32()
-{
- return (unsigned int)dr_rand_s32();
-}
-
-dr_uint64 dr_rand_u64()
-{
- return ((dr_uint64)dr_rand_u32() << 32) | dr_rand_u32();
-}
-
-double dr_rand_f64()
-{
- return dr_rand_s32() / (double)0x7FFFFFFF;
-}
-
-float dr_rand_f32()
-{
- return (float)dr_rand_f64();
-}
-
-float dr_rand_range_f32(float lo, float hi)
-{
- return dr_scale_to_range_f32(dr_rand_f32(), lo, hi);
-}
-
-int dr_rand_range_s32(int lo, int hi)
-{
- if (lo == hi) {
- return lo;
- }
-
- return lo + dr_rand_u32() / (0xFFFFFFFF / (hi - lo + 1) + 1);
-}
-
-dr_uint64 dr_rand_range_u64(dr_uint64 lo, dr_uint64 hi)
-{
- if (lo == hi) {
- return lo;
- }
-
- return lo + dr_rand_u64() / ((~(dr_uint64)0) / (hi - lo + 1) + 1);
-}
-
-
-
-void dr_pcm_s32_to_f32(void* dst, const void* src, dr_uint64 count)
-{
- float* dst_f32 = (float*)dst;
- const dr_int32* src_s32 = (const dr_int32*)src;
-
- dr_uint64 i;
- for (i = 0; i < count; i += 1) {
- double x = src_s32[i];
-
-#if 0
- x = x + 2147483648.0;
- x = x * 0.0000000004656612873077392578125;
- x = x - 1;
-#else
- x = x / 2147483648.0;
-#endif
-
- dst_f32[i] = (float)x;
- }
-}
-
-void dr_pcm_s32_to_s16(void* dst, const void* src, dr_uint64 count)
-{
- dr_int16* dst_s16 = (dr_int16*)dst;
- const dr_int32* src_s32 = (const dr_int32*)src;
-
- dr_uint64 i;
- for (i = 0; i < count; i += 1) {
- dr_int32 x = src_s32[i];
- x = x >> 16;
- dst_s16[i] = (dr_int16)x;
- }
-}
-
-
-
-
-dr_handle dr_dlopen(const char* filename)
-{
- dr_handle handle;
-
-#ifdef _WIN32
- handle = (dr_handle)LoadLibraryA(filename);
-#else
- handle = (dr_handle)dlopen(filename, RTLD_NOW);
-#endif
-
- return handle;
-}
-
-void dr_dlclose(dr_handle handle)
-{
-#ifdef _WIN32
- FreeLibrary((HMODULE)handle);
-#else
- dlclose((void*)handle);
-#endif
-}
-
-dr_proc dr_dlsym(dr_handle handle, const char* symbol)
-{
- dr_proc proc;
-
-#ifdef _WIN32
- proc = (dr_proc)GetProcAddress((HMODULE)handle, symbol);
-#else
-#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wpedantic"
-#endif
- proc = (dr_proc)dlsym((void*)handle, symbol);
-#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
- #pragma GCC diagnostic pop
-#endif
-#endif
-
- return proc;
-}
diff --git a/source/engine/thirdparty/dr_libs/tests/external/miniaudio b/source/engine/thirdparty/dr_libs/tests/external/miniaudio
deleted file mode 160000
index d1a166c..0000000
--- a/source/engine/thirdparty/dr_libs/tests/external/miniaudio
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit d1a166c83ab445b1c14bc83d37c84e18d172e5f5
diff --git a/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_common.c b/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_common.c
deleted file mode 100644
index 45342b9..0000000
--- a/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_common.c
+++ /dev/null
@@ -1,332 +0,0 @@
-#define DR_FLAC_IMPLEMENTATION
-#include "../../dr_flac.h"
-
-/* libFLAC has a warning in their header. *sigh*. */
-#if defined(__GNUC__) || defined(__clang__)
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wpedantic"
- #if defined(__clang__)
- #pragma GCC diagnostic ignored "-Wc99-extensions"
- #endif
-#endif
-#include
-#if defined(__GNUC__)
- #pragma GCC diagnostic pop
-#endif
-
-#include "../common/dr_common.c"
-
-
-/*
-The libflac object is used to make it easier to compare things against the reference implementation. I don't think
-libFLAC's API is all that easy to use or intuitive so I'm wrapping it. This is deliberately simple. It decodes the
-entire file into memory upon initialization.
-*/
-typedef struct
-{
- drflac_int32* pPCMFrames; /* Interleaved. */
- drflac_uint64 pcmFrameCount;
- drflac_uint64 pcmFrameCap; /* The capacity of the pPCMFrames buffer in PCM frames. */
- drflac_uint32 channels;
- drflac_uint32 sampleRate;
- drflac_uint64 currentPCMFrame; /* The index of the PCM frame the decoder is currently sitting on. */
- double decodeTimeInSeconds; /* The total amount of time it took to decode the file. This is used for profiling. */
- drflac_uint8* pFileData;
- size_t fileSizeInBytes;
- size_t fileReadPos;
-} libflac;
-
-static FLAC__StreamDecoderReadStatus libflac__read_callback(const FLAC__StreamDecoder *pStreamDecoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
-{
- libflac* pDecoder = (libflac*)client_data;
- size_t bytesToRead = *bytes;
- size_t bytesRemaining = pDecoder->fileSizeInBytes - pDecoder->fileReadPos;
-
- (void)pStreamDecoder;
-
- if (bytesToRead > bytesRemaining) {
- bytesToRead = bytesRemaining;
- }
-
- if (bytesToRead > 0) {
- memcpy(buffer, pDecoder->pFileData + pDecoder->fileReadPos, bytesToRead);
- pDecoder->fileReadPos += bytesToRead;
- }
-
- *bytes = bytesToRead;
- return (bytesToRead == 0 && bytesRemaining == 0) ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
-}
-
-static FLAC__StreamDecoderWriteStatus libflac__write_callback(const FLAC__StreamDecoder *pStreamDecoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
-{
- libflac* pDecoder = (libflac*)client_data;
- drflac_uint32 pcmFramesInFLACFrame;
- drflac_int32* pNextSampleInNewFrame;
- drflac_uint32 i, j;
-
- (void)pStreamDecoder;
-
- pcmFramesInFLACFrame = frame->header.blocksize;
-
- /* Make sure there's room in the buffer. */
- if ((pDecoder->pcmFrameCount + pcmFramesInFLACFrame) > pDecoder->pcmFrameCap) {
- drflac_int32* pNewPCMFrames;
-
- pDecoder->pcmFrameCap *= 2;
- if (pDecoder->pcmFrameCap < (pDecoder->pcmFrameCount + pcmFramesInFLACFrame)) {
- pDecoder->pcmFrameCap = (pDecoder->pcmFrameCount + pcmFramesInFLACFrame);
- }
-
- pNewPCMFrames = (drflac_int32*)realloc(pDecoder->pPCMFrames, (size_t)(pDecoder->pcmFrameCap * pDecoder->channels * sizeof(drflac_int32)));
- if (pNewPCMFrames == NULL) {
- return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
- }
-
- pDecoder->pPCMFrames = pNewPCMFrames;
- }
-
- /* Make sure the decoded samples are interleaved. */
- pNextSampleInNewFrame = pDecoder->pPCMFrames + (pDecoder->pcmFrameCount * pDecoder->channels);
- for (i = 0; i < pcmFramesInFLACFrame; i += 1) {
- for (j = 0; j < pDecoder->channels; ++j) {
- *pNextSampleInNewFrame++ = buffer[j][i] << (32 - frame->header.bits_per_sample);
- }
- }
-
- pDecoder->pcmFrameCount += pcmFramesInFLACFrame;
-
- return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
-}
-
-static FLAC__StreamDecoderLengthStatus libflac__length_callback(const FLAC__StreamDecoder *pStreamDecoder, FLAC__uint64 *stream_length, void *client_data)
-{
- libflac* pDecoder = (libflac*)client_data;
-
- (void)pStreamDecoder;
-
- *stream_length = pDecoder->fileSizeInBytes;
- return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
-}
-
-static void libflac__metadata_callback(const FLAC__StreamDecoder *pStreamDecoder, const FLAC__StreamMetadata *metadata, void *client_data)
-{
- libflac* pDecoder = (libflac*)client_data;
-
- (void)pStreamDecoder;
-
- /* Here is where we initialize the buffer for the decoded data. */
- if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
- pDecoder->pcmFrameCount = 0; /* Always set this to 0. Don't be tempted to set it to metadata->data.stream_info.total_samples. It's increment in libflac__write_callback. */
- pDecoder->channels = metadata->data.stream_info.channels;
- pDecoder->sampleRate = metadata->data.stream_info.sample_rate;
-
- /* Allocate an initial block of memory if we know the total size. */
- if (metadata->data.stream_info.total_samples > 0) {
- pDecoder->pcmFrameCap = metadata->data.stream_info.total_samples;
- pDecoder->pPCMFrames = (drflac_int32*)malloc((size_t)(pDecoder->pcmFrameCap * pDecoder->channels * sizeof(drflac_int32)));
- if (pDecoder->pPCMFrames != NULL) {
- DRFLAC_ZERO_MEMORY(pDecoder->pPCMFrames, (size_t)(pDecoder->pcmFrameCap * pDecoder->channels * sizeof(drflac_int32)));
- }
- }
- }
-}
-
-static void libflac__error_callback(const FLAC__StreamDecoder *pStreamDecoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
-{
- (void)pStreamDecoder;
- (void)status;
- (void)client_data;
-}
-
-/*
-Initializes a libflac object.
-
-This will perform a full decode of the file using libFLAC. The time it takes to decode the file will be stored in decodeTimeInSeconds
-for the purpose of profiling. The decoding process will load the entire file into memory before calling into libFLAC. This way profiling
-will exclude any IO time.
-*/
-drflac_result libflac_init_file(const char* pFilePath, libflac* pDecoder)
-{
- FLAC__StreamDecoder* pStreamDecoder;
- FLAC__bool libflacResult;
- FLAC__StreamDecoderInitStatus libflacStatus;
- double decodeTimeBeg;
- double decodeTimeEnd;
-
- DRFLAC_ZERO_MEMORY(pDecoder, sizeof(*pDecoder));
-
- pDecoder->pFileData = (drflac_uint8*)dr_open_and_read_file(pFilePath, &pDecoder->fileSizeInBytes);
- if (pDecoder->pFileData == NULL) {
- return DRFLAC_ERROR; /* Failed to open the file. */
- }
-
-
- /* Initialize the libFLAC decoder. */
- pStreamDecoder = FLAC__stream_decoder_new();
- if (pDecoder == NULL) {
- return DRFLAC_ERROR; /* Failed to create a new stream decoder. Out of memory. */
- }
-
- if (dr_extension_equal(pFilePath, "ogg") || dr_extension_equal(pFilePath, "oga") || dr_extension_equal(pFilePath, "ogv")) {
- libflacStatus = FLAC__stream_decoder_init_ogg_stream(pStreamDecoder, libflac__read_callback, NULL, NULL, libflac__length_callback, NULL, libflac__write_callback, libflac__metadata_callback, libflac__error_callback, pDecoder);
- } else {
- libflacStatus = FLAC__stream_decoder_init_stream(pStreamDecoder, libflac__read_callback, NULL, NULL, libflac__length_callback, NULL, libflac__write_callback, libflac__metadata_callback, libflac__error_callback, pDecoder);
- }
-
- if (libflacStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
- FLAC__stream_decoder_delete(pStreamDecoder);
- free(pDecoder->pFileData);
- return DRFLAC_ERROR; /* Failed to initialize the stream. */
- }
-
- /* Read past the metadata first. This will fire the libflac__metadata_callback which will do a bit of initialization for us. */
- libflacResult = FLAC__stream_decoder_process_until_end_of_metadata(pStreamDecoder);
- if (libflacResult == DRFLAC_FALSE) {
- FLAC__stream_decoder_delete(pStreamDecoder);
- free(pDecoder->pFileData);
- return DRFLAC_ERROR; /* Failed to read metadata from the FLAC stream. */
- }
-
- /* Now we can just decode the entire file. */
- decodeTimeBeg = dr_timer_now();
- libflacResult = FLAC__stream_decoder_process_until_end_of_stream(pStreamDecoder);
- decodeTimeEnd = dr_timer_now();
-
- pDecoder->decodeTimeInSeconds = decodeTimeEnd - decodeTimeBeg;
-
- /* We're done with the libFLAC decoder. */
- FLAC__stream_decoder_delete(pStreamDecoder);
- pStreamDecoder = NULL;
-
-
- /* Free the file data. */
- free(pDecoder->pFileData);
- pDecoder->pFileData = NULL;
- pDecoder->fileSizeInBytes = 0;
- pDecoder->fileReadPos = 0;
-
- if (libflacResult == DRFLAC_FALSE) {
- return DRFLAC_ERROR; /* Some error ocurred while decoding. */
- }
-
- return DRFLAC_SUCCESS;
-}
-
-void libflac_uninit(libflac* pDecoder)
-{
- if (pDecoder == NULL) {
- return;
- }
-
- free(pDecoder->pPCMFrames);
-}
-
-drflac_uint64 libflac_read_pcm_frames_s32(libflac* pDecoder, drflac_uint64 framesToRead, drflac_int32* pBufferOut)
-{
- drflac_uint64 pcmFramesRemaining;
-
- if (pDecoder == NULL) {
- return 0;
- }
-
- pcmFramesRemaining = pDecoder->pcmFrameCount - pDecoder->currentPCMFrame;
- if (framesToRead > pcmFramesRemaining) {
- framesToRead = pcmFramesRemaining;
- }
-
- if (framesToRead == 0) {
- return 0;
- }
-
- DRFLAC_COPY_MEMORY(pBufferOut, pDecoder->pPCMFrames + (pDecoder->currentPCMFrame * pDecoder->channels), (size_t)(framesToRead * pDecoder->channels * sizeof(drflac_int32)));
- pDecoder->currentPCMFrame += framesToRead;
-
- return framesToRead;
-}
-
-drflac_uint64 libflac_read_pcm_frames_f32(libflac* pDecoder, drflac_uint64 framesToRead, float* pBufferOut)
-{
- drflac_uint64 pcmFramesRemaining;
-
- if (pDecoder == NULL) {
- return 0;
- }
-
- pcmFramesRemaining = pDecoder->pcmFrameCount - pDecoder->currentPCMFrame;
- if (framesToRead > pcmFramesRemaining) {
- framesToRead = pcmFramesRemaining;
- }
-
- if (framesToRead == 0) {
- return 0;
- }
-
- /* s32 to f32 */
- dr_pcm_s32_to_f32(pBufferOut, pDecoder->pPCMFrames + (pDecoder->currentPCMFrame * pDecoder->channels), framesToRead * pDecoder->channels);
- pDecoder->currentPCMFrame += framesToRead;
-
- return framesToRead;
-}
-
-drflac_uint64 libflac_read_pcm_frames_s16(libflac* pDecoder, drflac_uint64 framesToRead, drflac_int16* pBufferOut)
-{
- drflac_uint64 pcmFramesRemaining;
-
- if (pDecoder == NULL) {
- return 0;
- }
-
- pcmFramesRemaining = pDecoder->pcmFrameCount - pDecoder->currentPCMFrame;
- if (framesToRead > pcmFramesRemaining) {
- framesToRead = pcmFramesRemaining;
- }
-
- if (framesToRead == 0) {
- return 0;
- }
-
- /* s32 to f32 */
- dr_pcm_s32_to_s16(pBufferOut, pDecoder->pPCMFrames + (pDecoder->currentPCMFrame * pDecoder->channels), framesToRead * pDecoder->channels);
- pDecoder->currentPCMFrame += framesToRead;
-
- return framesToRead;
-}
-
-drflac_bool32 libflac_seek_to_pcm_frame(libflac* pDecoder, drflac_uint64 targetPCMFrameIndex)
-{
- if (pDecoder == NULL) {
- return DRFLAC_FALSE;
- }
-
- if (targetPCMFrameIndex > pDecoder->pcmFrameCount) {
- return DRFLAC_FALSE; /* Trying to seek too far forward. */
- }
-
- pDecoder->currentPCMFrame = targetPCMFrameIndex;
-
- return DRFLAC_TRUE;
-}
-
-
-/* Helper for printing CPU caps from dr_flac. */
-void print_cpu_caps()
-{
-#if defined(DRFLAC_64BIT)
- printf("64 Bit\n");
-#else
- printf("32 Bit\n");
-#endif
-
- drflac__init_cpu_caps();
-
-#if defined(DRFLAC_X86) || defined(DRFLAC_X64)
- #if defined(DRFLAC_X64)
- printf("Architecture: x64\n");
- #else
- printf("Architecture: x86\n");
- #endif
- printf("Has SSE2: %s\n", drflac_has_sse2() ? "YES" : "NO");
- printf("Has SSE4.1: %s\n", drflac_has_sse41() ? "YES" : "NO");
- printf("Has LZCNT: %s\n", drflac__is_lzcnt_supported() ? "YES" : "NO");
-#endif
-}
diff --git a/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_decoding.c b/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_decoding.c
deleted file mode 100644
index fbaf413..0000000
--- a/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_decoding.c
+++ /dev/null
@@ -1,825 +0,0 @@
-/*#define DR_FLAC_NO_CRC*/
-/*#define DR_FLAC_NO_SIMD*/
-/*#define DR_FLAC_BUFFER_SIZE 4096*/
-#include "dr_flac_common.c"
-
-#define FILE_NAME_WIDTH 40
-#define NUMBER_WIDTH 10
-#define TABLE_MARGIN 2
-
-#define DEFAULT_SOURCE_DIR "testvectors/flac/testbench"
-
-drflac_result decode_test__read_and_compare_pcm_frames_s32(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameCount, drflac_int32* pPCMFrames_libflac, drflac_int32* pPCMFrames_drflac)
-{
- drflac_uint64 pcmFrameCount_libflac;
- drflac_uint64 pcmFrameCount_drflac;
- drflac_uint64 iPCMFrame;
-
- /* To test decoding we just read a number of PCM frames from each decoder and compare. */
- pcmFrameCount_libflac = libflac_read_pcm_frames_s32(pLibFlac, pcmFrameCount, pPCMFrames_libflac);
- pcmFrameCount_drflac = drflac_read_pcm_frames_s32(pFlac, pcmFrameCount, pPCMFrames_drflac);
-
- /* The total number of frames we decoded need to match. */
- if (pcmFrameCount_libflac != pcmFrameCount_drflac) {
- printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pcmFrameCount_libflac, (int)pcmFrameCount_drflac);
- return DRFLAC_ERROR;
- }
-
- /* Each of the decoded PCM frames need to match. */
- DRFLAC_ASSERT(pcmFrameCount_libflac == pcmFrameCount_drflac);
-
- for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libflac; iPCMFrame += 1) {
- drflac_int32* pPCMFrame_libflac = pPCMFrames_libflac + (iPCMFrame * pLibFlac->channels);
- drflac_int32* pPCMFrame_drflac = pPCMFrames_drflac + (iPCMFrame * pLibFlac->channels);
- drflac_uint32 iChannel;
- drflac_bool32 hasError = DRFLAC_FALSE;
-
- for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
- if (pPCMFrame_libflac[iChannel] != pPCMFrame_drflac[iChannel]) {
- printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d\n", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
- hasError = DRFLAC_TRUE;
- break;
- }
- }
-
- if (hasError) {
- return DRFLAC_ERROR; /* Decoded frames do not match. */
- }
- }
-
- /* Done. */
- return DRFLAC_SUCCESS;
-}
-
-drflac_result decode_test__read_and_compare_pcm_frame_chunks_s32(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameChunkSize)
-{
- drflac_result result = DRFLAC_SUCCESS;
- drflac_uint64 iPCMFrame;
- drflac_int32* pPCMFrames_libflac;
- drflac_int32* pPCMFrames_drflac;
-
- /* Make sure the decoder's are seeked back to the start first. */
- drflac_seek_to_pcm_frame(pFlac, 0);
- libflac_seek_to_pcm_frame(pLibFlac, 0);
-
- pPCMFrames_libflac = (drflac_int32*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(drflac_int32)));
- if (pPCMFrames_libflac == NULL) {
- printf(" [libFLAC] Out of memory");
- return DRFLAC_ERROR;
- }
-
- pPCMFrames_drflac = (drflac_int32*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(drflac_int32)));
- if (pPCMFrames_drflac == NULL) {
- free(pPCMFrames_libflac);
- printf(" [dr_flac] Out of memory");
- return DRFLAC_ERROR;
- }
-
- for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += pcmFrameChunkSize) {
- result = decode_test__read_and_compare_pcm_frames_s32(pLibFlac, pFlac, pcmFrameChunkSize, pPCMFrames_libflac, pPCMFrames_drflac);
- if (result != DRFLAC_SUCCESS) {
- break;
- }
- }
-
- free(pPCMFrames_libflac);
- free(pPCMFrames_drflac);
-
- return result;
-}
-
-
-drflac_result decode_test__read_and_compare_pcm_frames_f32(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameCount, float* pPCMFrames_libflac, float* pPCMFrames_drflac)
-{
- drflac_uint64 pcmFrameCount_libflac;
- drflac_uint64 pcmFrameCount_drflac;
- drflac_uint64 iPCMFrame;
-
- /* To test decoding we just read a number of PCM frames from each decoder and compare. */
- pcmFrameCount_libflac = libflac_read_pcm_frames_f32(pLibFlac, pcmFrameCount, pPCMFrames_libflac);
- pcmFrameCount_drflac = drflac_read_pcm_frames_f32(pFlac, pcmFrameCount, pPCMFrames_drflac);
-
- /* The total number of frames we decoded need to match. */
- if (pcmFrameCount_libflac != pcmFrameCount_drflac) {
- printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pLibFlac->currentPCMFrame, (int)pFlac->currentPCMFrame);
- return DRFLAC_ERROR;
- }
-
- /* Each of the decoded PCM frames need to match. */
- DRFLAC_ASSERT(pcmFrameCount_libflac == pcmFrameCount_drflac);
-
- for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libflac; iPCMFrame += 1) {
- float* pPCMFrame_libflac = pPCMFrames_libflac + (iPCMFrame * pLibFlac->channels);
- float* pPCMFrame_drflac = pPCMFrames_drflac + (iPCMFrame * pLibFlac->channels);
- drflac_uint32 iChannel;
- drflac_bool32 hasError = DRFLAC_FALSE;
-
- for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
- if (pPCMFrame_libflac[iChannel] != pPCMFrame_drflac[iChannel]) {
- printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
- hasError = DRFLAC_TRUE;
- break;
- }
- }
-
- if (hasError) {
- return DRFLAC_ERROR; /* Decoded frames do not match. */
- }
- }
-
- /* Done. */
- return DRFLAC_SUCCESS;
-}
-
-drflac_result decode_test__read_and_compare_pcm_frame_chunks_f32(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameChunkSize)
-{
- drflac_result result = DRFLAC_SUCCESS;
- drflac_uint64 iPCMFrame;
- float* pPCMFrames_libflac;
- float* pPCMFrames_drflac;
-
- /* Make sure the decoder's are seeked back to the start first. */
- drflac_seek_to_pcm_frame(pFlac, 0);
- libflac_seek_to_pcm_frame(pLibFlac, 0);
-
- pPCMFrames_libflac = (float*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(float)));
- if (pPCMFrames_libflac == NULL) {
- printf(" [libFLAC] Out of memory");
- return DRFLAC_ERROR;
- }
-
- pPCMFrames_drflac = (float*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(float)));
- if (pPCMFrames_drflac == NULL) {
- free(pPCMFrames_libflac);
- printf(" [dr_flac] Out of memory");
- return DRFLAC_ERROR;
- }
-
- for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += pcmFrameChunkSize) {
- result = decode_test__read_and_compare_pcm_frames_f32(pLibFlac, pFlac, pcmFrameChunkSize, pPCMFrames_libflac, pPCMFrames_drflac);
- if (result != DRFLAC_SUCCESS) {
- break;
- }
- }
-
- free(pPCMFrames_libflac);
- free(pPCMFrames_drflac);
-
- return result;
-}
-
-
-drflac_result decode_test__read_and_compare_pcm_frames_s16(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameCount, drflac_int16* pPCMFrames_libflac, drflac_int16* pPCMFrames_drflac)
-{
- drflac_uint64 pcmFrameCount_libflac;
- drflac_uint64 pcmFrameCount_drflac;
- drflac_uint64 iPCMFrame;
-
- /* To test decoding we just read a number of PCM frames from each decoder and compare. */
- pcmFrameCount_libflac = libflac_read_pcm_frames_s16(pLibFlac, pcmFrameCount, pPCMFrames_libflac);
- pcmFrameCount_drflac = drflac_read_pcm_frames_s16(pFlac, pcmFrameCount, pPCMFrames_drflac);
-
- /* The total number of frames we decoded need to match. */
- if (pcmFrameCount_libflac != pcmFrameCount_drflac) {
- printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pLibFlac->currentPCMFrame, (int)pFlac->currentPCMFrame);
- return DRFLAC_ERROR;
- }
-
- /* Each of the decoded PCM frames need to match. */
- DRFLAC_ASSERT(pcmFrameCount_libflac == pcmFrameCount_drflac);
-
- for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libflac; iPCMFrame += 1) {
- drflac_int16* pPCMFrame_libflac = pPCMFrames_libflac + (iPCMFrame * pLibFlac->channels);
- drflac_int16* pPCMFrame_drflac = pPCMFrames_drflac + (iPCMFrame * pLibFlac->channels);
- drflac_uint32 iChannel;
- drflac_bool32 hasError = DRFLAC_FALSE;
-
- for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
- if (pPCMFrame_libflac[iChannel] != pPCMFrame_drflac[iChannel]) {
- printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
- hasError = DRFLAC_TRUE;
- break;
- }
- }
-
- if (hasError) {
- return DRFLAC_ERROR; /* Decoded frames do not match. */
- }
- }
-
- /* Done. */
- return DRFLAC_SUCCESS;
-}
-
-drflac_result decode_test__read_and_compare_pcm_frame_chunks_s16(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameChunkSize)
-{
- drflac_result result = DRFLAC_SUCCESS;
- drflac_uint64 iPCMFrame;
- drflac_int16* pPCMFrames_libflac;
- drflac_int16* pPCMFrames_drflac;
-
- /* Make sure the decoder's are seeked back to the start first. */
- drflac_seek_to_pcm_frame(pFlac, 0);
- libflac_seek_to_pcm_frame(pLibFlac, 0);
-
- pPCMFrames_libflac = (drflac_int16*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(drflac_int16)));
- if (pPCMFrames_libflac == NULL) {
- printf(" [libFLAC] Out of memory");
- return DRFLAC_ERROR;
- }
-
- pPCMFrames_drflac = (drflac_int16*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(drflac_int16)));
- if (pPCMFrames_drflac == NULL) {
- free(pPCMFrames_libflac);
- printf(" [dr_flac] Out of memory");
- return DRFLAC_ERROR;
- }
-
- for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += pcmFrameChunkSize) {
- result = decode_test__read_and_compare_pcm_frames_s16(pLibFlac, pFlac, pcmFrameChunkSize, pPCMFrames_libflac, pPCMFrames_drflac);
- if (result != DRFLAC_SUCCESS) {
- break;
- }
- }
-
- free(pPCMFrames_libflac);
- free(pPCMFrames_drflac);
-
- return result;
-}
-
-
-
-drflac_result decode_test_file_s32(libflac* pLibFlac, drflac* pFlac)
-{
- drflac_result result = DRFLAC_SUCCESS;
-
- /* Start with reading the entire file in one go. */
- if (result == DRFLAC_SUCCESS) {
- result = decode_test__read_and_compare_pcm_frame_chunks_s32(pLibFlac, pFlac, pLibFlac->pcmFrameCount);
- }
-
- /* Now try with reading one PCM frame at a time.*/
- if (result == DRFLAC_SUCCESS) {
- result = decode_test__read_and_compare_pcm_frame_chunks_s32(pLibFlac, pFlac, 1);
- }
-
- /* Now test FLAC frame boundaries. */
- if (result == DRFLAC_SUCCESS) {
- result = decode_test__read_and_compare_pcm_frame_chunks_s32(pLibFlac, pFlac, (pFlac->maxBlockSizeInPCMFrames > 0) ? pFlac->maxBlockSizeInPCMFrames : 4096);
- }
-
- return result;
-}
-
-drflac_result decode_test_file_f32(libflac* pLibFlac, drflac* pFlac)
-{
- drflac_result result = DRFLAC_SUCCESS;
-
- /* Start with reading the entire file in one go. */
- if (result == DRFLAC_SUCCESS) {
- result = decode_test__read_and_compare_pcm_frame_chunks_f32(pLibFlac, pFlac, pLibFlac->pcmFrameCount);
- }
-
- /* Now try with reading one PCM frame at a time.*/
- if (result == DRFLAC_SUCCESS) {
- result = decode_test__read_and_compare_pcm_frame_chunks_f32(pLibFlac, pFlac, 1);
- }
-
- /* Now test FLAC frame boundaries. */
- if (result == DRFLAC_SUCCESS) {
- result = decode_test__read_and_compare_pcm_frame_chunks_f32(pLibFlac, pFlac, (pFlac->maxBlockSizeInPCMFrames > 0) ? pFlac->maxBlockSizeInPCMFrames : 4096);
- }
-
- return result;
-}
-
-drflac_result decode_test_file_s16(libflac* pLibFlac, drflac* pFlac)
-{
- drflac_result result = DRFLAC_SUCCESS;
-
- /* Start with reading the entire file in one go. */
- if (result == DRFLAC_SUCCESS) {
- result = decode_test__read_and_compare_pcm_frame_chunks_s16(pLibFlac, pFlac, pLibFlac->pcmFrameCount);
- }
-
- /* Now try with reading one PCM frame at a time.*/
- if (result == DRFLAC_SUCCESS) {
- result = decode_test__read_and_compare_pcm_frame_chunks_s16(pLibFlac, pFlac, 1);
- }
-
- /* Now test FLAC frame boundaries. */
- if (result == DRFLAC_SUCCESS) {
- result = decode_test__read_and_compare_pcm_frame_chunks_s16(pLibFlac, pFlac, (pFlac->maxBlockSizeInPCMFrames > 0) ? pFlac->maxBlockSizeInPCMFrames : 4096);
- }
-
- return result;
-}
-
-static void on_meta(void* pUserData, drflac_metadata* pMetadata)
-{
- (void)pUserData;
-
- switch (pMetadata->type)
- {
- case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET:
- {
- /*
- This section is here just to make it easy to test cuesheets. It's not designed to integrate cleanly with the output of this program
- so I'm just selectively enabling and disabling this section as required.
- */
- #if 0
- drflac_cuesheet_track_iterator i;
- drflac_cuesheet_track track;
-
- printf("Cuesheet Found. Track Count = %d\n", (int)pMetadata->data.cuesheet.trackCount);
-
- drflac_init_cuesheet_track_iterator(&i, pMetadata->data.cuesheet.trackCount, pMetadata->data.cuesheet.pTrackData);
- while (drflac_next_cuesheet_track(&i, &track)) {
- drflac_uint32 iTrackIndex;
-
- printf("Cuesheet Track %d. Index Count = %d:\n", track.trackNumber, track.indexCount);
- for (iTrackIndex = 0; iTrackIndex < track.indexCount; iTrackIndex += 1) {
- printf(" Index %d - Offset %llu\n", iTrackIndex, track.pIndexPoints[iTrackIndex].offset);
- }
- }
- #endif
- } break;
- }
-}
-
-drflac_result decode_test_file(const char* pFilePath)
-{
- /* To test seeking we just seek to our target PCM frame and then decode whatever is remaining and compare it against libFLAC. */
- drflac_result result;
- libflac libflac;
- drflac* pFlac;
-
- dr_printf_fixed_with_margin(FILE_NAME_WIDTH, TABLE_MARGIN, "%s", dr_path_file_name(pFilePath));
-
- /* First load the decoder from libFLAC. */
- result = libflac_init_file(pFilePath, &libflac);
- if (result != DRFLAC_SUCCESS) {
- printf(" Failed to open via libFLAC.");
- return result;
- }
-
- /* Now load from dr_flac. */
- pFlac = drflac_open_file_with_metadata(pFilePath, on_meta, NULL, NULL);
- if (pFlac == NULL) {
- printf(" Failed to open via dr_flac.");
- libflac_uninit(&libflac);
- return DRFLAC_ERROR; /* Failed to load dr_flac decoder. */
- }
-
- /* At this point we should have both libFLAC and dr_flac decoders open. We can now perform identical operations on each of them and compare. */
- result = decode_test_file_s32(&libflac, pFlac);
- if (result != DRFLAC_SUCCESS) {
- drflac_close(pFlac);
- libflac_uninit(&libflac);
- return result;
- }
-
- result = decode_test_file_f32(&libflac, pFlac);
- if (result != DRFLAC_SUCCESS) {
- drflac_close(pFlac);
- libflac_uninit(&libflac);
- return result;
- }
-
- result = decode_test_file_s16(&libflac, pFlac);
- if (result != DRFLAC_SUCCESS) {
- drflac_close(pFlac);
- libflac_uninit(&libflac);
- return result;
- }
-
-
- /* We're done with our decoders. */
- drflac_close(pFlac);
- libflac_uninit(&libflac);
-
- if (result == DRFLAC_SUCCESS) {
- printf(" Passed");
- }
-
- return result;
-}
-
-drflac_result decode_test_directory(const char* pDirectoryPath)
-{
- dr_file_iterator iteratorState;
- dr_file_iterator* pFile;
-
- dr_printf_fixed(FILE_NAME_WIDTH, "%s", pDirectoryPath);
- dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "RESULT");
- printf("\n");
-
- pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
- while (pFile != NULL) {
- drflac_result result;
-
- /* Skip directories for now, but we may want to look at doing recursive file iteration. */
- if (!pFile->isDirectory) {
- result = decode_test_file(pFile->absolutePath);
- (void)result;
-
- printf("\n");
- }
-
- pFile = dr_file_iterator_next(pFile);
- }
-
- return DRFLAC_SUCCESS;
-}
-
-drflac_result decode_test()
-{
- drflac_result result = DRFLAC_SUCCESS;
-
- /* Directories. */
- {
- result = decode_test_directory(DEFAULT_SOURCE_DIR);
- (void)result;
- }
-
- return result;
-}
-
-
-
-drflac_result open_and_read_test_file_s32(libflac* pLibFlac, const char* pFilePath)
-{
- drflac_int32* pPCMFrames;
- drflac_uint64 pcmFrameCount;
- drflac_uint32 channels;
- drflac_uint32 sampleRate;
- drflac_uint64 iPCMFrame;
-
- pPCMFrames = drflac_open_file_and_read_pcm_frames_s32(pFilePath, &channels, &sampleRate, &pcmFrameCount, NULL);
- if (pPCMFrames == NULL) {
- printf(" drflac_open_and_read failed.");
- return DRFLAC_ERROR; /* Error decoding */
- }
-
- if (pcmFrameCount != pLibFlac->pcmFrameCount) {
- printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pLibFlac->currentPCMFrame, (int)pcmFrameCount);
- drflac_free(pPCMFrames, NULL);
- return DRFLAC_ERROR;
- }
-
- for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += 1) {
- drflac_int32* pPCMFrame_libflac = pLibFlac->pPCMFrames + (iPCMFrame * pLibFlac->channels);
- drflac_int32* pPCMFrame_drflac = pPCMFrames + (iPCMFrame * pLibFlac->channels);
- drflac_uint32 iChannel;
- drflac_bool32 hasError = DRFLAC_FALSE;
-
- for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
- if (pPCMFrame_libflac[iChannel] != pPCMFrame_drflac[iChannel]) {
- printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
- hasError = DRFLAC_TRUE;
- break;
- }
- }
-
- if (hasError) {
- drflac_free(pPCMFrames, NULL);
- return DRFLAC_ERROR; /* Decoded frames do not match. */
- }
- }
-
- drflac_free(pPCMFrames, NULL);
- return DRFLAC_SUCCESS;
-}
-
-drflac_result open_and_read_test_file_f32(libflac* pLibFlac, const char* pFilePath)
-{
- float* pPCMFrames;
- drflac_uint64 pcmFrameCount;
- drflac_uint32 channels;
- drflac_uint32 sampleRate;
- drflac_uint64 iPCMFrame;
-
- pPCMFrames = drflac_open_file_and_read_pcm_frames_f32(pFilePath, &channels, &sampleRate, &pcmFrameCount, NULL);
- if (pPCMFrames == NULL) {
- printf(" drflac_open_and_read failed.");
- return DRFLAC_ERROR; /* Error decoding */
- }
-
- if (pcmFrameCount != pLibFlac->pcmFrameCount) {
- printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pLibFlac->currentPCMFrame, (int)pcmFrameCount);
- drflac_free(pPCMFrames, NULL);
- return DRFLAC_ERROR;
- }
-
- for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += 1) {
- drflac_int32* pPCMFrame_libflac = pLibFlac->pPCMFrames + (iPCMFrame * pLibFlac->channels);
- float* pPCMFrame_drflac = pPCMFrames + (iPCMFrame * pLibFlac->channels);
- drflac_uint32 iChannel;
- drflac_bool32 hasError = DRFLAC_FALSE;
-
- for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
- if ((pPCMFrame_libflac[iChannel] / 2147483648.0) != pPCMFrame_drflac[iChannel]) {
- printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
- hasError = DRFLAC_TRUE;
- break;
- }
- }
-
- if (hasError) {
- drflac_free(pPCMFrames, NULL);
- return DRFLAC_ERROR; /* Decoded frames do not match. */
- }
- }
-
- drflac_free(pPCMFrames, NULL);
- return DRFLAC_SUCCESS;
-}
-
-drflac_result open_and_read_test_file_s16(libflac* pLibFlac, const char* pFilePath)
-{
- drflac_int16* pPCMFrames;
- drflac_uint64 pcmFrameCount;
- drflac_uint32 channels;
- drflac_uint32 sampleRate;
- drflac_uint64 iPCMFrame;
-
- pPCMFrames = drflac_open_file_and_read_pcm_frames_s16(pFilePath, &channels, &sampleRate, &pcmFrameCount, NULL);
- if (pPCMFrames == NULL) {
- printf(" drflac_open_and_read failed.");
- return DRFLAC_ERROR; /* Error decoding */
- }
-
- if (pcmFrameCount != pLibFlac->pcmFrameCount) {
- printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pLibFlac->currentPCMFrame, (int)pcmFrameCount);
- drflac_free(pPCMFrames, NULL);
- return DRFLAC_ERROR;
- }
-
- for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += 1) {
- drflac_int32* pPCMFrame_libflac = pLibFlac->pPCMFrames + (iPCMFrame * pLibFlac->channels);
- drflac_int16* pPCMFrame_drflac = pPCMFrames + (iPCMFrame * pLibFlac->channels);
- drflac_uint32 iChannel;
- drflac_bool32 hasError = DRFLAC_FALSE;
-
- for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
- if ((pPCMFrame_libflac[iChannel] >> 16) != pPCMFrame_drflac[iChannel]) {
- printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
- hasError = DRFLAC_TRUE;
- break;
- }
- }
-
- if (hasError) {
- drflac_free(pPCMFrames, NULL);
- return DRFLAC_ERROR; /* Decoded frames do not match. */
- }
- }
-
- drflac_free(pPCMFrames, NULL);
- return DRFLAC_SUCCESS;
-}
-
-drflac_result open_and_read_test_file(const char* pFilePath)
-{
- drflac_result result;
- libflac libflac;
-
- dr_printf_fixed_with_margin(FILE_NAME_WIDTH, TABLE_MARGIN, "%s", dr_path_file_name(pFilePath));
-
- /* First load the decoder from libFLAC. */
- result = libflac_init_file(pFilePath, &libflac);
- if (result != DRFLAC_SUCCESS) {
- printf(" Failed to open via libFLAC.");
- return result;
- }
-
- result = open_and_read_test_file_s32(&libflac, pFilePath);
- if (result != DRFLAC_SUCCESS) {
- return result;
- }
-
- open_and_read_test_file_f32(&libflac, pFilePath);
- if (result != DRFLAC_SUCCESS) {
- return result;
- }
-
- open_and_read_test_file_s16(&libflac, pFilePath);
- if (result != DRFLAC_SUCCESS) {
- return result;
- }
-
- libflac_uninit(&libflac);
-
- if (result == DRFLAC_SUCCESS) {
- printf(" Passed");
- }
-
- return result;
-}
-
-drflac_result open_and_read_test_directory(const char* pDirectoryPath)
-{
- dr_file_iterator iteratorState;
- dr_file_iterator* pFile;
-
- dr_printf_fixed(FILE_NAME_WIDTH, "%s", pDirectoryPath);
- dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "RESULT");
- printf("\n");
-
- pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
- while (pFile != NULL) {
- drflac_result result;
-
- /* Skip directories for now, but we may want to look at doing recursive file iteration. */
- if (!pFile->isDirectory) {
- result = open_and_read_test_file(pFile->absolutePath);
- (void)result;
-
- printf("\n");
- }
-
- pFile = dr_file_iterator_next(pFile);
- }
-
- return DRFLAC_SUCCESS;
-}
-
-drflac_result open_and_read_test()
-{
- drflac_result result = DRFLAC_SUCCESS;
-
- /* Directories. */
- {
- result = open_and_read_test_directory(DEFAULT_SOURCE_DIR);
- (void)result;
- }
-
- return result;
-}
-
-
-
-drflac_result decode_profiling_file(const char* pFilePath)
-{
- drflac_result result;
- libflac libflac;
- drflac* pFlac;
- drflac_int32* pTempBuffer;
- double decodeTimeBeg;
- double decodeTimeEnd;
- double drflacDecodeTimeInSeconds;
- void* pFileData;
- size_t fileSizeInBytes;
-
- dr_printf_fixed_with_margin(FILE_NAME_WIDTH, 2, "%s", dr_path_file_name(pFilePath));
-
- /* libFLAC */
- result = libflac_init_file(pFilePath, &libflac);
- if (result != DRFLAC_SUCCESS) {
- printf(" [libFLAC] Failed to load file");
- return result;
- }
-
- /* dr_flac */
- pFileData = dr_open_and_read_file(pFilePath, &fileSizeInBytes);
- if (pFileData == NULL) {
- printf(" Failed to load file");
- return DRFLAC_ERROR; /* Failed to open the file. */
- }
-
- pFlac = drflac_open_memory(pFileData, fileSizeInBytes, NULL);
- if (pFlac == NULL) {
- free(pFileData);
- printf(" [dr_flac] Failed to load file.");
- return DRFLAC_ERROR;
- }
-
- /* libFLAC decode time. */
- dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "%.2fms", libflac.decodeTimeInSeconds*1000);
-
- /* dr_flac decode time. */
- pTempBuffer = (drflac_int32*)malloc((size_t)(libflac.pcmFrameCount * libflac.channels * sizeof(drflac_int32)));
- if (pTempBuffer == NULL) {
- libflac_uninit(&libflac);
- drflac_close(pFlac);
- free(pFileData);
- printf(" Out of memory.");
- return DRFLAC_ERROR; /* Out of memory. */
- }
-
- DRFLAC_ZERO_MEMORY(pTempBuffer, (size_t)(libflac.pcmFrameCount * libflac.channels * sizeof(drflac_int32)));
-
- decodeTimeBeg = dr_timer_now();
- drflac_read_pcm_frames_s32(pFlac, libflac.pcmFrameCount, pTempBuffer);
- decodeTimeEnd = dr_timer_now();
-
- free(pTempBuffer);
- free(pFileData);
-
- drflacDecodeTimeInSeconds = decodeTimeEnd - decodeTimeBeg;
- dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "%.2fms", drflacDecodeTimeInSeconds*1000);
-
- /* Difference. */
- dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "%d%%", (int)(drflacDecodeTimeInSeconds/libflac.decodeTimeInSeconds * 100));
-
- libflac_uninit(&libflac);
- drflac_close(pFlac);
-
- return DRFLAC_SUCCESS;
-}
-
-drflac_result decode_profiling_directory(const char* pDirectoryPath)
-{
- dr_file_iterator iteratorState;
- dr_file_iterator* pFile;
- drflac_bool32 foundError = DRFLAC_FALSE;
-
- dr_printf_fixed(FILE_NAME_WIDTH, "%s", pDirectoryPath);
- dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "libFLAC");
- dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "dr_flac");
- printf("\n");
-
- pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
- while (pFile != NULL) {
- drflac_result result;
-
- /* Skip directories for now, but we may want to look at doing recursive file iteration. */
- if (!pFile->isDirectory) {
- result = decode_profiling_file(pFile->absolutePath);
- if (result != DRFLAC_SUCCESS) {
- foundError = DRFLAC_TRUE;
- }
-
- printf("\n");
- }
-
- pFile = dr_file_iterator_next(pFile);
- }
-
- return (foundError) ? DRFLAC_ERROR : DRFLAC_SUCCESS;
-}
-
-drflac_result decode_profiling()
-{
- drflac_result result = DRFLAC_SUCCESS;
-
- /* Directories. */
- {
- result = decode_profiling_directory(DEFAULT_SOURCE_DIR);
- }
-
- return result;
-}
-
-
-int main(int argc, char** argv)
-{
- drflac_result result = DRFLAC_SUCCESS;
- drflac_bool32 doTesting = DRFLAC_TRUE;
- drflac_bool32 doProfiling = DRFLAC_TRUE;
-
- /* This program has two main parts. The first is just a normal functionality test. The second is a profiling of the different seeking methods. */
- if (dr_argv_is_set(argc, argv, "--onlyprofile")) {
- doTesting = DRFLAC_FALSE;
- }
-
- print_cpu_caps();
-
- /* Exhaustive seek test. */
- if (doTesting) {
- printf("=======================================================================\n");
- printf("DECODE TESTING\n");
- printf("=======================================================================\n");
- result = decode_test();
- if (result != DRFLAC_SUCCESS) {
- return (int)result; /* Don't continue if an error occurs during testing. */
- }
- printf("\n");
-
- printf("=======================================================================\n");
- printf("OPEN-AND-READ TESTING - drflac_open_*_and_read_pcm_frames_*()\n");
- printf("=======================================================================\n");
- result = open_and_read_test();
- if (result != DRFLAC_SUCCESS) {
- return (int)result; /* Don't continue if an error occurs during testing. */
- }
- printf("\n");
- } else {
- printf("=======================================================================\n");
- printf("WARNING: Correctness Tests Disabled\n");
- printf("=======================================================================\n");
- }
-
- /* Profiling. */
- if (doProfiling) {
- printf("=======================================================================\n");
- printf("DECODE PROFILING (LOWER IS BETTER)\n");
- printf("=======================================================================\n");
- result = decode_profiling();
- printf("\n");
- }
-
- /*getchar();*/
- return (int)result;
-}
diff --git a/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_decoding.cpp b/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_decoding.cpp
deleted file mode 100644
index 48d966c..0000000
--- a/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_decoding.cpp
+++ /dev/null
@@ -1 +0,0 @@
-#include "dr_flac_decoding.c"
diff --git a/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_seeking.c b/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_seeking.c
deleted file mode 100644
index 70494d6..0000000
--- a/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_seeking.c
+++ /dev/null
@@ -1,483 +0,0 @@
-/*#define DR_FLAC_NO_CRC*/
-#include "dr_flac_common.c"
-
-#define PROFILING_NAME_WIDTH 40
-#define PROFILING_NUMBER_WIDTH 10
-#define PROFILING_NUMBER_MARGIN 2
-
-typedef struct
-{
- double totalSeconds_BruteForce;
- double totalSeconds_BinarySearch;
- double totalSeconds_SeekTable;
-} profiling_state;
-
-void profiling_state_init(profiling_state* pProfiling)
-{
- DRFLAC_ZERO_MEMORY(pProfiling, sizeof(*pProfiling));
-}
-
-profiling_state profiling_state_sum(const profiling_state* pA, const profiling_state* pB)
-{
- profiling_state result;
- result.totalSeconds_BruteForce = pA->totalSeconds_BruteForce + pB->totalSeconds_BruteForce;
- result.totalSeconds_BinarySearch = pA->totalSeconds_BinarySearch + pB->totalSeconds_BinarySearch;
- result.totalSeconds_SeekTable = pA->totalSeconds_SeekTable + pB->totalSeconds_SeekTable;
-
- return result;
-}
-
-
-drflac_result seek_test_pcm_frame(libflac* pLibFlac, drflac* pFlac, drflac_uint64 targetPCMFrameIndex)
-{
- drflac_bool32 seekResult;
- drflac_uint64 pcmFrameCount_libflac;
- drflac_uint64 pcmFrameCount_drflac;
- drflac_int32* pPCMFrames_libflac;
- drflac_int32* pPCMFrames_drflac;
- drflac_uint64 iPCMFrame;
-
- if (pFlac->_noSeekTableSeek == DRFLAC_FALSE && pFlac->_noBinarySearchSeek == DRFLAC_TRUE && pFlac->_noBruteForceSeek == DRFLAC_TRUE) {
- if (pFlac->seekpointCount == 0) {
- printf(" No seek table");
- return DRFLAC_ERROR;
- }
- }
-
- /*
- To test seeking we just seek to the PCM frame, and then decode the rest of the file. If the PCM frames we read
- differs between the two implementations there's something wrong with one of them (probably dr_flac).
- */
- seekResult = libflac_seek_to_pcm_frame(pLibFlac, targetPCMFrameIndex);
- if (seekResult == DRFLAC_FALSE) {
- printf(" [libFLAC] Failed to seek to PCM frame @ %d", (int)targetPCMFrameIndex);
- return DRFLAC_ERROR;
- }
-
- seekResult = drflac_seek_to_pcm_frame(pFlac, targetPCMFrameIndex);
- if (seekResult == DRFLAC_FALSE) {
- printf(" [dr_flac] Failed to seek to PCM frame @ %d", (int)targetPCMFrameIndex);
- return DRFLAC_ERROR;
- }
-
- if (pLibFlac->currentPCMFrame != pFlac->currentPCMFrame) {
- printf(" Current PCM frame inconsistent @ %d: libFLAC=%d, dr_flac=%d", (int)targetPCMFrameIndex, (int)pLibFlac->currentPCMFrame, (int)pFlac->currentPCMFrame);
- return DRFLAC_ERROR;
- }
-
- /*
- Now we decode the rest of the file and compare the samples. Note that we try reading the _entire_ file, not just the leftovers, to ensure we
- haven't seeked too short.
- */
- pPCMFrames_libflac = (drflac_int32*)malloc((size_t)(pLibFlac->pcmFrameCount * pLibFlac->channels * sizeof(drflac_int32)));
- if (pPCMFrames_libflac == NULL) {
- printf(" [libFLAC] Out of memory");
- return DRFLAC_ERROR;
- }
- pcmFrameCount_libflac = libflac_read_pcm_frames_s32(pLibFlac, pLibFlac->pcmFrameCount, pPCMFrames_libflac);
-
- pPCMFrames_drflac = (drflac_int32*)malloc((size_t)(pLibFlac->pcmFrameCount * pLibFlac->channels * sizeof(drflac_int32)));
- if (pPCMFrames_drflac == NULL) {
- free(pPCMFrames_libflac);
- printf(" [dr_flac] Out of memory");
- return DRFLAC_ERROR;
- }
- pcmFrameCount_drflac = drflac_read_pcm_frames_s32(pFlac, pLibFlac->pcmFrameCount, pPCMFrames_drflac);
-
- /* The total number of frames we decoded need to match. */
- if (pcmFrameCount_libflac != pcmFrameCount_drflac) {
- free(pPCMFrames_drflac);
- free(pPCMFrames_libflac);
- printf(" Decoded frame counts differ @ %d: libFLAC=%d, dr_flac=%d", (int)targetPCMFrameIndex, (int)pLibFlac->currentPCMFrame, (int)pFlac->currentPCMFrame);
- return DRFLAC_ERROR;
- }
-
- /* Each of the decoded PCM frames need to match. */
- DRFLAC_ASSERT(pcmFrameCount_libflac == pcmFrameCount_drflac);
-
- for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libflac; iPCMFrame += 1) {
- drflac_int32* pPCMFrame_libflac = pPCMFrames_libflac + (iPCMFrame * pLibFlac->channels);
- drflac_int32* pPCMFrame_drflac = pPCMFrames_drflac + (iPCMFrame * pLibFlac->channels);
- drflac_uint32 iChannel;
- drflac_bool32 hasError = DRFLAC_FALSE;
-
- for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
- if (pPCMFrame_libflac[iChannel] != pPCMFrame_drflac[iChannel]) {
- printf(" PCM Frame @ %d[%d] does not match: targetPCMFrameIndex=%d", (int)iPCMFrame, iChannel, (int)targetPCMFrameIndex);
- hasError = DRFLAC_TRUE;
- break;
- }
- }
-
- if (hasError) {
- free(pPCMFrames_drflac);
- free(pPCMFrames_libflac);
- return DRFLAC_ERROR; /* Decoded frames do not match. */
- }
- }
-
-
- /* Done. */
- free(pPCMFrames_drflac);
- free(pPCMFrames_libflac);
-
- return DRFLAC_SUCCESS;
-}
-
-drflac_result seek_test_file(const char* pFilePath)
-{
- /* To test seeking we just seek to our target PCM frame and then decode whatever is remaining and compare it against libFLAC. */
- drflac_result result;
- libflac libflac;
- drflac* pFlac;
- drflac_uint32 iteration;
- drflac_uint32 totalIterationCount = 10;
-
- dr_printf_fixed_with_margin(PROFILING_NAME_WIDTH, PROFILING_NUMBER_MARGIN, "%s", dr_path_file_name(pFilePath));
-
- /* First load the decoder from libFLAC. */
- result = libflac_init_file(pFilePath, &libflac);
- if (result != DRFLAC_SUCCESS) {
- printf(" Failed to open via libFLAC.");
- return result;
- }
-
- /* Now load from dr_flac. */
- pFlac = drflac_open_file(pFilePath, NULL);
- if (pFlac == NULL) {
- printf(" Failed to open via dr_flac.");
- libflac_uninit(&libflac);
- return DRFLAC_ERROR; /* Failed to load dr_flac decoder. */
- }
-
- /* Use these to use specific seeking methods. Set all to false to use the normal prioritization (seek table, then binary search, then brute force). */
- pFlac->_noSeekTableSeek = DRFLAC_FALSE;
- pFlac->_noBinarySearchSeek = DRFLAC_FALSE;
- pFlac->_noBruteForceSeek = DRFLAC_FALSE;
-
- /* At this point we should have both libFLAC and dr_flac decoders open. We can now perform identical operations on each of them and compare. */
-
- /* Start with the basics: Seek to the very end, and then the very start. */
- if (result == DRFLAC_SUCCESS) {
- result = seek_test_pcm_frame(&libflac, pFlac, libflac.pcmFrameCount);
- }
- if (result == DRFLAC_SUCCESS) {
- result = seek_test_pcm_frame(&libflac, pFlac, 0);
- }
-
- /* Now we'll try seeking to random locations. */
- dr_seed(1234);
-
- iteration = 0;
- while (result == DRFLAC_SUCCESS && iteration < totalIterationCount) {
- dr_uint64 targetPCMFrame = dr_rand_range_u64(0, libflac.pcmFrameCount);
- if (targetPCMFrame > libflac.pcmFrameCount) {
- DRFLAC_ASSERT(DRFLAC_FALSE); /* Should never hit this, but if we do it means our random number generation routine is wrong. */
- }
-
- result = seek_test_pcm_frame(&libflac, pFlac, targetPCMFrame);
- iteration += 1;
- }
-
- /* We're done with our decoders. */
- drflac_close(pFlac);
- libflac_uninit(&libflac);
-
- if (result == DRFLAC_SUCCESS) {
- printf(" Passed");
- }
-
- return result;
-}
-
-drflac_result seek_test_directory(const char* pDirectoryPath)
-{
- dr_file_iterator iteratorState;
- dr_file_iterator* pFile;
-
- dr_printf_fixed(PROFILING_NAME_WIDTH, "%s", pDirectoryPath);
- dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "RESULT");
- printf("\n");
-
- pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
- while (pFile != NULL) {
- drflac_result result;
-
- /* Skip directories for now, but we may want to look at doing recursive file iteration. */
- if (!pFile->isDirectory) {
- result = seek_test_file(pFile->absolutePath);
- (void)result;
-
- printf("\n");
- }
-
- pFile = dr_file_iterator_next(pFile);
- }
-
- return DRFLAC_SUCCESS;
-}
-
-drflac_result seek_test()
-{
- drflac_result result = DRFLAC_SUCCESS;
-
- /* Directories. */
- {
- result = seek_test_directory("testvectors/flac/tests");
- (void)result;
- }
-
- return result;
-}
-
-
-drflac_result seek_profiling_drflac_and_close(drflac* pFlac, double* pProcessingTime)
-{
- drflac_result result = DRFLAC_SUCCESS;
- int i;
-
- if (pFlac == NULL) {
- result = DRFLAC_INVALID_ARGS;
- goto done;
- }
-
- if (pFlac->totalPCMFrameCount == 0) {
- result = DRFLAC_INVALID_ARGS;
- goto done;
- }
-
- if (pProcessingTime != NULL) {
- *pProcessingTime = 0;
- }
-
- /* Seek back to the start to keep everything normalized. */
- drflac_seek_to_pcm_frame(pFlac, 0);
-
- /* Random seek points based on a seed. */
- dr_seed(1234);
- /*dr_seed(4321);*/
- for (i = 0; i < 100; ++i) {
- double startTime;
- double endTime;
- dr_uint64 targetPCMFrame = dr_rand_range_u64(0, pFlac->totalPCMFrameCount);
-
- startTime = dr_timer_now();
- {
- drflac_seek_to_pcm_frame(pFlac, targetPCMFrame);
- }
- endTime = dr_timer_now();
-
- if (pProcessingTime != NULL) {
- *pProcessingTime += (endTime - startTime);
- }
- }
-
-done:
- drflac_close(pFlac);
- return result;
-}
-
-drflac_result seek_profiling_file__seek_table(const char* pFilePath, double* pProcessingTime)
-{
- drflac* pFlac;
-
- if (pFilePath == NULL) {
- return DRFLAC_INVALID_ARGS;
- }
-
- pFlac = drflac_open_file(pFilePath, NULL);
- if (pFlac == NULL) {
- return DRFLAC_ERROR;
- }
-
- pFlac->_noSeekTableSeek = DRFLAC_FALSE;
- pFlac->_noBinarySearchSeek = DRFLAC_TRUE;
- pFlac->_noBruteForceSeek = DRFLAC_TRUE;
-
- return seek_profiling_drflac_and_close(pFlac, pProcessingTime);
-}
-
-drflac_result seek_profiling_file__binary_search(const char* pFilePath, double* pProcessingTime)
-{
- drflac* pFlac;
-
- if (pFilePath == NULL) {
- return DRFLAC_INVALID_ARGS;
- }
-
- pFlac = drflac_open_file(pFilePath, NULL);
- if (pFlac == NULL) {
- return DRFLAC_ERROR;
- }
-
- pFlac->_noSeekTableSeek = DRFLAC_TRUE;
- pFlac->_noBinarySearchSeek = DRFLAC_FALSE;
- pFlac->_noBruteForceSeek = DRFLAC_TRUE;
-
- return seek_profiling_drflac_and_close(pFlac, pProcessingTime);
-}
-
-drflac_result seek_profiling_file__brute_force(const char* pFilePath, double* pProcessingTime)
-{
- drflac* pFlac;
-
- if (pFilePath == NULL) {
- return DRFLAC_INVALID_ARGS;
- }
-
- pFlac = drflac_open_file(pFilePath, NULL);
- if (pFlac == NULL) {
- return DRFLAC_ERROR;
- }
-
- pFlac->_noSeekTableSeek = DRFLAC_TRUE;
- pFlac->_noBinarySearchSeek = DRFLAC_TRUE;
- pFlac->_noBruteForceSeek = DRFLAC_FALSE;
-
- return seek_profiling_drflac_and_close(pFlac, pProcessingTime);
-}
-
-drflac_result seek_profiling_file(const char* pFilePath, profiling_state* pProfiling)
-{
- drflac_result result;
-
- profiling_state_init(pProfiling);
-
- /*
- There are different seeking modes, and each one is profiled so that we can compare the results:
- - Brute Force
- - Binary Search
- - Seek Table
-
- In order to keep the total run time fair, we can only include files with a seek table.
- */
- dr_printf_fixed_with_margin(PROFILING_NAME_WIDTH, 2, "%s", dr_path_file_name(pFilePath));
-
- /* Start off with the seek table version. If this fails we don't bother continuing. */
-#if 1
- result = seek_profiling_file__seek_table(pFilePath, &pProfiling->totalSeconds_SeekTable);
- if (result != DRFLAC_SUCCESS) {
- return result;
- }
- dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "%f", pProfiling->totalSeconds_SeekTable);
-#else
- dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "");
-#endif
-
-#if 1
- result = seek_profiling_file__binary_search(pFilePath, &pProfiling->totalSeconds_BinarySearch);
- if (result != DRFLAC_SUCCESS) {
- return result;
- }
- dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "%f", pProfiling->totalSeconds_BinarySearch);
-#else
- dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "");
-#endif
-
-#if 1
- result = seek_profiling_file__brute_force(pFilePath, &pProfiling->totalSeconds_BruteForce);
- if (result != DRFLAC_SUCCESS) {
- return result;
- }
- dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "%f", pProfiling->totalSeconds_BruteForce);
-#else
- dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "");
-#endif
-
- return DRFLAC_SUCCESS;
-}
-
-drflac_result seek_profiling_directory(const char* pDirectoryPath, profiling_state* pProfiling)
-{
- dr_file_iterator iteratorState;
- dr_file_iterator* pFile;
-
- profiling_state_init(pProfiling);
-
- dr_printf_fixed(PROFILING_NAME_WIDTH, "%s", pDirectoryPath);
- dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "S/Table");
- dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "Bin Srch");
- dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "B/Force");
- printf("\n");
-
- pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
- while (pFile != NULL) {
- drflac_result result;
- profiling_state fileProfiling;
-
- /* Skip directories for now, but we may want to look at doing recursive file iteration. */
- if (!pFile->isDirectory) {
- result = seek_profiling_file(pFile->absolutePath, &fileProfiling);
- if (result == DRFLAC_SUCCESS) {
- *pProfiling = profiling_state_sum(pProfiling, &fileProfiling);
- }
-
- printf("\n");
- }
-
- pFile = dr_file_iterator_next(pFile);
- }
-
- return DRFLAC_SUCCESS;
-}
-
-drflac_result seek_profiling()
-{
- drflac_result result = DRFLAC_SUCCESS;
- profiling_state globalProfiling;
-
- profiling_state_init(&globalProfiling);
-
- /* Directories. */
- {
- profiling_state directoryProfiling;
-
- result = seek_profiling_directory("testvectors/flac/tests", &directoryProfiling);
- if (result == DRFLAC_SUCCESS) {
- globalProfiling = profiling_state_sum(&globalProfiling, &directoryProfiling);
- }
- }
-
- return result;
-}
-
-
-int main(int argc, char** argv)
-{
- drflac_result result = DRFLAC_SUCCESS;
- drflac_bool32 doTesting = DRFLAC_TRUE;
- drflac_bool32 doProfiling = DRFLAC_TRUE;
-
- /* This program has two main parts. The first is just a normal functionality test. The second is a profiling of the different seeking methods. */
- if (dr_argv_is_set(argc, argv, "--onlyprofile")) {
- doTesting = DRFLAC_FALSE;
- }
-
- /* Exhaustive seek test. */
- if (doTesting) {
- printf("=======================================================================\n");
- printf("SEEK TESTING\n");
- printf("=======================================================================\n");
- result = seek_test();
- if (result != DRFLAC_SUCCESS) {
- return (int)result; /* Don't continue if an error occurs during testing. */
- }
- printf("\n");
- } else {
- printf("=======================================================================\n");
- printf("WARNING: Correctness Tests Disabled\n");
- printf("=======================================================================\n");
- }
-
- /* Profiling. */
- if (doProfiling) {
- printf("=======================================================================\n");
- printf("SEEK PROFILING\n");
- printf("=======================================================================\n");
- result = seek_profiling();
- printf("\n");
- }
-
- /*getchar();*/
- return (int)result;
-}
diff --git a/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_test_0.c b/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_test_0.c
deleted file mode 100644
index ffc28c8..0000000
--- a/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_test_0.c
+++ /dev/null
@@ -1,11 +0,0 @@
-#define DR_FLAC_IMPLEMENTATION
-#include "../../dr_flac.h"
-
-#include "../common/dr_common.c"
-
-int main(int argc, char** argv)
-{
- (void)argc;
- (void)argv;
- return 0;
-}
diff --git a/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_test_0.cpp b/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_test_0.cpp
deleted file mode 100644
index ce77013..0000000
--- a/source/engine/thirdparty/dr_libs/tests/flac/dr_flac_test_0.cpp
+++ /dev/null
@@ -1 +0,0 @@
-#include "dr_flac_test_0.c"
diff --git a/source/engine/thirdparty/dr_libs/tests/flac/fuzz_dr_flac.c b/source/engine/thirdparty/dr_libs/tests/flac/fuzz_dr_flac.c
deleted file mode 100644
index f4b6274..0000000
--- a/source/engine/thirdparty/dr_libs/tests/flac/fuzz_dr_flac.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Fuzz tester for dr_flac.h
- *
- * compile with
- * clang -g -O1 -fsanitize=fuzzer,address -o fuzz_dr_flac fuzz_dr_flac.c
- *
- * and run ./fuzz_dr_flac to run fuzz testing
- *
- * Other sanitizers are possible, for example
- * -fsanitize=fuzzer,memory
- * -fsanitize=fuzzer,undefined
- *
- * For more options, run ./fuzz_dr_flac -help=1
- *
- * If a problem is found, the problematic input is saved and can be
- * rerun (with for example a debugger) with
- *
- * ./fuzz_dr_flac file
- *
- */
-
-#include
-
-#define DR_FLAC_IMPLEMENTATION
-#define DR_FLAC_NO_CRC
-#define DR_FLAC_NO_STDIO
-#include "../../dr_flac.h"
-
-#define MIN(a,b) (((a)<(b))?(a):(b))
-
-uint8_t fuzz_flacstream[4096] = {0};
-size_t fuzz_flacstream_position;
-size_t fuzz_flacstream_length;
-
-static size_t read_fuzz_flacstream(void* pUserData, void* bufferOut, size_t bytesToRead)
-{
- size_t readsize = MIN(bytesToRead, fuzz_flacstream_length-fuzz_flacstream_position);
- if (readsize > 0) {
- memcpy(bufferOut, fuzz_flacstream+fuzz_flacstream_position, readsize);
- fuzz_flacstream_position += readsize;
- return readsize;
- } else {
- return 0;
- }
-}
-
-static drflac_bool32 seek_fuzz_flacstream(void* pUserData, int offset, drflac_seek_origin origin)
-{
- if ((int)fuzz_flacstream_position+offset < 0 || (int)fuzz_flacstream_position+offset > fuzz_flacstream_length) {
- return 1;
- } else {
- fuzz_flacstream_position = (int)fuzz_flacstream_position+offset;
- return 0;
- }
-}
-
-int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
-{
- if (size > 2) {
- drflac * drflac_fuzzer;
- drflac_int32 drflac_fuzzer_out[2048] = {0}; /* 256 samples over 8 channels */
- drflac_container container = data[0] & 1 ? drflac_container_native : drflac_container_ogg;
-
- memcpy(fuzz_flacstream, data, (size-1)<4096?(size-1):4096);
-
- fuzz_flacstream_position = 0;
- fuzz_flacstream_length = size-1;
-
- drflac_fuzzer = drflac_open_relaxed(read_fuzz_flacstream, seek_fuzz_flacstream, container, NULL, NULL);
-
- while (drflac_read_pcm_frames_s32(drflac_fuzzer, 256, drflac_fuzzer_out));
-
- drflac_close(drflac_fuzzer);
- }
- return 0;
-}
diff --git a/source/engine/thirdparty/dr_libs/tests/mp3/dr_mp3_common.c b/source/engine/thirdparty/dr_libs/tests/mp3/dr_mp3_common.c
deleted file mode 100644
index 813e9df..0000000
--- a/source/engine/thirdparty/dr_libs/tests/mp3/dr_mp3_common.c
+++ /dev/null
@@ -1,4 +0,0 @@
-#define DR_MP3_IMPLEMENTATION
-#include "../../dr_mp3.h"
-
-#include "../common/dr_common.c"
diff --git a/source/engine/thirdparty/dr_libs/tests/mp3/dr_mp3_playback.c b/source/engine/thirdparty/dr_libs/tests/mp3/dr_mp3_playback.c
deleted file mode 100644
index 17953a3..0000000
--- a/source/engine/thirdparty/dr_libs/tests/mp3/dr_mp3_playback.c
+++ /dev/null
@@ -1,161 +0,0 @@
-#include "dr_mp3_common.c"
-
-#define MA_NO_DECODING
-#define MA_NO_ENCODING
-#define MINIAUDIO_IMPLEMENTATION
-#include "../external/miniaudio/miniaudio.h"
-
-void data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
-{
- drmp3* pMP3;
-
- pMP3 = (drmp3*)pDevice->pUserData;
- DRMP3_ASSERT(pMP3 != NULL);
-
- if (pDevice->playback.format == ma_format_f32) {
- drmp3_read_pcm_frames_f32(pMP3, frameCount, pFramesOut);
- } else if (pDevice->playback.format == ma_format_s16) {
- drmp3_read_pcm_frames_s16(pMP3, frameCount, pFramesOut);
- } else {
- DRMP3_ASSERT(DRMP3_FALSE); /* Should never get here. */
- }
-
- (void)pFramesIn;
-}
-
-int do_decoding_validation(const char* pFilePath)
-{
- int result = 0;
- drmp3 mp3Memory;
- drmp3 mp3File;
- size_t dataSize;
- void* pData;
-
- /*
- When opening from a memory buffer, dr_mp3 will take a different path for decoding which is optimized to reduce data movement. Since it's
- running on a separate path, we need to ensure it's returning consistent results with the other code path which will be used when decoding
- from a file.
- */
-
- /* Initialize the memory decoder. */
- pData = dr_open_and_read_file(pFilePath, &dataSize);
- if (pData == NULL) {
- printf("Failed to open file \"%s\"", pFilePath);
- return -1;
- }
-
- if (!drmp3_init_memory(&mp3Memory, pData, dataSize, NULL)) {
- free(pData);
- printf("Failed to init MP3 decoder \"%s\"", pFilePath);
- return -1;
- }
-
-
- /* Initialize the file decoder. */
- if (!drmp3_init_file(&mp3File, pFilePath, NULL)) {
- drmp3_uninit(&mp3Memory);
- free(pData);
- printf("Failed to open file \"%s\"", pFilePath);
- return -1;
- }
-
- DRMP3_ASSERT(mp3Memory.channels == mp3File.channels);
-
-
- /* Compare. */
- for (;;) {
- drmp3_uint64 iSample;
- drmp3_uint64 pcmFrameCountMemory;
- drmp3_uint64 pcmFrameCountFile;
- drmp3_int16 pcmFramesMemory[4096];
- drmp3_int16 pcmFramesFile[4096];
-
- pcmFrameCountMemory = drmp3_read_pcm_frames_s16(&mp3Memory, DRMP3_COUNTOF(pcmFramesMemory) / mp3Memory.channels, pcmFramesMemory);
- pcmFrameCountFile = drmp3_read_pcm_frames_s16(&mp3File, DRMP3_COUNTOF(pcmFramesFile) / mp3File.channels, pcmFramesFile);
-
- /* Check the frame count first. */
- if (pcmFrameCountMemory != pcmFrameCountFile) {
- printf("Frame counts differ: memory = %d; file = %d\n", (int)pcmFrameCountMemory, (int)pcmFrameCountFile);
- result = -1;
- break;
- }
-
- /* Check individual frames. */
- DRMP3_ASSERT(pcmFrameCountMemory == pcmFrameCountFile);
- for (iSample = 0; iSample < pcmFrameCountMemory * mp3Memory.channels; iSample += 1) {
- if (pcmFramesMemory[iSample] != pcmFramesFile[iSample]) {
- printf("Samples differ: memory = %d; file = %d\n", (int)pcmFramesMemory[iSample], (int)pcmFramesFile[iSample]);
- result = -1;
- break;
- }
- }
-
- /* We've reached the end if we didn't return any PCM frames. */
- if (pcmFrameCountMemory == 0 || pcmFrameCountFile == 0) {
- break;
- }
- }
-
-
- drmp3_uninit(&mp3File);
- drmp3_uninit(&mp3Memory);
- free(pData);
- return result;
-}
-
-int main(int argc, char** argv)
-{
- drmp3 mp3;
- ma_result resultMA;
- ma_device_config deviceConfig;
- ma_device device;
- const char* pInputFilePath;
-
- if (argc < 2) {
- printf("No input file.\n");
- return -1;
- }
-
- pInputFilePath = argv[1];
-
- /* Quick validation test first. */
- if (do_decoding_validation(pInputFilePath) != 0) {
- return -1;
- }
-
-
- if (!drmp3_init_file(&mp3, pInputFilePath, NULL)) {
- printf("Failed to open file \"%s\"", pInputFilePath);
- return -1;
- }
-
- deviceConfig = ma_device_config_init(ma_device_type_playback);
- deviceConfig.playback.format = ma_format_s16;
- deviceConfig.playback.channels = mp3.channels;
- deviceConfig.sampleRate = mp3.sampleRate;
- deviceConfig.dataCallback = data_callback;
- deviceConfig.pUserData = &mp3;
- resultMA = ma_device_init(NULL, &deviceConfig, &device);
- if (resultMA != MA_SUCCESS) {
- drmp3_uninit(&mp3);
- printf("Failed to initialize playback device: %s.\n", ma_result_description(resultMA));
- return -1;
- }
-
- resultMA = ma_device_start(&device);
- if (resultMA != MA_SUCCESS) {
- ma_device_uninit(&device);
- drmp3_uninit(&mp3);
- printf("Failed to start playback device: %s.\n", ma_result_description(resultMA));
- return -1;
- }
-
- printf("Press Enter to quit...");
- getchar();
-
- /* We're done. */
- ma_device_uninit(&device);
- drmp3_uninit(&mp3);
-
- return 0;
-}
\ No newline at end of file
diff --git a/source/engine/thirdparty/dr_libs/tests/mp3/dr_mp3_test_0.c b/source/engine/thirdparty/dr_libs/tests/mp3/dr_mp3_test_0.c
deleted file mode 100644
index db14b7b..0000000
--- a/source/engine/thirdparty/dr_libs/tests/mp3/dr_mp3_test_0.c
+++ /dev/null
@@ -1,12 +0,0 @@
-#define DR_MP3_IMPLEMENTATION
-#include "../../dr_mp3.h"
-
-#include "../common/dr_common.c"
-
-int main(int argc, char** argv)
-{
- (void)argc;
- (void)argv;
- return 0;
-}
-
diff --git a/source/engine/thirdparty/dr_libs/tests/opus/dr_opus_decoding.c b/source/engine/thirdparty/dr_libs/tests/opus/dr_opus_decoding.c
deleted file mode 100644
index 76c5c60..0000000
--- a/source/engine/thirdparty/dr_libs/tests/opus/dr_opus_decoding.c
+++ /dev/null
@@ -1,205 +0,0 @@
-/*#define DR_OPUS_DEBUGGING*/
-
-#define DR_OPUS_IMPLEMENTATION
-#include "../../wip/dr_opus.h"
-#include "../common/dr_common.c"
-
-const char* dropus_mode_to_string(dropus_mode mode) /* Move this into dr_opus.h? */
-{
- switch (mode)
- {
- case dropus_mode_silk: return "SILK";
- case dropus_mode_celt: return "CELT";
- case dropus_mode_hybrid: return "Hybrid";
- default: break;
- }
-
- return "Unknown";
-}
-
-#include
-
-/* Forward declare our debugging entry point if necessary. */
-#ifdef DR_OPUS_DEBUGGING
-int main_debugging(int argc, char** argv);
-#endif
-
-dropus_result test_standard_vector(const char* pFilePath)
-{
- dropus_result result;
- dropus_stream stream;
- void* pFileData;
- size_t fileSize;
- const dropus_uint8* pRunningData8;
- size_t runningPos;
- dropus_uint32 iPacket = 0; /* For error reporting. */
-
-
- /* Load the entire file into memory first. */
- pFileData = dr_open_and_read_file(pFilePath, &fileSize);
- if (pFileData == NULL) {
- return DROPUS_ERROR; /* File not found. */
- }
-
- /* Now initialize the stream in preparation for decoding packets. */
- result = dropus_stream_init(&stream);
- if (result != DROPUS_SUCCESS) {
- free(pFileData);
- return result;
- }
-
- /*
- Here is where we scan the test vector. The way the file is structured is quite simple. It is made up of a list of Opus packets, each
- of which include an 8 byte header where the first 4 bytes is the size in bytes of the Opus packet (little endian) and the next 4 bytes
- contain the 32-bit range state which we'll use for validation.
- */
- pRunningData8 = (const dropus_uint8*)pFileData;
- runningPos = 0;
-
- /* For each packet... */
- while (runningPos < fileSize) {
- dropus_result decodeResult;
- dropus_uint32 packetSize;
- dropus_uint32 rangeState;
-
- memcpy(&packetSize, pRunningData8 + 0, 4);
- memcpy(&rangeState, pRunningData8 + 4, 4);
-
- packetSize = dropus__be2host_32(packetSize);
- rangeState = dropus__be2host_32(rangeState);
-
- pRunningData8 += 8;
- runningPos += 8;
-
- /* Safety. Break if we've run out of data. */
- if ((runningPos + packetSize) > fileSize) {
- printf("WARNING: Ran out of data before the end of the file.\n");
- break;
- }
-
- decodeResult = dropus_stream_decode_packet(&stream, pRunningData8, packetSize);
- if (decodeResult != DROPUS_SUCCESS) {
- result = DROPUS_ERROR;
- printf("Failed to decode packet %d\n", iPacket);
- }
-
- printf("Opus Packet %d: Mode=%s\n", iPacket, dropus_mode_to_string(dropus_toc_mode(stream.packet.toc)));
-
- pRunningData8 += packetSize;
- runningPos += packetSize;
- iPacket += 1;
- }
-
- free(pFileData);
- return result;
-}
-
-dropus_result test_standard_vectors_folder(const char* pFolderPath)
-{
- dropus_bool32 foundError = DROPUS_FALSE;
- dr_file_iterator iteratorState;
- dr_file_iterator* pFile;
-
- pFile = dr_file_iterator_begin(pFolderPath, &iteratorState);
- while (pFile != NULL) {
- /* Only look at files with the extension "bit". */
- if (dr_extension_equal(pFile->relativePath, "bit")) {
- if (test_standard_vector(pFile->absolutePath) != DROPUS_SUCCESS) {
- foundError = DROPUS_TRUE;
- }
- }
-
- pFile = dr_file_iterator_next(pFile);
- }
-
- if (foundError) {
- return DROPUS_ERROR;
- } else {
- return DROPUS_SUCCESS;
- }
-}
-
-dropus_result test_standard_vectors()
-{
- dropus_bool32 foundError = DROPUS_FALSE;
-
- /* Two groups of standard test vectors. The original vectors and the "new" vectors. */
-
- /* Original vectors. */
- if (test_standard_vectors_folder("testvectors/opus/opus_testvectors") != DROPUS_SUCCESS) {
- foundError = DROPUS_TRUE;
- }
-
- /* New vectors. */
- if (test_standard_vectors_folder("testvectors/opus/opus_newvectors") != DROPUS_SUCCESS) {
- foundError = DROPUS_TRUE;
- }
-
- if (foundError) {
- return DROPUS_ERROR;
- } else {
- return DROPUS_SUCCESS;
- }
-}
-
-dropus_result test_ogg_vectors()
-{
- dropus_result result;
-
- /* Ogg Opus test vectors are in the "oggopus" folder. */
- result = DROPUS_SUCCESS;
-
- return result;
-}
-
-int main(int argc, char** argv)
-{
- dropus_result result;
-
- /* Do debugging stuff first. */
-#ifdef DR_OPUS_DEBUGGING
- main_debugging(argc, argv);
-#endif
-
- result = test_standard_vector("testvectors/opus/opus_testvectors/testvector02.bit");
- if (result != DROPUS_SUCCESS) {
- /*return (int)result;*/
- }
-
- /* Test standard vectors first. */
- result = test_standard_vectors();
- if (result != DROPUS_SUCCESS) {
- /*return (int)result;*/
- }
-
- /* Ogg Opus. */
- result = test_ogg_vectors();
- if (result != DROPUS_SUCCESS) {
- /*return (int)result;*/
- }
-
- (void)argc;
- (void)argv;
- return 0;
-}
-
-#ifdef DR_OPUS_DEBUGGING
-int main_debugging(int argc, char** argv)
-{
- dr_file_iterator iterator;
- dr_file_iterator* pFile = dr_file_iterator_begin("testvectors/opus/oggopus", &iterator);
- while (pFile != NULL) {
- if (pFile->isDirectory) {
- printf("DIRECTORY: %s : %s\n", pFile->relativePath, pFile->absolutePath);
- } else {
- printf("FILE: %s : %s\n", pFile->relativePath, pFile->absolutePath);
- }
-
- pFile = dr_file_iterator_next(pFile);
- }
-
- (void)argc;
- (void)argv;
- return 0;
-}
-#endif
diff --git a/source/engine/thirdparty/dr_libs/tests/opus/dr_opus_test_0.c b/source/engine/thirdparty/dr_libs/tests/opus/dr_opus_test_0.c
deleted file mode 100644
index 0e073b7..0000000
--- a/source/engine/thirdparty/dr_libs/tests/opus/dr_opus_test_0.c
+++ /dev/null
@@ -1,9 +0,0 @@
-#define DR_OPUS_IMPLEMENTATION
-#include "../../wip/dr_opus.h"
-
-int main(int argc, char** argv)
-{
- (void)argc;
- (void)argv;
- return 0;
-}
\ No newline at end of file
diff --git a/source/engine/thirdparty/dr_libs/tests/opus/dr_opus_test_0.cpp b/source/engine/thirdparty/dr_libs/tests/opus/dr_opus_test_0.cpp
deleted file mode 100644
index 97401ce..0000000
--- a/source/engine/thirdparty/dr_libs/tests/opus/dr_opus_test_0.cpp
+++ /dev/null
@@ -1 +0,0 @@
-#include "dr_opus_test_0.c"
diff --git a/source/engine/thirdparty/dr_libs/tests/testvectors/DO_NOT_DELETE.md b/source/engine/thirdparty/dr_libs/tests/testvectors/DO_NOT_DELETE.md
deleted file mode 100644
index e69de29..0000000
diff --git a/source/engine/thirdparty/dr_libs/tests/testvectors/flac/DO_NOT_DELETE.md b/source/engine/thirdparty/dr_libs/tests/testvectors/flac/DO_NOT_DELETE.md
deleted file mode 100644
index e69de29..0000000
diff --git a/source/engine/thirdparty/dr_libs/tests/testvectors/mp3/DO_NOT_DELETE.md b/source/engine/thirdparty/dr_libs/tests/testvectors/mp3/DO_NOT_DELETE.md
deleted file mode 100644
index e69de29..0000000
diff --git a/source/engine/thirdparty/dr_libs/tests/testvectors/opus/DO_NOT_DELETE.md b/source/engine/thirdparty/dr_libs/tests/testvectors/opus/DO_NOT_DELETE.md
deleted file mode 100644
index e69de29..0000000
diff --git a/source/engine/thirdparty/dr_libs/tests/testvectors/wav/DO_NOT_DELETE.md b/source/engine/thirdparty/dr_libs/tests/testvectors/wav/DO_NOT_DELETE.md
deleted file mode 100644
index e69de29..0000000
diff --git a/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_common.c b/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_common.c
deleted file mode 100644
index 165b4ad..0000000
--- a/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_common.c
+++ /dev/null
@@ -1,326 +0,0 @@
-#define DR_WAV_IMPLEMENTATION
-#include "../../dr_wav.h"
-#include
-#include "../common/dr_common.c"
-
-dr_handle g_libsndfile = NULL;
-
-typedef SNDFILE* (* pfn_sf_open_virtual)(SF_VIRTUAL_IO* sfvirtual, int mode, SF_INFO* sfinfo, void* user_data);
-typedef int (* pfn_sf_close) (SNDFILE* sndfile);
-typedef sf_count_t (* pfn_sf_readf_short) (SNDFILE *sndfile, short *ptr, sf_count_t frames);
-typedef sf_count_t (* pfn_sf_readf_int) (SNDFILE *sndfile, int *ptr, sf_count_t frames);
-typedef sf_count_t (* pfn_sf_readf_float) (SNDFILE *sndfile, float *ptr, sf_count_t frames);
-typedef sf_count_t (* pfn_sf_readf_double)(SNDFILE *sndfile, double *ptr, sf_count_t frames);
-typedef sf_count_t (* pfn_sf_seek) (SNDFILE *sndfile, sf_count_t frames, int whence);
-
-pfn_sf_open_virtual libsndfile__sf_open_virtual;
-pfn_sf_close libsndfile__sf_close;
-pfn_sf_readf_short libsndfile__sf_readf_short;
-pfn_sf_readf_int libsndfile__sf_readf_int;
-pfn_sf_readf_float libsndfile__sf_readf_float;
-pfn_sf_readf_double libsndfile__sf_readf_double;
-pfn_sf_seek libsndfile__sf_seek;
-
-drwav_result libsndfile_init_api()
-{
- unsigned int i;
- const char* pFileNames[] = {
-#if defined(_WIN32)
- #if defined(_WIN64)
- "libsndfile-1-x64.dll",
- #else
- "libsndfile-1-x86.dll",
- #endif
- "libsndfile-1.dll"
-#else
- "libsndfile-1.so",
- "libsndfile.so.1"
-#endif
- };
-
- if (g_libsndfile != NULL) {
- return DRWAV_INVALID_OPERATION; /* Already initialized. */
- }
-
- for (i = 0; i < sizeof(pFileNames)/sizeof(pFileNames[0]); i += 1) {
- g_libsndfile = dr_dlopen(pFileNames[i]);
- if (g_libsndfile != NULL) {
- break;
- }
- }
-
- if (g_libsndfile == NULL) {
- return DRWAV_ERROR; /* Unable to load libsndfile-1.so/dll. */
- }
-
- libsndfile__sf_open_virtual = (pfn_sf_open_virtual)dr_dlsym(g_libsndfile, "sf_open_virtual");
- libsndfile__sf_close = (pfn_sf_close) dr_dlsym(g_libsndfile, "sf_close");
- libsndfile__sf_readf_short = (pfn_sf_readf_short) dr_dlsym(g_libsndfile, "sf_readf_short");
- libsndfile__sf_readf_int = (pfn_sf_readf_int) dr_dlsym(g_libsndfile, "sf_readf_int");
- libsndfile__sf_readf_float = (pfn_sf_readf_float) dr_dlsym(g_libsndfile, "sf_readf_float");
- libsndfile__sf_readf_double = (pfn_sf_readf_double)dr_dlsym(g_libsndfile, "sf_readf_double");
- libsndfile__sf_seek = (pfn_sf_seek) dr_dlsym(g_libsndfile, "sf_seek");
-
- return DRWAV_SUCCESS;
-}
-
-void libsndfile_uninit_api()
-{
- if (g_libsndfile == NULL) {
- return; /* Invalid operation. Not initialized. */
- }
-
- dr_dlclose(g_libsndfile);
- g_libsndfile = NULL;
-}
-
-typedef struct
-{
- SNDFILE* pHandle;
- SF_INFO info;
- drwav_uint8* pFileData;
- size_t fileSizeInBytes;
- size_t fileReadPos;
-} libsndfile;
-
-sf_count_t libsndfile__on_filelen(void *user_data)
-{
- libsndfile* pSndFile = (libsndfile*)user_data;
- return (sf_count_t)pSndFile->fileSizeInBytes;
-}
-
-sf_count_t libsndfile__on_seek(sf_count_t offset, int whence, void *user_data)
-{
- libsndfile* pSndFile = (libsndfile*)user_data;
-
- switch (whence)
- {
- case SF_SEEK_SET:
- {
- pSndFile->fileReadPos = (size_t)offset;
- } break;
- case SF_SEEK_CUR:
- {
- pSndFile->fileReadPos += (size_t)offset;
- } break;
- case SF_SEEK_END:
- {
- pSndFile->fileReadPos = pSndFile->fileSizeInBytes - (size_t)offset;
- } break;
- }
-
- return (sf_count_t)pSndFile->fileReadPos;
-}
-
-sf_count_t libsndfile__on_read(void *ptr, sf_count_t count, void *user_data)
-{
- libsndfile* pSndFile = (libsndfile*)user_data;
-
- DRWAV_COPY_MEMORY(ptr, pSndFile->pFileData + pSndFile->fileReadPos, (size_t)count);
- pSndFile->fileReadPos += (size_t)count;
-
- return count;
-}
-
-sf_count_t libsndfile__on_write(const void *ptr, sf_count_t count, void *user_data)
-{
- /* We're not doing anything with writing. */
- (void)ptr;
- (void)count;
- (void)user_data;
- return 0;
-}
-
-sf_count_t libsndfile__on_tell(void *user_data)
-{
- libsndfile* pSndFile = (libsndfile*)user_data;
- return (sf_count_t)pSndFile->fileReadPos;
-}
-
-drwav_result libsndfile_init_file(const char* pFilePath, libsndfile* pSndFile)
-{
- SF_VIRTUAL_IO callbacks;
-
- if (pFilePath == NULL || pSndFile == NULL) {
- return DRWAV_INVALID_ARGS;
- }
-
- DRWAV_ZERO_MEMORY(pSndFile, sizeof(*pSndFile));
-
- /* We use libsndfile's virtual IO technique because we want to load from memory to make speed benchmarking fairer. */
- pSndFile->pFileData = (drwav_uint8*)dr_open_and_read_file(pFilePath, &pSndFile->fileSizeInBytes);
- if (pSndFile->pFileData == NULL) {
- return DRWAV_ERROR; /* Failed to open the file. */
- }
-
- DRWAV_ZERO_MEMORY(&callbacks, sizeof(callbacks));
- callbacks.get_filelen = libsndfile__on_filelen;
- callbacks.seek = libsndfile__on_seek;
- callbacks.read = libsndfile__on_read;
- callbacks.write = libsndfile__on_write;
- callbacks.tell = libsndfile__on_tell;
-
- pSndFile->pHandle = libsndfile__sf_open_virtual(&callbacks, SFM_READ, &pSndFile->info, pSndFile);
- if (pSndFile->pHandle == NULL) {
- free(pSndFile->pFileData);
- return DRWAV_ERROR;
- }
-
- return DRWAV_SUCCESS;
-}
-
-void libsndfile_uninit(libsndfile* pSndFile)
-{
- if (pSndFile == NULL) {
- return;
- }
-
- libsndfile__sf_close(pSndFile->pHandle);
- free(pSndFile->pFileData);
-}
-
-drwav_uint64 libsndfile_read_pcm_frames_s16(libsndfile* pSndFile, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
-{
- if (pSndFile == NULL || pBufferOut == NULL) {
- return 0;
- }
-
- /* Unfortunately it looks like libsndfile does not return correct integral values when the source file is floating point. */
- if ((pSndFile->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT) {
- /* Read as float and convert. */
- drwav_uint64 totalFramesRead = 0;
- while (totalFramesRead < framesToRead) {
- float temp[4096];
- drwav_uint64 framesRemaining = framesToRead - totalFramesRead;
- drwav_uint64 framesReadThisIteration;
- drwav_uint64 framesToReadThisIteration = sizeof(temp)/sizeof(temp[0]) / pSndFile->info.channels;
- if (framesToReadThisIteration > framesRemaining) {
- framesToReadThisIteration = framesRemaining;
- }
-
- framesReadThisIteration = libsndfile__sf_readf_float(pSndFile->pHandle, temp, (sf_count_t)framesToReadThisIteration);
-
- drwav_f32_to_s16(pBufferOut, temp, (size_t)(framesReadThisIteration*pSndFile->info.channels));
-
- totalFramesRead += framesReadThisIteration;
- pBufferOut += framesReadThisIteration*pSndFile->info.channels;
-
- /* If we read less frames than we requested we've reached the end of the file. */
- if (framesReadThisIteration < framesToReadThisIteration) {
- break;
- }
- }
-
- return totalFramesRead;
- } else if ((pSndFile->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_DOUBLE) {
- /* Read as double and convert. */
- drwav_uint64 totalFramesRead = 0;
- while (totalFramesRead < framesToRead) {
- double temp[4096];
- drwav_uint64 framesRemaining = framesToRead - totalFramesRead;
- drwav_uint64 framesReadThisIteration;
- drwav_uint64 framesToReadThisIteration = sizeof(temp)/sizeof(temp[0]) / pSndFile->info.channels;
- if (framesToReadThisIteration > framesRemaining) {
- framesToReadThisIteration = framesRemaining;
- }
-
- framesReadThisIteration = libsndfile__sf_readf_double(pSndFile->pHandle, temp, (sf_count_t)framesToReadThisIteration);
-
- drwav_f64_to_s16(pBufferOut, temp, (size_t)(framesReadThisIteration*pSndFile->info.channels));
-
- totalFramesRead += framesReadThisIteration;
- pBufferOut += framesReadThisIteration*pSndFile->info.channels;
-
- /* If we read less frames than we requested we've reached the end of the file. */
- if (framesReadThisIteration < framesToReadThisIteration) {
- break;
- }
- }
-
- return totalFramesRead;
- } else {
- return libsndfile__sf_readf_short(pSndFile->pHandle, pBufferOut, framesToRead);
- }
-}
-
-drwav_uint64 libsndfile_read_pcm_frames_f32(libsndfile* pSndFile, drwav_uint64 framesToRead, float* pBufferOut)
-{
- if (pSndFile == NULL || pBufferOut == NULL) {
- return 0;
- }
-
- return libsndfile__sf_readf_float(pSndFile->pHandle, pBufferOut, framesToRead);
-}
-
-drwav_uint64 libsndfile_read_pcm_frames_s32(libsndfile* pSndFile, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
-{
- if (pSndFile == NULL || pBufferOut == NULL) {
- return 0;
- }
-
- /* Unfortunately it looks like libsndfile does not return correct integral values when the source file is floating point. */
- if ((pSndFile->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT) {
- /* Read floats and convert. */
- drwav_uint64 totalFramesRead = 0;
- while (totalFramesRead < framesToRead) {
- float temp[4096];
- drwav_uint64 framesRemaining = framesToRead - totalFramesRead;
- drwav_uint64 framesReadThisIteration;
- drwav_uint64 framesToReadThisIteration = sizeof(temp)/sizeof(temp[0]) / pSndFile->info.channels;
- if (framesToReadThisIteration > framesRemaining) {
- framesToReadThisIteration = framesRemaining;
- }
-
- framesReadThisIteration = libsndfile__sf_readf_float(pSndFile->pHandle, temp, (sf_count_t)framesToReadThisIteration);
-
- drwav_f32_to_s32(pBufferOut, temp, (size_t)(framesReadThisIteration*pSndFile->info.channels));
-
- totalFramesRead += framesReadThisIteration;
- pBufferOut += framesReadThisIteration*pSndFile->info.channels;
-
- /* If we read less frames than we requested we've reached the end of the file. */
- if (framesReadThisIteration < framesToReadThisIteration) {
- break;
- }
- }
-
- return totalFramesRead;
- } else if ((pSndFile->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_DOUBLE) {
- /* Read doubles and convert. */
- drwav_uint64 totalFramesRead = 0;
- while (totalFramesRead < framesToRead) {
- double temp[4096];
- drwav_uint64 framesRemaining = framesToRead - totalFramesRead;
- drwav_uint64 framesReadThisIteration;
- drwav_uint64 framesToReadThisIteration = sizeof(temp)/sizeof(temp[0]) / pSndFile->info.channels;
- if (framesToReadThisIteration > framesRemaining) {
- framesToReadThisIteration = framesRemaining;
- }
-
- framesReadThisIteration = libsndfile__sf_readf_double(pSndFile->pHandle, temp, (sf_count_t)framesToReadThisIteration);
-
- drwav_f64_to_s32(pBufferOut, temp, (size_t)(framesReadThisIteration*pSndFile->info.channels));
-
- totalFramesRead += framesReadThisIteration;
- pBufferOut += framesReadThisIteration*pSndFile->info.channels;
-
- /* If we read less frames than we requested we've reached the end of the file. */
- if (framesReadThisIteration < framesToReadThisIteration) {
- break;
- }
- }
-
- return totalFramesRead;
- } else {
- return libsndfile__sf_readf_int(pSndFile->pHandle, pBufferOut, framesToRead);
- }
-}
-
-drwav_bool32 libsndfile_seek_to_pcm_frame(libsndfile* pSndFile, drwav_uint64 targetPCMFrameIndex)
-{
- if (pSndFile == NULL) {
- return DRWAV_FALSE;
- }
-
- return libsndfile__sf_seek(pSndFile->pHandle, (sf_count_t)targetPCMFrameIndex, SF_SEEK_SET) == (sf_count_t)targetPCMFrameIndex;
-}
diff --git a/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_decoding.c b/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_decoding.c
deleted file mode 100644
index bf9decb..0000000
--- a/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_decoding.c
+++ /dev/null
@@ -1,446 +0,0 @@
-#define DR_WAV_LIBSNDFILE_COMPAT
-#include "dr_wav_common.c"
-
-#define FILE_NAME_WIDTH 40
-#define NUMBER_WIDTH 10
-#define TABLE_MARGIN 2
-
-#define DEFAULT_SOURCE_DIR "testvectors/wav/tests"
-
-
-drwav_result decode_test__read_and_compare_pcm_frames_s32(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameCount, drwav_int32* pPCMFrames_libsndfile, drwav_int32* pPCMFrames_drwav)
-{
- drwav_uint64 pcmFrameCount_libsndfile;
- drwav_uint64 pcmFrameCount_drwav;
- drwav_uint64 iPCMFrame;
-
- /* To test decoding we just read a number of PCM frames from each decoder and compare. */
- pcmFrameCount_libsndfile = libsndfile_read_pcm_frames_s32(pSndFile, pcmFrameCount, pPCMFrames_libsndfile);
- pcmFrameCount_drwav = drwav_read_pcm_frames_s32(pWav, pcmFrameCount, pPCMFrames_drwav);
-
- /* The total number of frames we decoded need to match. */
- if (pcmFrameCount_libsndfile != pcmFrameCount_drwav) {
- printf(" Decoded frame counts differ: pcmFrameCount=%d, libsndfile=%d, dr_wav=%d", (int)pcmFrameCount, (int)pcmFrameCount_libsndfile, (int)pcmFrameCount_drwav);
- return DRWAV_ERROR;
- }
-
- /* Each of the decoded PCM frames need to match. */
- DRWAV_ASSERT(pcmFrameCount_libsndfile == pcmFrameCount_drwav);
-
- for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libsndfile; iPCMFrame += 1) {
- drwav_int32* pPCMFrame_libsndfile = pPCMFrames_libsndfile + (iPCMFrame * pWav->channels);
- drwav_int32* pPCMFrame_drwav = pPCMFrames_drwav + (iPCMFrame * pWav->channels);
- drwav_uint32 iChannel;
- drwav_bool32 hasError = DRWAV_FALSE;
-
- for (iChannel = 0; iChannel < pWav->channels; iChannel += 1) {
- if (pPCMFrame_libsndfile[iChannel] != pPCMFrame_drwav[iChannel]) {
- printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
- hasError = DRWAV_TRUE;
- break;
- }
- }
-
- if (hasError) {
- return DRWAV_ERROR; /* Decoded frames do not match. */
- }
- }
-
- /* Done. */
- return DRWAV_SUCCESS;
-}
-
-drwav_result decode_test__read_and_compare_pcm_frame_chunks_s32(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameChunkSize)
-{
- drwav_result result = DRWAV_SUCCESS;
- drwav_uint64 iPCMFrame;
- drwav_int32* pPCMFrames_libsndfile;
- drwav_int32* pPCMFrames_drwav;
-
- /* Make sure the decoder's are seeked back to the start first. */
- drwav_seek_to_pcm_frame(pWav, 0);
- libsndfile_seek_to_pcm_frame(pSndFile, 0);
-
- pPCMFrames_libsndfile = (drwav_int32*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(drwav_int32)));
- if (pPCMFrames_libsndfile == NULL) {
- printf(" [libsndfile] Out of memory");
- return DRWAV_ERROR;
- }
-
- pPCMFrames_drwav = (drwav_int32*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(drwav_int32)));
- if (pPCMFrames_drwav == NULL) {
- free(pPCMFrames_libsndfile);
- printf(" [dr_wav] Out of memory");
- return DRWAV_ERROR;
- }
-
- for (iPCMFrame = 0; iPCMFrame < pWav->totalPCMFrameCount; iPCMFrame += pcmFrameChunkSize) {
- result = decode_test__read_and_compare_pcm_frames_s32(pSndFile, pWav, pcmFrameChunkSize, pPCMFrames_libsndfile, pPCMFrames_drwav);
- if (result != DRWAV_SUCCESS) {
- break;
- }
- }
-
- free(pPCMFrames_libsndfile);
- free(pPCMFrames_drwav);
-
- return result;
-}
-
-
-drwav_result decode_test__read_and_compare_pcm_frames_f32(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameCount, float* pPCMFrames_libsndfile, float* pPCMFrames_drwav)
-{
- drwav_uint64 pcmFrameCount_libsndfile;
- drwav_uint64 pcmFrameCount_drwav;
- drwav_uint64 iPCMFrame;
-
- /* To test decoding we just read a number of PCM frames from each decoder and compare. */
- pcmFrameCount_libsndfile = libsndfile_read_pcm_frames_f32(pSndFile, pcmFrameCount, pPCMFrames_libsndfile);
- pcmFrameCount_drwav = drwav_read_pcm_frames_f32(pWav, pcmFrameCount, pPCMFrames_drwav);
-
- /* The total number of frames we decoded need to match. */
- if (pcmFrameCount_libsndfile != pcmFrameCount_drwav) {
- printf(" Decoded frame counts differ: pcmFrameCount=%d, libsndfile=%d, dr_wav=%d", (int)pcmFrameCount, (int)pcmFrameCount_libsndfile, (int)pcmFrameCount_drwav);
- return DRWAV_ERROR;
- }
-
- /* Each of the decoded PCM frames need to match. */
- DRWAV_ASSERT(pcmFrameCount_libsndfile == pcmFrameCount_drwav);
-
- for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libsndfile; iPCMFrame += 1) {
- float* pPCMFrame_libsndfile = pPCMFrames_libsndfile + (iPCMFrame * pWav->channels);
- float* pPCMFrame_drwav = pPCMFrames_drwav + (iPCMFrame * pWav->channels);
- drwav_uint32 iChannel;
- drwav_bool32 hasError = DRWAV_FALSE;
-
- for (iChannel = 0; iChannel < pWav->channels; iChannel += 1) {
- if (pPCMFrame_libsndfile[iChannel] != pPCMFrame_drwav[iChannel]) {
- printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
- hasError = DRWAV_TRUE;
- break;
- }
- }
-
- if (hasError) {
- return DRWAV_ERROR; /* Decoded frames do not match. */
- }
- }
-
- /* Done. */
- return DRWAV_SUCCESS;
-}
-
-drwav_result decode_test__read_and_compare_pcm_frame_chunks_f32(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameChunkSize)
-{
- drwav_result result = DRWAV_SUCCESS;
- drwav_uint64 iPCMFrame;
- float* pPCMFrames_libsndfile;
- float* pPCMFrames_drwav;
-
- /* Make sure the decoder's are seeked back to the start first. */
- drwav_seek_to_pcm_frame(pWav, 0);
- libsndfile_seek_to_pcm_frame(pSndFile, 0);
-
- pPCMFrames_libsndfile = (float*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(float)));
- if (pPCMFrames_libsndfile == NULL) {
- printf(" [libsndfile] Out of memory");
- return DRWAV_ERROR;
- }
-
- pPCMFrames_drwav = (float*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(float)));
- if (pPCMFrames_drwav == NULL) {
- free(pPCMFrames_libsndfile);
- printf(" [dr_wav] Out of memory");
- return DRWAV_ERROR;
- }
-
- for (iPCMFrame = 0; iPCMFrame < pWav->totalPCMFrameCount; iPCMFrame += pcmFrameChunkSize) {
- result = decode_test__read_and_compare_pcm_frames_f32(pSndFile, pWav, pcmFrameChunkSize, pPCMFrames_libsndfile, pPCMFrames_drwav);
- if (result != DRWAV_SUCCESS) {
- break;
- }
- }
-
- free(pPCMFrames_libsndfile);
- free(pPCMFrames_drwav);
-
- return result;
-}
-
-
-drwav_result decode_test__read_and_compare_pcm_frames_s16(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameCount, drwav_int16* pPCMFrames_libsndfile, drwav_int16* pPCMFrames_drwav)
-{
- drwav_uint64 pcmFrameCount_libsndfile;
- drwav_uint64 pcmFrameCount_drwav;
- drwav_uint64 iPCMFrame;
-
- /* To test decoding we just read a number of PCM frames from each decoder and compare. */
- pcmFrameCount_libsndfile = libsndfile_read_pcm_frames_s16(pSndFile, pcmFrameCount, pPCMFrames_libsndfile);
- pcmFrameCount_drwav = drwav_read_pcm_frames_s16(pWav, pcmFrameCount, pPCMFrames_drwav);
-
- /* The total number of frames we decoded need to match. */
- if (pcmFrameCount_libsndfile != pcmFrameCount_drwav) {
- printf(" Decoded frame counts differ: pcmFrameCount=%d, libsndfile=%d, dr_wav=%d", (int)pcmFrameCount, (int)pcmFrameCount_libsndfile, (int)pcmFrameCount_drwav);
- return DRWAV_ERROR;
- }
-
- /* Each of the decoded PCM frames need to match. */
- DRWAV_ASSERT(pcmFrameCount_libsndfile == pcmFrameCount_drwav);
-
- for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libsndfile; iPCMFrame += 1) {
- drwav_int16* pPCMFrame_libsndfile = pPCMFrames_libsndfile + (iPCMFrame * pWav->channels);
- drwav_int16* pPCMFrame_drwav = pPCMFrames_drwav + (iPCMFrame * pWav->channels);
- drwav_uint32 iChannel;
- drwav_bool32 hasError = DRWAV_FALSE;
-
- for (iChannel = 0; iChannel < pWav->channels; iChannel += 1) {
- if (pPCMFrame_libsndfile[iChannel] != pPCMFrame_drwav[iChannel]) {
- printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
- hasError = DRWAV_TRUE;
- break;
- }
- }
-
- if (hasError) {
- return DRWAV_ERROR; /* Decoded frames do not match. */
- }
- }
-
- /* Done. */
- return DRWAV_SUCCESS;
-}
-
-drwav_result decode_test__read_and_compare_pcm_frame_chunks_s16(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameChunkSize)
-{
- drwav_result result = DRWAV_SUCCESS;
- drwav_uint64 iPCMFrame;
- drwav_int16* pPCMFrames_libsndfile;
- drwav_int16* pPCMFrames_drwav;
-
- /* Make sure the decoder's are seeked back to the start first. */
- drwav_seek_to_pcm_frame(pWav, 0);
- libsndfile_seek_to_pcm_frame(pSndFile, 0);
-
- pPCMFrames_libsndfile = (drwav_int16*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(drwav_int16)));
- if (pPCMFrames_libsndfile == NULL) {
- printf(" [libsndfile] Out of memory");
- return DRWAV_ERROR;
- }
-
- pPCMFrames_drwav = (drwav_int16*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(drwav_int16)));
- if (pPCMFrames_drwav == NULL) {
- free(pPCMFrames_libsndfile);
- printf(" [dr_wav] Out of memory");
- return DRWAV_ERROR;
- }
-
- for (iPCMFrame = 0; iPCMFrame < pWav->totalPCMFrameCount; iPCMFrame += pcmFrameChunkSize) {
- result = decode_test__read_and_compare_pcm_frames_s16(pSndFile, pWav, pcmFrameChunkSize, pPCMFrames_libsndfile, pPCMFrames_drwav);
- if (result != DRWAV_SUCCESS) {
- break;
- }
- }
-
- free(pPCMFrames_libsndfile);
- free(pPCMFrames_drwav);
-
- return result;
-}
-
-
-drwav_result decode_test_file_s32(libsndfile* pSndFile, drwav* pWav)
-{
- drwav_result result = DRWAV_SUCCESS;
-
- /* Start with reading the entire file in one go. */
- if (result == DRWAV_SUCCESS) {
- result = decode_test__read_and_compare_pcm_frame_chunks_s32(pSndFile, pWav, pWav->totalPCMFrameCount);
- }
-
- /* Now try with reading one PCM frame at a time.*/
- if (result == DRWAV_SUCCESS) {
- result = decode_test__read_and_compare_pcm_frame_chunks_s32(pSndFile, pWav, 1);
- }
-
- return result;
-}
-
-drwav_result decode_test_file_f32(libsndfile* pSndFile, drwav* pWav)
-{
- drwav_result result = DRWAV_SUCCESS;
-
- /* Start with reading the entire file in one go. */
- if (result == DRWAV_SUCCESS) {
- result = decode_test__read_and_compare_pcm_frame_chunks_f32(pSndFile, pWav, pWav->totalPCMFrameCount);
- }
-
- /* Now try with reading one PCM frame at a time.*/
- if (result == DRWAV_SUCCESS) {
- result = decode_test__read_and_compare_pcm_frame_chunks_f32(pSndFile, pWav, 1);
- }
-
- return result;
-}
-
-drwav_result decode_test_file_s16(libsndfile* pSndFile, drwav* pWav)
-{
- drwav_result result = DRWAV_SUCCESS;
-
- /* Start with reading the entire file in one go. */
- if (result == DRWAV_SUCCESS) {
- result = decode_test__read_and_compare_pcm_frame_chunks_s16(pSndFile, pWav, pWav->totalPCMFrameCount);
- }
-
- /* Now try with reading one PCM frame at a time.*/
- if (result == DRWAV_SUCCESS) {
- result = decode_test__read_and_compare_pcm_frame_chunks_s16(pSndFile, pWav, 1);
- }
-
- return result;
-}
-
-drwav_result decode_test_file(const char* pFilePath)
-{
- /* To test seeking we just seek to our target PCM frame and then decode whatever is remaining and compare it against libsndfile. */
- drwav_result result;
- libsndfile libsndfile;
- drwav wav;
-
- dr_printf_fixed_with_margin(FILE_NAME_WIDTH, TABLE_MARGIN, "%s", dr_path_file_name(pFilePath));
-
- /* First load the decoder from libsndfile. */
- result = libsndfile_init_file(pFilePath, &libsndfile);
- if (result != DRWAV_SUCCESS) {
- printf(" Failed to open via libsndfile.");
- return result;
- }
-
- /* Now load from dr_wav. */
- if (!drwav_init_file_with_metadata(&wav, pFilePath, 0, NULL)) {
- printf(" Failed to open via dr_wav.");
- libsndfile_uninit(&libsndfile);
- return DRWAV_ERROR; /* Failed to load dr_wav decoder. */
- }
-
- /* At this point we should have both libsndfile and dr_wav decoders open. We can now perform identical operations on each of them and compare. */
- result = decode_test_file_s32(&libsndfile, &wav);
- if (result != DRWAV_SUCCESS) {
- drwav_uninit(&wav);
- libsndfile_uninit(&libsndfile);
- return result;
- }
-
- result = decode_test_file_f32(&libsndfile, &wav);
- if (result != DRWAV_SUCCESS) {
- drwav_uninit(&wav);
- libsndfile_uninit(&libsndfile);
- return result;
- }
-
- result = decode_test_file_s16(&libsndfile, &wav);
- if (result != DRWAV_SUCCESS) {
- drwav_uninit(&wav);
- libsndfile_uninit(&libsndfile);
- return result;
- }
-
-
- /* We're done with our decoders. */
- drwav_uninit(&wav);
- libsndfile_uninit(&libsndfile);
-
- if (result == DRWAV_SUCCESS) {
- printf(" Passed");
- }
-
- return result;
-}
-
-drwav_result decode_test_directory(const char* pDirectoryPath)
-{
- dr_file_iterator iteratorState;
- dr_file_iterator* pFile;
-
- dr_printf_fixed(FILE_NAME_WIDTH, "%s", pDirectoryPath);
- dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "RESULT");
- printf("\n");
-
- pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
- while (pFile != NULL) {
- drwav_result result;
-
- /* Skip directories for now, but we may want to look at doing recursive file iteration. */
- if (!pFile->isDirectory) {
- result = decode_test_file(pFile->absolutePath);
- (void)result;
-
- printf("\n");
- }
-
- pFile = dr_file_iterator_next(pFile);
- }
-
- return DRWAV_SUCCESS;
-}
-
-drwav_result decode_test()
-{
- drwav_result result = DRWAV_SUCCESS;
-
- /* Directories. */
- {
- result = decode_test_directory(DEFAULT_SOURCE_DIR);
- (void)result;
- }
-
- return result;
-}
-
-drwav_result decode_profiling()
-{
- return DRWAV_SUCCESS;
-}
-
-int main(int argc, char** argv)
-{
- drwav_result result = DRWAV_SUCCESS;
- drwav_bool32 doTesting = DRWAV_TRUE;
- drwav_bool32 doProfiling = DRWAV_TRUE;
-
- if (dr_argv_is_set(argc, argv, "--onlyprofile")) {
- doTesting = DRWAV_FALSE;
- }
-
- if (libsndfile_init_api() != DRWAV_SUCCESS) {
- printf("Failed to initialize libsndfile API.");
- return -1;
- }
-
- if (doTesting) {
- printf("=======================================================================\n");
- printf("DECODE TESTING\n");
- printf("=======================================================================\n");
- result = decode_test();
- if (result != DRWAV_SUCCESS) {
- goto done; /* Don't continue if an error occurs during testing. */
- }
- printf("\n");
- } else {
- printf("=======================================================================\n");
- printf("WARNING: Correctness Tests Disabled\n");
- printf("=======================================================================\n");
- }
-
- /* Profiling. */
- if (doProfiling) {
- printf("=======================================================================\n");
- printf("DECODE PROFILING (LOWER IS BETTER)\n");
- printf("=======================================================================\n");
- result = decode_profiling();
- printf("\n");
- }
-
-done:
- libsndfile_uninit_api();
- return (int)result;
-}
diff --git a/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_decoding.cpp b/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_decoding.cpp
deleted file mode 100644
index a66ae71..0000000
--- a/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_decoding.cpp
+++ /dev/null
@@ -1 +0,0 @@
-#include "dr_wav_decoding.c"
diff --git a/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_encoding.c b/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_encoding.c
deleted file mode 100644
index 8506632..0000000
--- a/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_encoding.c
+++ /dev/null
@@ -1,68 +0,0 @@
-#define DR_WAV_IMPLEMENTATION
-#include "../../dr_wav.h"
-#include
-
-void generate_sine_wave(float* pOutput, drwav_uint64 frameCount, drwav_uint32 channels, drwav_uint32 sampleRate, float frequency, float* t)
-{
- float x = *t;
- float a = 1.0f / sampleRate;
- drwav_uint64 iFrame;
- drwav_uint32 iChannel;
-
- for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
- float s = (float)(sin(3.1415965*2 * x * frequency) * 0.25);
- for (iChannel = 0; iChannel < channels; iChannel += 1) {
- pOutput[iFrame*channels + iChannel] = s;
- }
-
- x += a;
- }
-
- *t = x;
-}
-
-int main(int argc, char** argv)
-{
- drwav_data_format format;
- drwav wav;
- float t = 0;
- float tempFrames[4096];
- drwav_uint64 totalFramesToWrite;
- drwav_uint64 totalFramesWritten = 0;
-
-
- if (argc < 2) {
- printf("No output file specified.\n");
- return -1;
- }
-
- format.container = drwav_container_riff;
- format.format = DR_WAVE_FORMAT_IEEE_FLOAT;
- format.channels = 2;
- format.sampleRate = 44100;
- format.bitsPerSample = 32;
- if (!drwav_init_file_write(&wav, argv[1], &format, NULL)) {
- printf("Failed to open file.\n");
- return -1;
- }
-
- totalFramesToWrite = format.sampleRate * 1;
- totalFramesWritten = 0;
-
- while (totalFramesToWrite > totalFramesWritten) {
- drwav_uint64 framesRemaining = totalFramesToWrite - totalFramesWritten;
- drwav_uint64 framesToWriteNow = drwav_countof(tempFrames) / format.channels;
- if (framesToWriteNow > framesRemaining) {
- framesToWriteNow = framesRemaining;
- }
-
- generate_sine_wave(tempFrames, framesToWriteNow, format.channels, format.sampleRate, 400, &t);
- drwav_write_pcm_frames(&wav, framesToWriteNow, tempFrames);
-
- totalFramesWritten += framesToWriteNow;
- }
-
- drwav_uninit(&wav);
-
- return 0;
-}
\ No newline at end of file
diff --git a/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_playback.c b/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_playback.c
deleted file mode 100644
index 4410b85..0000000
--- a/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_playback.c
+++ /dev/null
@@ -1,70 +0,0 @@
-#define MA_NO_DECODING
-#define MA_NO_ENCODING
-#define MA_IMPLEMENTATION
-#include "../../../miniaudio/miniaudio.h"
-
-#define DR_WAV_IMPLEMENTATION
-#include "../../dr_wav.h"
-#include "../common/dr_common.c"
-
-drwav g_wav;
-
-void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
-{
- /* Assuming format is always s16 for now. */
- if (pDevice->playback.format == ma_format_s16) {
- drwav_read_pcm_frames_s16(&g_wav, frameCount, (drwav_int16*)pOutput);
- } else if (pDevice->playback.format == ma_format_f32) {
- drwav_read_pcm_frames_f32(&g_wav, frameCount, (float*)pOutput);
- } else {
- /* Unsupported format. */
- }
-
- (void)pInput;
-}
-
-int main(int argc, char** argv)
-{
- ma_result result;
- ma_device device;
- ma_device_config deviceConfig;
-
- if (argc < 2) {
- printf("No input file specified.");
- return -1;
- }
-
- if (!drwav_init_file_with_metadata(&g_wav, argv[1], 0, NULL)) {
- printf("Failed to load file: %s", argv[1]);
- return -1;
- }
-
- deviceConfig = ma_device_config_init(ma_device_type_playback);
- deviceConfig.playback.format = ma_format_s16;
- deviceConfig.playback.channels = g_wav.channels;
- deviceConfig.sampleRate = g_wav.sampleRate;
- deviceConfig.dataCallback = data_callback;
-
- result = ma_device_init(NULL, &deviceConfig, &device);
- if (result != MA_SUCCESS) {
- printf("Failed to initialize playback device.");
- drwav_uninit(&g_wav);
- return -1;
- }
-
- result = ma_device_start(&device);
- if (result != MA_SUCCESS) {
- printf("Failed to start playback device.");
- ma_device_uninit(&device);
- drwav_uninit(&g_wav);
- return -1;
- }
-
- printf("Press Enter to quit...");
- getchar();
-
- ma_device_uninit(&device);
- drwav_uninit(&g_wav);
-
- return 0;
-}
\ No newline at end of file
diff --git a/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_playback.cpp b/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_playback.cpp
deleted file mode 100644
index 0127e1a..0000000
--- a/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_playback.cpp
+++ /dev/null
@@ -1 +0,0 @@
-#include "dr_wav_playback.c"
diff --git a/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_test_0.c b/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_test_0.c
deleted file mode 100644
index 7f6a88b..0000000
--- a/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_test_0.c
+++ /dev/null
@@ -1,11 +0,0 @@
-#define DR_WAV_IMPLEMENTATION
-#include "../../dr_wav.h"
-
-#include "../common/dr_common.c"
-
-int main(int argc, char** argv)
-{
- (void)argc;
- (void)argv;
- return 0;
-}
diff --git a/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_test_0.cpp b/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_test_0.cpp
deleted file mode 100644
index f446b65..0000000
--- a/source/engine/thirdparty/dr_libs/tests/wav/dr_wav_test_0.cpp
+++ /dev/null
@@ -1 +0,0 @@
-#include "dr_wav_test_0.c"
diff --git a/source/engine/thirdparty/dr_libs/wip/dr_opus.h b/source/engine/thirdparty/dr_libs/wip/dr_opus.h
deleted file mode 100644
index f399886..0000000
--- a/source/engine/thirdparty/dr_libs/wip/dr_opus.h
+++ /dev/null
@@ -1,2565 +0,0 @@
-/*
-Opus audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
-dr_opus - v0.0.0 (Unreleased) - xxxx-xx-xx
-
-David Reid - mackron@gmail.com
-*/
-
-/* ====== WORK-IN-PROGRESSS ====== */
-
-#ifndef dr_opus_h
-#define dr_opus_h
-
-#include /* For size_t. */
-
-/* Sized types. Prefer built-in types. Fall back to stdint. */
-#ifdef _MSC_VER
- #if defined(__clang__)
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wlanguage-extension-token"
- #pragma GCC diagnostic ignored "-Wlong-long"
- #pragma GCC diagnostic ignored "-Wc++11-long-long"
- #endif
- typedef signed __int8 dropus_int8;
- typedef unsigned __int8 dropus_uint8;
- typedef signed __int16 dropus_int16;
- typedef unsigned __int16 dropus_uint16;
- typedef signed __int32 dropus_int32;
- typedef unsigned __int32 dropus_uint32;
- typedef signed __int64 dropus_int64;
- typedef unsigned __int64 dropus_uint64;
- #if defined(__clang__)
- #pragma GCC diagnostic pop
- #endif
-
- #if defined(_WIN32)
- #if defined(_WIN64)
- typedef dropus_uint64 dropus_uintptr;
- #else
- typedef dropus_uint32 dropus_uintptr;
- #endif
- #elif defined(__GNUC__)
- #if defined(__LP64__)
- typedef dropus_uint64 dropus_uintptr;
- #else
- typedef dropus_uint32 dropus_uintptr;
- #endif
- #else
- typedef dropus_uint64 dropus_uintptr; /* Fallback. */
- #endif
-#else
- #include
- typedef int8_t dropus_int8;
- typedef uint8_t dropus_uint8;
- typedef int16_t dropus_int16;
- typedef uint16_t dropus_uint16;
- typedef int32_t dropus_int32;
- typedef uint32_t dropus_uint32;
- typedef int64_t dropus_int64;
- typedef uint64_t dropus_uint64;
- typedef uintptr_t dropus_uintptr;
-#endif
-typedef dropus_uint8 dropus_bool8;
-typedef dropus_uint32 dropus_bool32;
-#define DROPUS_TRUE 1
-#define DROPUS_FALSE 0
-
-typedef void* dropus_handle;
-typedef void* dropus_ptr;
-typedef void (* dropus_proc)(void);
-
-#ifndef NULL
-#define NULL 0
-#endif
-
-#if defined(SIZE_MAX)
- #define DROPUS_SIZE_MAX SIZE_MAX
-#else
- #define DROPUS_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */
-#endif
-
-#ifdef _MSC_VER
- #define DROPUS_INLINE __forceinline
-#elif defined(__GNUC__)
- /*
- I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
- the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
- case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
- command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
- I am using "__inline__" only when we're compiling in strict ANSI mode.
- */
- #if defined(__STRICT_ANSI__)
- #define DROPUS_INLINE __inline__ __attribute__((always_inline))
- #else
- #define DROPUS_INLINE inline __attribute__((always_inline))
- #endif
-#else
- #define DROPUS_INLINE
-#endif
-
-#if !defined(DROPUS_API)
- #if defined(DROPUS_DLL)
- #if defined(_WIN32)
- #define DROPUS_DLL_IMPORT __declspec(dllimport)
- #define DROPUS_DLL_EXPORT __declspec(dllexport)
- #define DROPUS_DLL_PRIVATE static
- #else
- #if defined(__GNUC__) && __GNUC__ >= 4
- #define DROPUS_DLL_IMPORT __attribute__((visibility("default")))
- #define DROPUS_DLL_EXPORT __attribute__((visibility("default")))
- #define DROPUS_DLL_PRIVATE __attribute__((visibility("hidden")))
- #else
- #define DROPUS_DLL_IMPORT
- #define DROPUS_DLL_EXPORT
- #define DROPUS_DLL_PRIVATE static
- #endif
- #endif
-
- #if defined(DR_OPUS_IMPLEMENTATION) || defined(DROPUS_IMPLEMENTATION)
- #define DROPUS_API DROPUS_DLL_EXPORT
- #else
- #define DROPUS_API DROPUS_DLL_IMPORT
- #endif
- #define DROPUS_PRIVATE DROPUS_DLL_PRIVATE
- #else
- #define DROPUS_API extern
- #define DROPUS_PRIVATE static
- #endif
-#endif
-
-typedef int dropus_result;
-#define DROPUS_SUCCESS 0
-#define DROPUS_ERROR -1 /* A generic error. */
-#define DROPUS_INVALID_ARGS -2
-#define DROPUS_INVALID_OPERATION -3
-#define DROPUS_OUT_OF_MEMORY -4
-#define DROPUS_OUT_OF_RANGE -5
-#define DROPUS_ACCESS_DENIED -6
-#define DROPUS_DOES_NOT_EXIST -7
-#define DROPUS_ALREADY_EXISTS -8
-#define DROPUS_TOO_MANY_OPEN_FILES -9
-#define DROPUS_INVALID_FILE -10
-#define DROPUS_TOO_BIG -11
-#define DROPUS_PATH_TOO_LONG -12
-#define DROPUS_NAME_TOO_LONG -13
-#define DROPUS_NOT_DIRECTORY -14
-#define DROPUS_IS_DIRECTORY -15
-#define DROPUS_DIRECTORY_NOT_EMPTY -16
-#define DROPUS_END_OF_FILE -17
-#define DROPUS_NO_SPACE -18
-#define DROPUS_BUSY -19
-#define DROPUS_IO_ERROR -20
-#define DROPUS_INTERRUPT -21
-#define DROPUS_UNAVAILABLE -22
-#define DROPUS_ALREADY_IN_USE -23
-#define DROPUS_BAD_ADDRESS -24
-#define DROPUS_BAD_SEEK -25
-#define DROPUS_BAD_PIPE -26
-#define DROPUS_DEADLOCK -27
-#define DROPUS_TOO_MANY_LINKS -28
-#define DROPUS_NOT_IMPLEMENTED -29
-#define DROPUS_NO_MESSAGE -30
-#define DROPUS_BAD_MESSAGE -31
-#define DROPUS_NO_DATA_AVAILABLE -32
-#define DROPUS_INVALID_DATA -33
-#define DROPUS_TIMEOUT -34
-#define DROPUS_NO_NETWORK -35
-#define DROPUS_NOT_UNIQUE -36
-#define DROPUS_NOT_SOCKET -37
-#define DROPUS_NO_ADDRESS -38
-#define DROPUS_BAD_PROTOCOL -39
-#define DROPUS_PROTOCOL_UNAVAILABLE -40
-#define DROPUS_PROTOCOL_NOT_SUPPORTED -41
-#define DROPUS_PROTOCOL_FAMILY_NOT_SUPPORTED -42
-#define DROPUS_ADDRESS_FAMILY_NOT_SUPPORTED -43
-#define DROPUS_SOCKET_NOT_SUPPORTED -44
-#define DROPUS_CONNECTION_RESET -45
-#define DROPUS_ALREADY_CONNECTED -46
-#define DROPUS_NOT_CONNECTED -47
-#define DROPUS_CONNECTION_REFUSED -48
-#define DROPUS_NO_HOST -49
-#define DROPUS_IN_PROGRESS -50
-#define DROPUS_CANCELLED -51
-#define DROPUS_MEMORY_ALREADY_MAPPED -52
-#define DROPUS_AT_END -53
-#define DROPUS_CRC_MISMATCH -100
-#define DROPUS_BAD_DATA -101
-
-/***********************************************************************************************************************************************************
-
-Low-Level Opus Stream API
-
-This API is where the low-level decoding takes place. In order to use this, you must know the packet structure of the Opus stream. This is usually the job of
-encapsulations such as Ogg and Matroska.
-
-************************************************************************************************************************************************************/
-#define DROPUS_MAX_OPUS_FRAMES_PER_PACKET 48 /* RFC 6716 - Section 3.2.5 */
-#define DROPUS_MAX_PCM_FRAMES_PER_PACKET 6144 /* RFC 6716 - Section 3.2.5. Maximum of 120ms. Maximum rate is 48kHz. 6144 = 120*48. */
-
-typedef enum
-{
- dropus_mode_silk,
- dropus_mode_celt,
- dropus_mode_hybrid
-} dropus_mode;
-
-typedef struct
-{
- dropus_uint16 sizeInBytes;
-} dropus_stream_frame;
-
-typedef struct
-{
- dropus_uint8 toc; /* TOC byte. RFC 6716 - Section 3.1 */
- dropus_stream_frame frames[DROPUS_MAX_OPUS_FRAMES_PER_PACKET];
-} dropus_stream_packet;
-
-typedef struct
-{
- dropus_stream_packet packet; /* The current packet. */
- struct
- {
- dropus_int32 w0_Q13_prev; /* Previous stereo weights. */
- dropus_int32 w1_Q13_prev;
- } silk;
-} dropus_stream;
-
-/*
-Initializes a new low-level Opus stream object.
-*/
-DROPUS_API dropus_result dropus_stream_init(dropus_stream* pOpusStream);
-
-/*
-Decodes a packet from raw compressed data.
-*/
-DROPUS_API dropus_result dropus_stream_decode_packet(dropus_stream* pOpusStream, const void* pData, size_t dataSize);
-
-
-
-/***********************************************************************************************************************************************************
-
-High-Level Opus Decoding API
-
-************************************************************************************************************************************************************/
-typedef enum
-{
- dropus_seek_origin_start,
- dropus_seek_origin_current
-} dropus_seek_origin;
-
-typedef size_t (* dropus_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
-typedef dropus_bool32 (* dropus_seek_proc)(void* pUserData, int offset, dropus_seek_origin origin);
-
-typedef struct
-{
- void* pUserData;
- void* (* onMalloc)(size_t sz, void* pUserData);
- void* (* onRealloc)(void* p, size_t sz, void* pUserData);
- void (* onFree)(void* p, void* pUserData);
-} dropus_allocation_callbacks;
-
-typedef struct
-{
- dropus_read_proc onRead;
- dropus_seek_proc onSeek;
- void* pUserData;
- dropus_allocation_callbacks allocationCallbacks;
- void* pFile; /* Only used for decoders that were opened against a file. */
- struct
- {
- const dropus_uint8* pData;
- size_t dataSize;
- size_t currentReadPos;
- } memory; /* Only used for decoders that were opened against a block of memory. */
-} dropus;
-
-/*
-Initializes a pre-allocated decoder object from callbacks.
-*/
-DROPUS_API dropus_result dropus_init(dropus* pOpus, dropus_read_proc onRead, dropus_seek_proc onSeek, void* pUserData, const dropus_allocation_callbacks* pAllocationCallbacks);
-
-#ifndef DR_OPUS_NO_STDIO
-/*
-Initializes a pre-allocated decoder object from a file.
-
-This keeps hold of the file handle throughout the lifetime of the decoder and closes it in dropus_uninit().
-*/
-DROPUS_API dropus_result dropus_init_file(dropus* pOpus, const char* pFilePath, const dropus_allocation_callbacks* pAllocationCallbacks);
-DROPUS_API dropus_result dropus_init_file_w(dropus* pOpus, const wchar_t* pFilePath, const dropus_allocation_callbacks* pAllocationCallbacks);
-#endif
-
-/*
-Initializes a pre-allocated decoder object from a block of memory.
-
-This does not make a copy of the memory.
-*/
-DROPUS_API dropus_result dropus_init_memory(dropus* pOpus, const void* pData, size_t dataSize, const dropus_allocation_callbacks* pAllocationCallbacks);
-
-/*
-Uninitializes an Opus decoder.
-*/
-DROPUS_API void dropus_uninit(dropus* pOpus);
-
-
-
-/************************************************************************************************************************************************************
-
-Utilities
-
-************************************************************************************************************************************************************/
-/*
-Retrieves a human readable description of the given result code.
-*/
-DROPUS_API const char* dropus_result_description(dropus_result result);
-
-/*
-Allocates memory from callbacks.
-
-
-Parameters
-----------
-sz (in)
- The size of the allocation in bytes.
-
-pAllocationCallbacks (in, optional)
- A pointer to the `dropus_allocation_callbacks` object containing pointers to the allocation routines.
-
-
-Return Value
-------------
-A pointer to the allocated block of memory. NULL if an error occurs.
-
-
-Remarks
--------
-`pAllocationCallbacks` can be NULL, in which case DROPUS_MALLOC() will be used. Otherwise, if `pAllocationCallbacks` is not null, either `onMalloc` or
-`onRealloc` must be set. If `onMalloc` is NULL, it will fall back to `onRealloc`. If both `onMalloc` and `onRealloc` are NULL, NULL will be returned.
-*/
-DROPUS_API void* dropus_malloc(size_t sz, const dropus_allocation_callbacks* pAllocationCallbacks);
-
-/*
-Reallocates memory from callbacks.
-
-
-Parameters
-----------
-p (in)
- A pointer to the memory being reallocated.
-
-sz (in)
- The size of the allocation in bytes.
-
-pAllocationCallbacks (in, optional)
- A pointer to the `dropus_allocation_callbacks` object containing pointers to the allocation routines.
-
-
-Return Value
-------------
-A pointer to the allocated block of memory. NULL if an error occurs.
-
-
-Remarks
--------
-`pAllocationCallbacks` can be NULL, in which case DROPUS_REALLOC() will be used. If `onRealloc` is NULL, this will fail and NULL will be returned.
-*/
-DROPUS_API void* dropus_realloc(void* p, size_t sz, const dropus_allocation_callbacks* pAllocationCallbacks);
-
-/*
-Frees memory allocated by `dropus_malloc()` or `dropus_realloc()`.
-
-
-Parameters
-----------
-p (in)
- A pointer to the memory being freed.
-
-
-Remarks
--------
-`pAllocationCallbacks` can be NULL in which case DROPUS_FREE() will be used. If `onFree` is NULL, this will be a no-op.
-*/
-DROPUS_API void dropus_free(void* p, const dropus_allocation_callbacks* pAllocationCallbacks);
-
-
-#endif /* dr_opus_h */
-
-/************************************************************************************************************************************************************
- ************************************************************************************************************************************************************
-
- IMPLEMENTATION
-
- ************************************************************************************************************************************************************
- ************************************************************************************************************************************************************/
-#if defined(DR_OPUS_IMPLEMENTATION) || defined(DROPUS_IMPLEMENTATION)
-#include
-#include
-#ifndef DR_OPUS_NO_STDIO
-#include
-#include /* For wcslen(), wcsrtombs() */
-#endif
-
-/* CPU Architecture. */
-#if defined(__x86_64__) || defined(_M_X64)
- #define DROPUS_X64
-#elif defined(__i386) || defined(_M_IX86)
- #define DROPUS_X86
-#elif defined(__arm__) || defined(_M_ARM)
- #define DROPUS_ARM
-#endif
-
-/* Compile-time CPU feature support. */
-#if !defined(DR_OPUS_NO_SIMD) && (defined(DROPUS_X86) || defined(DROPUS_X64))
- #if defined(_MSC_VER) && !defined(__clang__)
- #if _MSC_VER >= 1400
- #include
- static DROPUS_INLINE void dropus__cpuid(int info[4], int fid)
- {
- __cpuid(info, fid);
- }
- #else
- #define DROPUS_NO_CPUID
- #endif
- #else
- #if defined(__GNUC__) || defined(__clang__)
- static DROPUS_INLINE void dropus__cpuid(int info[4], int fid)
- {
- /*
- It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the
- specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for
- supporting different assembly dialects.
-
- What's basically happening is that we're saving and restoring the ebx register manually.
- */
- #if defined(DROPUS_X86) && defined(__PIC__)
- __asm__ __volatile__ (
- "xchg{l} {%%}ebx, %k1;"
- "cpuid;"
- "xchg{l} {%%}ebx, %k1;"
- : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
- );
- #else
- __asm__ __volatile__ (
- "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
- );
- #endif
- }
- #else
- #define DROPUS_NO_CPUID
- #endif
- #endif
-#else
- #define DROPUS_NO_CPUID
-#endif
-
-
-#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DROPUS_X86) || defined(DROPUS_X64))
- #define DROPUS_HAS_LZCNT_INTRINSIC
-#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
- #define DROPUS_HAS_LZCNT_INTRINSIC
-#elif defined(__clang__)
- #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl)
- #define DROPUS_HAS_LZCNT_INTRINSIC
- #endif
-#endif
-
-#if defined(_MSC_VER) && _MSC_VER >= 1400
- #define DROPUS_HAS_BYTESWAP16_INTRINSIC
- #define DROPUS_HAS_BYTESWAP32_INTRINSIC
- #define DROPUS_HAS_BYTESWAP64_INTRINSIC
-#elif defined(__clang__)
- #if __has_builtin(__builtin_bswap16)
- #define DROPUS_HAS_BYTESWAP16_INTRINSIC
- #endif
- #if __has_builtin(__builtin_bswap32)
- #define DROPUS_HAS_BYTESWAP32_INTRINSIC
- #endif
- #if __has_builtin(__builtin_bswap64)
- #define DROPUS_HAS_BYTESWAP64_INTRINSIC
- #endif
-#elif defined(__GNUC__)
- #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
- #define DROPUS_HAS_BYTESWAP32_INTRINSIC
- #define DROPUS_HAS_BYTESWAP64_INTRINSIC
- #endif
- #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
- #define DROPUS_HAS_BYTESWAP16_INTRINSIC
- #endif
-#endif
-
-
-#ifndef DROPUS_ASSERT
-#include
-#define DROPUS_ASSERT(expression) assert(expression)
-#endif
-#ifndef DROPUS_MALLOC
-#define DROPUS_MALLOC(sz) malloc((sz))
-#endif
-#ifndef DROPUS_REALLOC
-#define DROPUS_REALLOC(p, sz) realloc((p), (sz))
-#endif
-#ifndef DROPUS_FREE
-#define DROPUS_FREE(p) free((p))
-#endif
-#ifndef DROPUS_COPY_MEMORY
-#define DROPUS_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
-#endif
-#ifndef DROPUS_ZERO_MEMORY
-#define DROPUS_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
-#endif
-#ifndef DROPUS_ZERO_OBJECT
-#define DROPUS_ZERO_OBJECT(p) DROPUS_ZERO_MEMORY((p), sizeof(*(p)))
-#endif
-
-#ifndef DROPUS_MIN
-#define DROPUS_MIN(x, y) (((x) < (y)) ? (x) : (y))
-#endif
-#ifndef DROPUS_MAX
-#define DROPUS_MAX(x, y) (((x) > (y)) ? (x) : (y))
-#endif
-#ifndef DROPUS_COUNTOF
-#define DROPUS_COUNTOF(p) (sizeof(p) / sizeof((p)[0]))
-#endif
-
-
-/***********************************
-Endian Management
-************************************/
-static DROPUS_INLINE dropus_bool32 dropus__is_little_endian()
-{
-#if defined(DROPUS_X86) || defined(DROPUS_X64)
- return DROPUS_TRUE;
-#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
- return DROPUS_TRUE;
-#else
- int n = 1;
- return (*(char*)&n) == 1;
-#endif
-}
-
-static DROPUS_INLINE dropus_uint16 dropus__swap_endian_uint16(dropus_uint16 n)
-{
-#ifdef DROPUS_HAS_BYTESWAP16_INTRINSIC
- #if defined(_MSC_VER)
- return _byteswap_ushort(n);
- #elif defined(__GNUC__) || defined(__clang__)
- return __builtin_bswap16(n);
- #else
- #error "This compiler does not support the byte swap intrinsic."
- #endif
-#else
- return ((n & 0xFF00) >> 8) |
- ((n & 0x00FF) << 8);
-#endif
-}
-
-static DROPUS_INLINE dropus_uint32 dropus__swap_endian_uint32(dropus_uint32 n)
-{
-#ifdef DROPUS_HAS_BYTESWAP32_INTRINSIC
- #if defined(_MSC_VER)
- return _byteswap_ulong(n);
- #elif defined(__GNUC__) || defined(__clang__)
- return __builtin_bswap32(n);
- #else
- #error "This compiler does not support the byte swap intrinsic."
- #endif
-#else
- return ((n & 0xFF000000) >> 24) |
- ((n & 0x00FF0000) >> 8) |
- ((n & 0x0000FF00) << 8) |
- ((n & 0x000000FF) << 24);
-#endif
-}
-
-static DROPUS_INLINE dropus_uint64 dropus__swap_endian_uint64(dropus_uint64 n)
-{
-#ifdef DROPUS_HAS_BYTESWAP64_INTRINSIC
- #if defined(_MSC_VER)
- return _byteswap_uint64(n);
- #elif defined(__GNUC__) || defined(__clang__)
- return __builtin_bswap64(n);
- #else
- #error "This compiler does not support the byte swap intrinsic."
- #endif
-#else
- return ((n & (dropus_uint64)0xFF00000000000000) >> 56) |
- ((n & (dropus_uint64)0x00FF000000000000) >> 40) |
- ((n & (dropus_uint64)0x0000FF0000000000) >> 24) |
- ((n & (dropus_uint64)0x000000FF00000000) >> 8) |
- ((n & (dropus_uint64)0x00000000FF000000) << 8) |
- ((n & (dropus_uint64)0x0000000000FF0000) << 24) |
- ((n & (dropus_uint64)0x000000000000FF00) << 40) |
- ((n & (dropus_uint64)0x00000000000000FF) << 56);
-#endif
-}
-
-
-static DROPUS_INLINE dropus_uint16 dropus__be2host_16(dropus_uint16 n)
-{
- if (dropus__is_little_endian()) {
- return dropus__swap_endian_uint16(n);
- }
-
- return n;
-}
-
-static DROPUS_INLINE dropus_uint32 dropus__be2host_32(dropus_uint32 n)
-{
- if (dropus__is_little_endian()) {
- return dropus__swap_endian_uint32(n);
- }
-
- return n;
-}
-
-static DROPUS_INLINE dropus_uint64 dropus__be2host_64(dropus_uint64 n)
-{
- if (dropus__is_little_endian()) {
- return dropus__swap_endian_uint64(n);
- }
-
- return n;
-}
-
-
-static DROPUS_INLINE dropus_uint16 dropus__le2host_16(dropus_uint16 n)
-{
- if (!dropus__is_little_endian()) {
- return dropus__swap_endian_uint16(n);
- }
-
- return n;
-}
-
-static DROPUS_INLINE dropus_uint32 dropus__le2host_32(dropus_uint32 n)
-{
- if (!dropus__is_little_endian()) {
- return dropus__swap_endian_uint32(n);
- }
-
- return n;
-}
-
-static DROPUS_INLINE dropus_uint64 dropus__le2host_64(dropus_uint64 n)
-{
- if (!dropus__is_little_endian()) {
- return dropus__swap_endian_uint64(n);
- }
-
- return n;
-}
-
-
-/***********************************************************************************************************************************************************
-
-Low-Level Opus Stream API
-
-************************************************************************************************************************************************************/
-#define DROPUS_MAX_FRAME_SIZE_IN_BYTES 1275 /* RFC 6716 - Section 3.4 [R2] */
-#define DROPUS_MAX_PACKET_SIZE_IN_BYTES DROPUS_MAX_FRAME_SIZE_IN_BYTES*DROPUS_MAX_OPUS_FRAMES_PER_PACKET
-
-/***********************************
-RFC 6716 - Section 3.1 The TOC Byte
-************************************/
-static DROPUS_INLINE dropus_uint8 dropus_toc_config(dropus_uint8 toc)
-{
- return (toc & 0xF8) >> 3;
-}
-
-static DROPUS_INLINE dropus_uint8 dropus_toc_s(dropus_uint8 toc)
-{
- return (toc & 0x04) >> 2;
-}
-
-static DROPUS_INLINE dropus_uint8 dropus_toc_c(dropus_uint8 toc)
-{
- return (toc & 0x03);
-}
-
-static DROPUS_INLINE dropus_mode dropus_toc_config_mode(dropus_uint8 config)
-{
- /* Table 2 in RFC 6716 */
- static dropus_mode modes[32] = {
- dropus_mode_silk, dropus_mode_silk, dropus_mode_silk, dropus_mode_silk, /* 0...3 */
- dropus_mode_silk, dropus_mode_silk, dropus_mode_silk, dropus_mode_silk, /* 4...7 */
- dropus_mode_silk, dropus_mode_silk, dropus_mode_silk, dropus_mode_silk, /* 8...11 */
- dropus_mode_hybrid, dropus_mode_hybrid, /* 12...13 */
- dropus_mode_hybrid, dropus_mode_hybrid, /* 14...15 */
- dropus_mode_celt, dropus_mode_celt, dropus_mode_celt, dropus_mode_celt, /* 16...19 */
- dropus_mode_celt, dropus_mode_celt, dropus_mode_celt, dropus_mode_celt, /* 20...23 */
- dropus_mode_celt, dropus_mode_celt, dropus_mode_celt, dropus_mode_celt, /* 24...27 */
- dropus_mode_celt, dropus_mode_celt, dropus_mode_celt, dropus_mode_celt /* 28...31 */
- };
-
- DROPUS_ASSERT(config < 32);
- return modes[config];
-}
-
-static DROPUS_INLINE dropus_mode dropus_toc_mode(dropus_uint8 toc)
-{
- return dropus_toc_config_mode(dropus_toc_config(toc));
-}
-
-static DROPUS_INLINE dropus_uint32 dropus_toc_config_sample_rate(dropus_uint8 config)
-{
- /* Table 2 with Table 1 in RFC 6716 */
- static dropus_uint32 rates[32] = {
- 8000, 8000, 8000, 8000, /* 0...3 */
- 12000, 12000, 12000, 12000, /* 4...7 */
- 16000, 16000, 16000, 16000, /* 8...11 */
- 24000, 24000, /* 12...13 */
- 48000, 48000, /* 14...15 */
- 8000, 8000, 8000, 8000, /* 16...19 */
- 16000, 16000, 16000, 16000, /* 20...23 */
- 24000, 24000, 24000, 24000, /* 24...27 */
- 48000, 48000, 48000, 48000 /* 28...31 */
- };
-
- DROPUS_ASSERT(config < 32);
- return rates[config];
-}
-
-static DROPUS_INLINE dropus_uint32 dropus_toc_sample_rate(dropus_uint8 toc)
-{
- return dropus_toc_config_sample_rate(dropus_toc_config(toc));
-}
-
-static DROPUS_INLINE dropus_uint32 dropus_toc_sample_rate_ms(dropus_uint8 toc)
-{
- return dropus_toc_sample_rate(toc) / 1000;
-}
-
-static DROPUS_INLINE dropus_uint32 dropus_toc_config_frame_size_in_pcm_frames(dropus_uint8 config)
-{
- /* Table 2 with Table 1 in RFC 6716 */
- static dropus_uint32 sizes[32] = {
- 80, 160, 320, 480, /* 0...3 */
- 120, 240, 480, 720, /* 4...7 */
- 160, 320, 640, 960, /* 8...11 */
- 240, 480, /* 12...13 */
- 480, 960, /* 14...15 */
- 20, 40, 80, 160, /* 16...19 */
- 40, 80, 160, 320, /* 20...23 */
- 60, 120, 240, 480, /* 24...27 */
- 120, 240, 480, 960 /* 28...31 */
- };
-
- DROPUS_ASSERT(config < 32);
- return sizes[config];
-}
-
-static DROPUS_INLINE dropus_uint32 dropus_toc_frame_size_in_pcm_frames(dropus_uint8 toc)
-{
- return dropus_toc_config_frame_size_in_pcm_frames(dropus_toc_config(toc));
-}
-
-static DROPUS_INLINE dropus_uint8 dropus_toc_config_silk_frame_count(dropus_uint8 config)
-{
- /* Table 2 in RFC 6716 */
- static dropus_uint8 counts[32] = {
- 1, 1, 2, 3, /* 0...3 */
- 1, 1, 2, 3, /* 4...7 */
- 1, 1, 2, 3, /* 8...11 */
- 1, 1, /* 12...13 */
- 1, 1, /* 14...15 */
- 0, 0, 0, 0, /* 16...19 */ /* Here down is CELT-only. */
- 0, 0, 0, 0, /* 20...23 */
- 0, 0, 0, 0, /* 24...27 */
- 0, 0, 0, 0 /* 28...31 */
- };
-
- DROPUS_ASSERT(config < 32);
- return counts[config];
-}
-
-static DROPUS_INLINE dropus_uint8 dropus_toc_silk_frame_count(dropus_uint8 toc)
-{
- return dropus_toc_config_silk_frame_count(dropus_toc_config(toc));
-}
-
-static DROPUS_INLINE dropus_int32 dropus_Q13(dropus_uint16 index)
-{
- /* Table 7 in RFC 6716 */
- static dropus_int32 Q13[16] = {
- -13732, -10050, -8266, -7526,
- -6500, -5000, -2950, -820,
- 820, 2950, 5000, 6500,
- 7526, 8266, 10050, 13732
- };
-
- DROPUS_ASSERT(index < DROPUS_COUNTOF(Q13));
- return Q13[index];
-}
-
-
-typedef struct
-{
- const dropus_uint8* pData;
- dropus_uint16 dataSize;
- dropus_uint16 readPointer;
- dropus_uint8 b0;
- dropus_uint32 rng; /* RFC 6716 - Section 4.1 - Both val and rng are 32-bit unsigned integer values. */
- dropus_uint32 val; /* ^^^ */
-} dropus_range_decoder;
-
-static DROPUS_INLINE void dropus_range_decoder_normalize(dropus_range_decoder* pRangeDecoder)
-{
- dropus_uint8 sym;
- dropus_uint8 b1;
-
- DROPUS_ASSERT(pRangeDecoder != NULL);
-
- while (pRangeDecoder->rng <= 0x800000) {
- pRangeDecoder->rng = (pRangeDecoder->rng << 8); /* RFC 6716 - Section 4.1.2.1 - First, it sets rng to (rng<<8). */
-
- /* RFC 6716 - Section 4.1.2.1 - If no more input bytes remain, it uses zero bits instead. */
- if (pRangeDecoder->dataSize > pRangeDecoder->readPointer) {
- b1 = pRangeDecoder->pData[pRangeDecoder->readPointer++];
- } else {
- b1 = 0;
- }
-
- /* RFC 6716 - Section 4.1.2.1 - ... using the leftover bit buffered from the previous byte as the high bit and the top 7 bits of the byte just read as the other 7 bits of sym. */
- sym = ((pRangeDecoder->b0 & 0x01) << 7) | (b1 >> 7);
-
- /* RFC 6716 - Section 4.1.2.1 - The remaining bit in the byte just read is buffered for use in the next iteration. */
- pRangeDecoder->b0 = (b1 & 0x01);
-
- /* val */
- pRangeDecoder->val = ((pRangeDecoder->val << 8) + (255 - sym)) & 0x7FFFFFFF;
- }
-
- DROPUS_ASSERT(pRangeDecoder->rng > 0x800000);
-}
-
-static DROPUS_INLINE void dropus_range_decoder_init(const dropus_uint8* pData, dropus_uint16 dataSize, dropus_range_decoder* pRangeDecoder)
-{
- DROPUS_ASSERT(pRangeDecoder != NULL);
-
- pRangeDecoder->pData = pData;
- pRangeDecoder->dataSize = dataSize;
- pRangeDecoder->readPointer = 0;
-
- pRangeDecoder->b0 = 0; /* RFC 6716 - Section 4.1.1 - Let b0 be an 8-bit unsigned integer containing first input byte (or containing zero if there are no bytes in this Opus frame). */
- if (dataSize > 0) {
- pRangeDecoder->b0 = pData[pRangeDecoder->readPointer++];
- }
-
- pRangeDecoder->rng = 128; /* RFC 6716 - Section 4.1.1 - The decoder initializes rng to 128 ... */
- pRangeDecoder->val = 127 - (pRangeDecoder->b0 >> 1); /* ... and initializes val to (127 - (b0>>1)) ...*/
-
- /*
- It saves the remaining bit, (b0&1), for use in the renormalization
- procedure described in Section 4.1.2.1, which the decoder invokes
- immediately after initialization to read additional bits and
- establish the invariant that rng > 2**23.
- */
- dropus_range_decoder_normalize(pRangeDecoder);
-}
-
-static DROPUS_INLINE dropus_uint16 dropus_range_decoder_fs(dropus_range_decoder* pRangeDecoder, dropus_uint16 ft)
-{
- /* Implements RFC 6716 - Section 4.1.2 (first step) */
-
- DROPUS_ASSERT(pRangeDecoder != NULL);
- /*DROPUS_ASSERT(ft <= 65535);*/ /* RFC 6716 - Section 4.1 */ /* Always true due to range limit of `ft`. */
-
- return (dropus_uint16)(ft - DROPUS_MIN((pRangeDecoder->val / (pRangeDecoder->rng/ft)) + 1, ft));
-}
-
-static DROPUS_INLINE dropus_uint16 dropus_range_decoder_k(dropus_uint16* f, dropus_uint16 n, dropus_uint16 fs, dropus_uint16* flOut, dropus_uint16* fhOut)
-{
- dropus_uint8 i;
- dropus_uint16 k = 0;
- dropus_uint16 fl = 0;
- dropus_uint16 fh = 0;
- for (i = 0; i < n; ++i) {
- fh = fl + f[i];
- if (fl <= fs && fs < fh) {
- k = i;
- break;
- }
-
- fl += f[i];
- }
-
- *flOut = fl;
- *fhOut = fh;
- return k;
-}
-
-static DROPUS_INLINE dropus_uint16 dropus_range_decoder_update(dropus_range_decoder* pRangeDecoder, dropus_uint16* f, dropus_uint16 n, dropus_uint16 ft, dropus_uint16 fs)
-{
- /* Implements RFC 6716 - Section 4.1.2 (second step) */
- dropus_uint16 k;
- dropus_uint16 fl;
- dropus_uint16 fh;
-
- DROPUS_ASSERT(pRangeDecoder != NULL);
- DROPUS_ASSERT(f != NULL);
- DROPUS_ASSERT(n > 0);
- DROPUS_ASSERT(ft > 0);
-
- k = dropus_range_decoder_k(f, n, fs, &fl, &fh);
-
- /*DROPUS_ASSERT(0 <= fl);*/ /* RFC 6716 - Section 4.1 */ /* Always true due to `fl` being unsigned. */
- DROPUS_ASSERT(fl < fh); /* RFC 6716 - Section 4.1 */
- DROPUS_ASSERT(fh <= ft); /* RFC 6716 - Section 4.1 */
- /*DROPUS_ASSERT(ft <= 65535);*/ /* RFC 6716 - Section 4.1 */ /* Always true due to range limit of `ft`. */
-
- /* val */
- pRangeDecoder->val = pRangeDecoder->val - (pRangeDecoder->rng/ft) * (ft - fh);
-
- /* rng */
- if (fl > 0) { /* RFC 6716 - Section 4.1.2 - If fl[k] is greater than zero, then the decoder updates rng using... */
- pRangeDecoder->rng = (pRangeDecoder->rng/ft) * (fh - fl);
- } else { /* RFC 6716 - Section 4.1.2 - Otherwise, it updates rng using... */
- pRangeDecoder->rng = pRangeDecoder->rng - (pRangeDecoder->rng/ft) * (ft - fh);
- }
-
- /*
- RFC 6716 - Section 4.1.2
-
- After the updates, implemented by ec_dec_update() (entdec.c), the
- decoder normalizes the range using the procedure in the next section,
- and returns the index k.
- */
- dropus_range_decoder_normalize(pRangeDecoder);
-
- return k;
-}
-
-static DROPUS_INLINE dropus_uint16 dropus_range_decoder_decode(dropus_range_decoder* pRangeDecoder, dropus_uint16* f, dropus_uint16 n, dropus_uint16 ft)
-{
- dropus_uint16 fs;
-
- DROPUS_ASSERT(pRangeDecoder != NULL);
- DROPUS_ASSERT(f != NULL);
- DROPUS_ASSERT(n > 0);
- DROPUS_ASSERT(ft > 0);
-
- /* Step 1 from RFC 6716 - Section 4.1.2. */
- fs = dropus_range_decoder_fs(pRangeDecoder, ft);
-
- /* Step 2 from RFC 6716 - Section 4.1.2. */
- return dropus_range_decoder_update(pRangeDecoder, f, n, ft, fs);
-}
-
-
-DROPUS_API dropus_result dropus_stream_init(dropus_stream* pOpusStream)
-{
- if (pOpusStream == NULL) {
- return DROPUS_INVALID_ARGS;
- }
-
- DROPUS_ZERO_OBJECT(pOpusStream);
-
- return DROPUS_SUCCESS;
-}
-
-
-#define DROPUS_MAX_SILK_FRAME_SIZE_IN_PCM_FRAMES 20*16 /* 20 = 20ms SILK frame length. 16 = Wideband sample rate. */
-
-
-typedef enum
-{
- dropus_silk_frame_regularity_regular,
- dropus_silk_frame_regularity_lbrr,
-} dropus_silk_frame_regularity;
-
-typedef struct
-{
- union
- {
- dropus_int16 s16[DROPUS_MAX_SILK_FRAME_SIZE_IN_PCM_FRAMES];
- float f32[DROPUS_MAX_SILK_FRAME_SIZE_IN_PCM_FRAMES];
- } pcm;
-} dropus_silk_frame;
-
-static dropus_result dropus_stream_decode_silk_frame(dropus_stream* pOpusStream, dropus_range_decoder* pRD, dropus_silk_frame_regularity regularity, dropus_uint8 iSILKFrame, dropus_uint8 iChannel, dropus_uint8* pFlagsVAD, dropus_uint8* pFlagsLBRR, dropus_silk_frame* pSILKFrame)
-{
- dropus_uint8 channels;
- dropus_uint32 w0_Q13 = 0;
- dropus_uint32 w1_Q13 = 0;
- dropus_uint8 midOnlyFlag = 0;
-
- channels = dropus_toc_s(pOpusStream->packet.toc) + 1;
-
- /*
- RFC 6716 - Section 4.2.7.1
-
- ... these weights are coded if and only if
- - This is a stereo Opus frame (Section 3.1), and
- - The current SILK frame corresponds to the mid channel.
- */
- if (channels == 2 && iChannel == 0) {
- dropus_uint16 f_Stage1[] = {7, 2, 1, 1, 1, 10, 24, 8, 1, 1, 3, 23, 92, 23, 3, 1, 1, 8, 24, 10, 1, 1, 1, 2, 7}, ft_Stage1 = 256;
- dropus_uint16 f_Stage2[] = {85, 86, 85}, ft_Stage2 = 256;
- dropus_uint16 f_Stage3[] = {51, 51, 52, 51, 51}, ft_Stage3 = 256;
- dropus_uint16 n;
- dropus_uint16 i0, i1, i2, i3;
- dropus_uint16 wi0, wi1;
-
- n = dropus_range_decoder_decode(pRD, f_Stage1, DROPUS_COUNTOF(f_Stage1), ft_Stage1);
- i0 = dropus_range_decoder_decode(pRD, f_Stage2, DROPUS_COUNTOF(f_Stage2), ft_Stage2);
- i1 = dropus_range_decoder_decode(pRD, f_Stage3, DROPUS_COUNTOF(f_Stage3), ft_Stage3);
- i2 = dropus_range_decoder_decode(pRD, f_Stage2, DROPUS_COUNTOF(f_Stage2), ft_Stage2);
- i3 = dropus_range_decoder_decode(pRD, f_Stage3, DROPUS_COUNTOF(f_Stage3), ft_Stage3);
-
- wi0 = i0 + 3 * (n / 5);
- wi1 = i2 + 3 * (n % 5);
-
- /* Note that w0_Q13 depends on w1_Q13 so must be calculated afterwards. */
- w1_Q13 = dropus_Q13(wi1) + (((dropus_Q13((dropus_uint16)(wi1 + 1)) - dropus_Q13(wi1)) * 6554) >> 16) * ((2 * i3) + 1);
- w0_Q13 = dropus_Q13(wi0) + (((dropus_Q13((dropus_uint16)(wi0 + 1)) - dropus_Q13(wi0)) * 6554) >> 16) * ((2 * i1) + 1) - w1_Q13;
-
- /* RFC 6716 - Section 4.2.7.2 - Mid-Only Flag */
- {
- dropus_uint16 f_MOF[] = {192, 64}, ft_MOF = 256;
- midOnlyFlag = (dropus_uint8)dropus_range_decoder_decode(pRD, f_MOF, DROPUS_COUNTOF(f_MOF), ft_MOF);
- }
- }
-}
-
-static dropus_result dropus_stream_decode_silk_frame__mono(dropus_stream* pOpusStream, dropus_range_decoder* pRD, dropus_silk_frame_regularity regularity, dropus_uint8 iSILKFrame, dropus_uint8* pFlagsVAD, dropus_uint8* pFlagsLBRR, dropus_silk_frame* pSILKFrames)
-{
-
-}
-
-static dropus_result dropus_stream_decode_silk_frame__stereo(dropus_stream* pOpusStream, dropus_range_decoder* pRD, dropus_silk_frame_regularity regularity, dropus_uint8 iSILKFrame, dropus_uint8* pFlagsVAD, dropus_uint8* pFlagsLBRR, dropus_silk_frame* pSILKFrames)
-{
- dropus_uint8 iChannel;
- dropus_uint16 f_Stage1[] = {7, 2, 1, 1, 1, 10, 24, 8, 1, 1, 3, 23, 92, 23, 3, 1, 1, 8, 24, 10, 1, 1, 1, 2, 7}, ft_Stage1 = 256;
- dropus_uint16 f_Stage2[] = {85, 86, 85}, ft_Stage2 = 256;
- dropus_uint16 f_Stage3[] = {51, 51, 52, 51, 51}, ft_Stage3 = 256;
- dropus_uint16 n;
- dropus_uint16 i0, i1, i2, i3;
- dropus_uint16 wi0, wi1;
-
- /*
- RFC 6716 - Section 4.2.7.1
-
- ... these weights are coded if and only if
- - This is a stereo Opus frame (Section 3.1), and
- - The current SILK frame corresponds to the mid channel.
- */
-
- for (iChannel = 0; iChannel < 2; iChannel += 1) {
- /* Don't include LBRR frames if we don't have one. */
- if (regularity == dropus_silk_frame_regularity_lbrr && (pFlagsLBRR[iChannel] & (1 << iSILKFrame)) == 0) {
- continue;
- }
-
- /* The mid channel is always the first one in a SILK frame. */
- if (iChannel == 0) {
- n = dropus_range_decoder_decode(pRD, f_Stage1, DROPUS_COUNTOF(f_Stage1), ft_Stage1);
- i0 = dropus_range_decoder_decode(pRD, f_Stage2, DROPUS_COUNTOF(f_Stage2), ft_Stage2);
- i1 = dropus_range_decoder_decode(pRD, f_Stage3, DROPUS_COUNTOF(f_Stage3), ft_Stage3);
- i2 = dropus_range_decoder_decode(pRD, f_Stage2, DROPUS_COUNTOF(f_Stage2), ft_Stage2);
- i3 = dropus_range_decoder_decode(pRD, f_Stage3, DROPUS_COUNTOF(f_Stage3), ft_Stage3);
-
- wi0 = i0 + 3 * (n / 5);
- wi1 = i2 + 3 * (n % 5);
-
- #if 0
- /* Note that w0_Q13 depends on w1_Q13 so must be calculated afterwards. */
- w1_Q13 = dropus_Q13(wi1) + (((dropus_Q13((dropus_uint16)(wi1 + 1)) - dropus_Q13(wi1)) * 6554) >> 16) * ((2 * i3) + 1);
- w0_Q13 = dropus_Q13(wi0) + (((dropus_Q13((dropus_uint16)(wi0 + 1)) - dropus_Q13(wi0)) * 6554) >> 16) * ((2 * i1) + 1) - w1_Q13;
-
- /* RFC 6716 - Section 4.2.7.2 - Mid-Only Flag */
- {
- dropus_uint16 f_MOF[] = {192, 64}, ft_MOF = 256;
- midOnlyFlag = (dropus_uint8)dropus_range_decoder_decode(pRD, f_MOF, DROPUS_COUNTOF(f_MOF), ft_MOF);
- }
- #endif
- }
- }
-}
-
-static dropus_result dropus_stream_decode_silk_frames__stereo(dropus_stream* pOpusStream, dropus_range_decoder* pRD, dropus_silk_frame_regularity regularity, dropus_uint8* pFlagsVAD, dropus_uint8* pFlagsLBRR, dropus_silk_frame* pSILKFrames)
-{
- dropus_result result;
- dropus_uint8 silkFrameCount;
- dropus_uint8 iSILKFrame;
-
- silkFrameCount = dropus_toc_silk_frame_count(pOpusStream->packet.toc);
- DROPUS_ASSERT(silkFrameCount > 0);
-
- for (iSILKFrame = 0; iSILKFrame < silkFrameCount; iSILKFrame += 1) {
- result = dropus_stream_decode_silk_frame__stereo(pOpusStream, pRD, regularity, iSILKFrame, pFlagsVAD, pFlagsLBRR, pSILKFrames);
- if (result != DROPUS_SUCCESS) {
- return result;
- }
- }
-
- return DROPUS_SUCCESS;
-}
-
-
-static dropus_result dropus_stream_decode_frame__silk(dropus_stream* pOpusStream, dropus_stream_frame* pOpusFrame, const dropus_uint8* pData, size_t dataSize)
-{
- dropus_result result;
- dropus_range_decoder rd;
-
- DROPUS_ASSERT(pOpusStream != NULL);
- DROPUS_ASSERT(pOpusFrame != NULL);
- DROPUS_ASSERT(pData != NULL);
- DROPUS_ASSERT(dataSize <= DROPUS_MAX_FRAME_SIZE_IN_BYTES);
-
- pOpusFrame->sizeInBytes = (dropus_uint16)dataSize; /* Safe cast because dataSize <= DROPUS_MAX_FRAME_SIZE_IN_BYTES <= 1275. */
-
- /* Everything is fed through the range decoder. */
- dropus_range_decoder_init(pData, pOpusFrame->sizeInBytes, &rd);
- {
- dropus_uint16 f_Flags[2] = {1, 1}, ft_Flags = 2;
-
- dropus_uint8 iChannel;
- dropus_uint8 channels;
- dropus_uint8 iFrameSILK;
- dropus_uint8 frameCountSILK;
- dropus_uint16 k;
- dropus_uint8 flagsVAD[2] = {0, 0}; /* One for each channel. */
- dropus_uint8 flagsLBRR[2] = {0, 0}; /* One for each channel. */
- dropus_silk_frame silkFrames_LBRR[2][3];
- dropus_silk_frame silkFrames_Regular[2][3];
-
- dropus_uint32 w0_Q13[3] = {0, 0, 0}; /* One for each SILK frame (max 3). */
- dropus_uint32 w1_Q13[3] = {0, 0, 0}; /* One for each SILK frame (max 3). */
- dropus_uint8 midOnlyFlag[3] = {0, 0, 0}; /* One for each SILK frame (max 3). */
-
- frameCountSILK = dropus_toc_silk_frame_count(pOpusStream->packet.toc); /* SILK frame count. Between 1 and 3. Either 1 10ms SILK frame, or between 1 and 3 20ms frames (20ms, 40ms, 60ms). */
- if (frameCountSILK == 0) {
- return DROPUS_BAD_DATA;
- }
-
- channels = dropus_toc_s(pOpusStream->packet.toc) + 1;
-
- /* Header flags. */
- for (iChannel = 0; iChannel < channels; ++iChannel) {
- for (iFrameSILK = 0; iFrameSILK < frameCountSILK; ++iFrameSILK) {
- k = dropus_range_decoder_decode(&rd, f_Flags, DROPUS_COUNTOF(f_Flags), ft_Flags);
- DROPUS_ASSERT(k <= 1);
- flagsVAD[iChannel] |= (k << iFrameSILK);
- }
-
- k = dropus_range_decoder_decode(&rd, f_Flags, DROPUS_COUNTOF(f_Flags), ft_Flags);
- DROPUS_ASSERT(k <= 1);
- flagsLBRR[iChannel] = (dropus_uint8)k;
- }
-
- /* RFC 6716 - 4.2.4. Per-Frame LBRR Flags. */
- if (frameCountSILK > 1) {
- dropus_uint8 iChannel;
- for (iChannel = 0; iChannel < channels; iChannel += 1) {
- dropus_uint16 f_40[4] = {0, 53, 53, 150}, ft_40 = 256;
- dropus_uint16 f_60[8] = {0, 41, 20, 29, 41, 15, 28, 82}, ft_60 = 256;
- if (flagsLBRR[iChannel] != 0) {
- if (frameCountSILK == 2) {
- k = dropus_range_decoder_decode(&rd, f_40, DROPUS_COUNTOF(f_40), ft_40);
- DROPUS_ASSERT(k <= 0b11);
- } else {
- DROPUS_ASSERT(frameCountSILK == 3);
- k = dropus_range_decoder_decode(&rd, f_60, DROPUS_COUNTOF(f_60), ft_60);
- DROPUS_ASSERT(k <= 0b111);
- }
-
- flagsLBRR[iChannel] = (dropus_uint8)k;
- }
- }
- }
-
- /* LBRR frames. Only do this if the relevant flag is set. */
- for (iFrameSILK = 0; iFrameSILK < frameCountSILK; ++iFrameSILK) {
- if (channels == 1) {
- /*result = dropus_stream_decode_silk_frames__mono(pOpusStream, &rd, );*/
- } else {
- result = dropus_stream_decode_silk_frames__stereo(pOpusStream, &rd, dropus_silk_frame_regularity_lbrr, flagsVAD, flagsLBRR, silkFrames_LBRR);
- }
-
- for (iChannel = 0; iChannel < channels; ++iChannel) {
- if ((flagsLBRR[iChannel] & (1 << iFrameSILK)) != 0) {
- result = dropus_stream_decode_frame__silk_frame(pOpusStream, &rd, &silkFrames_LBRR[iFrameSILK]);
- if (result != DROPUS_SUCCESS) {
- return result;
- }
- }
- }
- }
-
- /* TODO: Don't forget to set the previous stereo weights. Don't just blindly set it without first checking the rules in RFC 6716 - Section 4.2.7.1. */
- /* RFC 6716 - Section 4.2.7.1 - These prediction weights are never included in a mono Opus frame, and the previous weights are reset to zeros on any transition from mono to stereo. */
- if (channels == 1) {
- pOpusStream->silk.w0_Q13_prev = 0;
- pOpusStream->silk.w1_Q13_prev = 0;
- }
-
-
- }
-
- return DROPUS_SUCCESS;
-}
-
-static dropus_result dropus_stream_decode_frame__celt(dropus_stream* pOpusStream, dropus_stream_frame* pOpusFrame, const dropus_uint8* pData, size_t dataSize)
-{
- dropus_range_decoder rd;
-
- DROPUS_ASSERT(pOpusStream != NULL);
- DROPUS_ASSERT(pOpusFrame != NULL);
- DROPUS_ASSERT(pData != NULL);
- DROPUS_ASSERT(dataSize <= DROPUS_MAX_FRAME_SIZE_IN_BYTES);
-
- pOpusFrame->sizeInBytes = (dropus_uint16)dataSize; /* Safe cast because dataSize <= DROPUS_MAX_FRAME_SIZE_IN_BYTES <= 1275. */
-
- /* Everything is fed through the range decoder. */
- dropus_range_decoder_init(pData, pOpusFrame->sizeInBytes, &rd);
- {
-
- }
-
- return DROPUS_SUCCESS;
-}
-
-static dropus_result dropus_stream_decode_frame__hybrid(dropus_stream* pOpusStream, dropus_stream_frame* pOpusFrame, const dropus_uint8* pData, size_t dataSize)
-{
- dropus_range_decoder rd;
-
- DROPUS_ASSERT(pOpusStream != NULL);
- DROPUS_ASSERT(pOpusFrame != NULL);
- DROPUS_ASSERT(pData != NULL);
- DROPUS_ASSERT(dataSize <= DROPUS_MAX_FRAME_SIZE_IN_BYTES);
-
- pOpusFrame->sizeInBytes = (dropus_uint16)dataSize; /* Safe cast because dataSize <= DROPUS_MAX_FRAME_SIZE_IN_BYTES <= 1275. */
-
- /* Everything is fed through the range decoder. */
- dropus_range_decoder_init(pData, pOpusFrame->sizeInBytes, &rd);
- {
-
- }
-
- return DROPUS_SUCCESS;
-}
-
-DROPUS_API dropus_result dropus_stream_decode_packet(dropus_stream* pOpusStream, const void* pData, size_t dataSize)
-{
- const dropus_uint8* pRunningData8 = (const dropus_uint8*)pData;
- dropus_uint8 toc; /* Table of Contents byte. */
- dropus_uint8 opusFrameCount;
- dropus_uint16 opusFrameSizes[DROPUS_MAX_OPUS_FRAMES_PER_PACKET];
- dropus_uint32 code; /* Determines the structure of the Opus packet. */
-
- if (pOpusStream == NULL || pData == NULL) {
- return DROPUS_INVALID_ARGS;
- }
-
- DROPUS_ASSERT(DROPUS_MAX_PACKET_SIZE_IN_BYTES < 65536);
- if (dataSize > DROPUS_MAX_PACKET_SIZE_IN_BYTES) {
- return DROPUS_BAD_DATA;
- }
-
- /* RFC 6716 - Section 3.4 [R1] Packets are at least one byte. */
- if (dataSize < 1) {
- return DROPUS_BAD_DATA;
- }
-
- /* The TOC byte specifies the structure of the packet. */
- toc = pRunningData8[0];
- pRunningData8 += 1;
-
- /*
- We need to look at the code to know the frames making up the packet are structured. We will do a pre-processing step to
- extract basic information about each frame in the packet.
- */
- code = dropus_toc_c(toc);
- switch (code) {
- case 0: /* RFC 6716 - Section 3.2.2. Code 0: One Frame in the Packet */
- {
- dropus_uint16 opusFrameSize = (dropus_uint16)(dataSize-1);
-
- /* RFC 6716 - Section 3.4 [R2] No implicit frame length is larger than 1275 bytes. */
- if (opusFrameSize > DROPUS_MAX_FRAME_SIZE_IN_BYTES) {
- return DROPUS_BAD_DATA;
- }
-
- opusFrameCount = 1;
- opusFrameSizes[0] = opusFrameSize;
- } break;
-
- case 1: /* RFC 6716 - Section 3.2.3. Code 1: Two Frames in the Packet, Each with Equal Compressed Size */
- {
- dropus_uint16 opusFrameSize;
-
- /* RFC 6716 - Section 3.4 [R3] Code 1 packets have an odd total length, N, so that (N-1)/2 is an integer. */
- if ((dataSize & 1) == 0) {
- return DROPUS_BAD_DATA;
- }
-
- opusFrameSize = (dropus_uint16)(dataSize-1)/2;
-
- /* RFC 6716 - Section 3.4 [R2] No implicit frame length is larger than 1275 bytes. */
- if (opusFrameSize > DROPUS_MAX_FRAME_SIZE_IN_BYTES) {
- return DROPUS_BAD_DATA;
- }
-
- opusFrameCount = 2;
- opusFrameSizes[0] = opusFrameSize;
- opusFrameSizes[1] = opusFrameSize;
- } break;
-
- case 2: /* RFC 6716 - Section 3.2.4. Code 2: Two Frames in the Packet, with Different Compressed Sizes */
- {
- dropus_uint8 byte0;
- dropus_uint8 byte1;
- dropus_uint16 opusFrameSize0 = 0;
- dropus_uint16 opusFrameSize1 = 0;
- dropus_uint16 headerByteCount;
-
- /* RFC 6716 - Section 3.4 [R4] Code 2 packets have enough bytes after the TOC for a valid frame length, and that length is no larger than the number of bytes remaining in the packet. */
- if (dataSize < 2) {
- return DROPUS_BAD_DATA;
- }
-
- /* RFC 6716 - Section 3.2.1. Frame Length Coding */
- byte0 = pRunningData8[0]; pRunningData8 += 1;
- if (byte0 == 0) {
- /*
- Section 3.2.1 of RFC 6716 says the following:
-
- "Any Opus frame in any mode MAY have a length of 0.
-
- This implies to me that this is a valid case. dr_opus is going to handle this by setting the PCM frame count to 0 for this packet.
- */
- opusFrameSize0 = 0;
- opusFrameSize1 = 0;
- } else {
- if (byte0 >= 1 && byte0 <= 251) {
- opusFrameSize0 = byte0;
- }
- if (byte0 >= 252 /*&& byte0 <= 255*/) {
- /* RFC 6716 - Section 3.4 [R4] Code 2 packets have enough bytes after the TOC for a valid frame length, and that length is no larger than the number of bytes remaining in the packet. */
- if (dataSize < 3) {
- return DROPUS_BAD_DATA;
- }
-
- byte1 = pRunningData8[0]; pRunningData8 += 1;
- opusFrameSize0 = (byte1*4) + byte0;
- }
-
- headerByteCount = (dropus_uint16)(pRunningData8 - (const dropus_uint8*)pData); /* This is a safe case because the maximum difference will be 3. */
-
- /* RFC 6716 - Section 3.4 [R2] No implicit frame length is larger than 1275 bytes. */
- if (opusFrameSize0 > DROPUS_MAX_FRAME_SIZE_IN_BYTES) {
- return DROPUS_BAD_DATA;
- }
-
- /* RFC 6716 - Section 3.4 [R4] Code 2 packets have enough bytes after the TOC for a valid frame length, and that length is no larger than the number of bytes remaining in the packet. */
- if ((dataSize-headerByteCount) > dataSize) {
- return DROPUS_BAD_DATA;
- }
-
- opusFrameSize1 = (dropus_uint16)(dataSize-headerByteCount-opusFrameSize0); /* Safe cast because dataSize is guaranteed to be < 65536 at this point since it was checked at the top of this function. */
-
- /* RFC 6716 - Section 3.4 [R2] No implicit frame length is larger than 1275 bytes. */
- if (opusFrameSize1 > DROPUS_MAX_FRAME_SIZE_IN_BYTES) {
- return DROPUS_BAD_DATA;
- }
-
- /* RFC 6716 - Section 3.4 [R4] Code 2 packets have enough bytes after the TOC for a valid frame length, and that length is no larger than the number of bytes remaining in the packet. */
- if ((size_t)(headerByteCount + opusFrameSize0 + opusFrameSize1) > dataSize) {
- return DROPUS_BAD_DATA;
- }
- }
-
- opusFrameCount = 2;
- opusFrameSizes[0] = opusFrameSize0;
- opusFrameSizes[1] = opusFrameSize1;
- } break;
-
- case 3: /* RFC 6716 - Section 3.2.5. Code 3: A Signaled Number of Frames in the Packet */
- {
- dropus_uint16 iFrame;
- dropus_uint8 frameCountByte;
- dropus_uint8 v; /* Is VBR? */
- dropus_uint8 p; /* Has padding? */
- dropus_uint8 M; /* Frame count. */
- dropus_uint16 P; /* The size of the padding. Must never be more than dataSize-2. */
- dropus_uint16 R; /* The number of bytes remaining in the packet after subtracting the TOC, frame count byte and padding. */
- dropus_uint32 ms; /* Total length in milliseconds. */
- dropus_uint32 paddingByteCount; /* The number of bytes making up the size of the padding. Only used for validation. */
-
- /*
- RFC 6716 - Section 3.2.5:
- "Code 3 packets MUST have at least 2 bytes [R6,R7]."
- */
- if (dataSize < 2) {
- return DROPUS_BAD_DATA;
- }
-
- frameCountByte = pRunningData8[0]; pRunningData8 += 1;
- v = (frameCountByte & 0x80) >> 7;
- p = (frameCountByte & 0x40) >> 6;
- M = (frameCountByte & 0x3F);
-
- /* RFC 6716 - Section 3.4 [R5] Code 3 packets contain at least one frame, but no more than 120 ms of audio total. */
- ms = (M * dropus_toc_frame_size_in_pcm_frames(toc)) / dropus_toc_sample_rate_ms(toc);
- if (M < 1 || ms > 120) {
- return DROPUS_BAD_DATA;
- }
-
- /* Sanity check to ensure the frame count is never greather than the maximum allowed. */
- if (M > DROPUS_MAX_OPUS_FRAMES_PER_PACKET) {
- return DROPUS_BAD_DATA;
- }
-
- /* Padding bytes. Need to run this in a loop. */
- P = 0;
- paddingByteCount = 0;
- if (p != 0) {
- size_t iPaddingByte;
- for (iPaddingByte = 0; iPaddingByte < dataSize-2; ++iPaddingByte) {
- dropus_uint8 paddingByte = pRunningData8[0]; pRunningData8 += 1;
- P += paddingByte;
- paddingByteCount += 1;
-
- /* A padding byte not equal to 255 signals the last padding byte. */
- if (paddingByte == 255) {
- /* There must be an additional byte available in this case. */
- if (iPaddingByte+1 >= dataSize-2) {
- return DROPUS_BAD_DATA;
- }
- } else {
- break; /* Reached the end of the padding bytes. */
- }
- }
- }
-
- /* Safety check. */
- if (P > dataSize-2) {
- return DROPUS_BAD_DATA;
- }
-
- /* R = bytes remaining. */
- R = (dropus_uint16)(dataSize-2-P);
-
- if (v == 0) {
- /* CBR */
- dropus_uint16 frameSize = R/M;
-
- /* RFC 6716 - Section 3.4 [R2] No implicit frame length is larger than 1275 bytes. */
- if (frameSize > DROPUS_MAX_FRAME_SIZE_IN_BYTES) {
- return DROPUS_BAD_DATA;
- }
-
- /* RFC 6716 - Section 3.4 [R6] ... */
- if (dataSize < 2) { /* ... The length of a CBR code 3 packet, N, is at least two bytes ... */
- return DROPUS_BAD_DATA;
- }
- if (paddingByteCount+P > dataSize-2) { /* ... the number of bytes added to indicate the padding size plus the trailing padding bytes themselves, P, is no more than N-2 ... */
- return DROPUS_BAD_DATA;
- }
- if (frameSize*M != (dropus_uint16)(dataSize-2-P)) { /* ... the frame count, M, satisfies the constraint that (N-2-P) is a non-negative integer multiple of M ... */
- return DROPUS_BAD_DATA;
- }
-
- opusFrameCount = M;
- for (iFrame = 0; iFrame < opusFrameCount; ++iFrame) {
- opusFrameSizes[iFrame] = frameSize;
- }
- } else {
- /* VBR */
- dropus_uint16 totalFrameSizeExceptLast = 0; /* Used later for checking [R7]. */
- dropus_uintptr headerSizeInBytes; /* For validation and deriving the size of the last frame. */
-
- opusFrameCount = M;
- for (iFrame = 0; iFrame < opusFrameCount-1; ++iFrame) {
- dropus_uint8 byte0;
- dropus_uint8 byte1;
-
- if (pRunningData8 >= ((const dropus_uint8*)pData) + dataSize) {
- return DROPUS_BAD_DATA; /* Ran out of data in the packet. Implicitly handles part of [R7]. */
- }
-
- byte0 = pRunningData8[0]; pRunningData8 += 1;
- if (byte0 == 0) {
- opusFrameSizes[iFrame] = 0;
- } else {
- if (byte0 >= 1 && byte0 <= 251) {
- opusFrameSizes[iFrame] = byte0;
- }
- if (byte0 >= 252 /*&& byte0 <= 255*/) {
- if (pRunningData8 >= ((const dropus_uint8*)pData) + dataSize) {
- return DROPUS_BAD_DATA; /* Ran out of data in the packet. Implicitly handles part of [R7]. */
- }
-
- byte1 = pRunningData8[0]; pRunningData8 += 1;
- opusFrameSizes[iFrame] = (byte1*4) + byte0;
-
- /* RFC 6716 - Section 3.4 [R2] No implicit frame length is larger than 1275 bytes. */
- if (opusFrameSizes[iFrame] > DROPUS_MAX_FRAME_SIZE_IN_BYTES) {
- return DROPUS_BAD_DATA;
- }
- }
- }
-
- totalFrameSizeExceptLast += opusFrameSizes[iFrame];
- }
-
- headerSizeInBytes = (dropus_uintptr)(pRunningData8 - (const dropus_uint8*)pData);
-
- /*
- RFC 6716 - Section 3.4 [R6]
- VBR code 3 packets are large enough to contain all the header
- bytes (TOC byte, frame count byte, any padding length bytes,
- and any frame length bytes), plus the length of the first M-1
- frames, plus any trailing padding bytes.
- */
- if ((headerSizeInBytes + totalFrameSizeExceptLast + P) > dataSize) {
- return DROPUS_BAD_DATA;
- }
-
- /* The size of the last frame is derived. */
- opusFrameSizes[opusFrameCount-1] = (dropus_uint16)(dataSize - headerSizeInBytes - totalFrameSizeExceptLast - P); /* Safe cast thanks to the myriad of validation done beforehand. */
-
- /* RFC 6716 - Section 3.4 [R2] No implicit frame length is larger than 1275 bytes. */
- if (opusFrameSizes[opusFrameCount-1] > DROPUS_MAX_FRAME_SIZE_IN_BYTES) {
- return DROPUS_BAD_DATA;
- }
- }
- } break;
-
- /* Will never hit this, but need the default to keep some compilers quiet. */
- default: return DROPUS_BAD_DATA;
- }
-
- pOpusStream->packet.toc = toc;
-
- /* Make sure the frame count is within a valid range. */
- if (opusFrameCount > DROPUS_MAX_OPUS_FRAMES_PER_PACKET) {
- return DROPUS_BAD_DATA;
- }
-
- /* At this point, pRunningData8 should be sitting on the first byte of the first frame in the packet. */
-
- /* Decoding. */
- {
- dropus_result result;
- dropus_uint16 iFrame;
- dropus_mode mode;
-
- mode = dropus_toc_mode(pOpusStream->packet.toc);
-
- /* Would be slightly more optimal to pull the mode checks out of the loop, but it's more compact, readable and maintainable to do it inside. We can let the compiler decide what to do with it. */
- for (iFrame = 0; iFrame < opusFrameCount; ++iFrame) {
- /* */ if (mode == dropus_mode_silk) {
- result = dropus_stream_decode_frame__silk(pOpusStream, &pOpusStream->packet.frames[iFrame], pRunningData8, opusFrameSizes[iFrame]);
- } else if (mode == dropus_mode_celt) {
- result = dropus_stream_decode_frame__celt(pOpusStream, &pOpusStream->packet.frames[iFrame], pRunningData8, opusFrameSizes[iFrame]);
- } else {
- DROPUS_ASSERT(mode == dropus_mode_hybrid);
- result = dropus_stream_decode_frame__hybrid(pOpusStream, &pOpusStream->packet.frames[iFrame], pRunningData8, opusFrameSizes[iFrame]);
- }
-
- if (result != DROPUS_SUCCESS) {
- return result; /* Probably a corrupt frame. */
- }
-
- pRunningData8 += opusFrameSizes[iFrame];
- }
- }
-
- return DROPUS_SUCCESS;
-}
-
-
-
-/***********************************************************************************************************************************************************
-
-High-Level Opus Decoding API
-
-************************************************************************************************************************************************************/
-static void* dropus__malloc_default(size_t sz, void* pUserData)
-{
- (void)pUserData;
- return DROPUS_MALLOC(sz);
-}
-
-static void* dropus__realloc_default(void* p, size_t sz, void* pUserData)
-{
- (void)pUserData;
- return DROPUS_REALLOC(p, sz);
-}
-
-static void dropus__free_default(void* p, void* pUserData)
-{
- (void)pUserData;
- DROPUS_FREE(p);
-}
-
-
-static void* dropus__malloc_from_callbacks(size_t sz, const dropus_allocation_callbacks* pAllocationCallbacks)
-{
- if (pAllocationCallbacks == NULL) {
- return NULL;
- }
-
- if (pAllocationCallbacks->onMalloc != NULL) {
- return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
- }
-
- /* Try using realloc(). */
- if (pAllocationCallbacks->onRealloc != NULL) {
- return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
- }
-
- return NULL;
-}
-
-static void* dropus__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const dropus_allocation_callbacks* pAllocationCallbacks)
-{
- if (pAllocationCallbacks == NULL) {
- return NULL;
- }
-
- if (pAllocationCallbacks->onRealloc != NULL) {
- return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
- }
-
- /* Try emulating realloc() in terms of malloc()/free(). */
- if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
- void* p2;
-
- p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
- if (p2 == NULL) {
- return NULL;
- }
-
- if (p != NULL) {
- DROPUS_COPY_MEMORY(p2, p, szOld);
- pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
- }
-
- return p2;
- }
-
- return NULL;
-}
-
-static void dropus__free_from_callbacks(void* p, const dropus_allocation_callbacks* pAllocationCallbacks)
-{
- if (p == NULL || pAllocationCallbacks == NULL) {
- return;
- }
-
- if (pAllocationCallbacks->onFree != NULL) {
- pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
- }
-}
-
-
-static dropus_allocation_callbacks dropus__copy_allocation_callbacks_or_defaults(const dropus_allocation_callbacks* pAllocationCallbacks)
-{
- if (pAllocationCallbacks != NULL) {
- /* Copy. */
- return *pAllocationCallbacks;
- } else {
- /* Defaults. */
- dropus_allocation_callbacks allocationCallbacks;
- allocationCallbacks.pUserData = NULL;
- allocationCallbacks.onMalloc = dropus__malloc_default;
- allocationCallbacks.onRealloc = dropus__realloc_default;
- allocationCallbacks.onFree = dropus__free_default;
- return allocationCallbacks;
- }
-}
-
-static dropus_result dropus_init_internal(dropus* pOpus, dropus_read_proc onRead, dropus_seek_proc onSeek, void* pUserData, const dropus_allocation_callbacks* pAllocationCallbacks)
-{
- DROPUS_ASSERT(pOpus != NULL);
- DROPUS_ASSERT(onRead != NULL);
-
- /* Must always have an onRead callback. */
- if (onRead == NULL) {
- return DROPUS_INVALID_ARGS;
- }
-
- pOpus->onRead = onRead;
- pOpus->onSeek = onSeek;
- pOpus->pUserData = pUserData;
- pOpus->allocationCallbacks = dropus__copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
-
- /* Basic validation for allocation callbacks. free() and at least one of malloc() or realloc() must be set. */
- if (pOpus->allocationCallbacks.onFree == NULL || (pOpus->allocationCallbacks.onMalloc == NULL && pOpus->allocationCallbacks.onRealloc == NULL)) {
- return DROPUS_INVALID_ARGS;
- }
-
- /* TODO: Implement me. */
-
- return DROPUS_SUCCESS;
-}
-
-DROPUS_API dropus_result dropus_init(dropus* pOpus, dropus_read_proc onRead, dropus_seek_proc onSeek, void* pUserData, const dropus_allocation_callbacks* pAllocationCallbacks)
-{
- if (pOpus == NULL) {
- return DROPUS_INVALID_ARGS;
- }
-
- DROPUS_ZERO_OBJECT(pOpus);
-
- return dropus_init_internal(pOpus, onRead, onSeek, pUserData, pAllocationCallbacks);
-}
-
-#ifndef DR_OPUS_NO_STDIO
-#include
-static dropus_result dropus_result_from_errno(int e)
-{
- switch (e)
- {
- case 0: return DROPUS_SUCCESS;
- #ifdef EPERM
- case EPERM: return DROPUS_INVALID_OPERATION;
- #endif
- #ifdef ENOENT
- case ENOENT: return DROPUS_DOES_NOT_EXIST;
- #endif
- #ifdef ESRCH
- case ESRCH: return DROPUS_DOES_NOT_EXIST;
- #endif
- #ifdef EINTR
- case EINTR: return DROPUS_INTERRUPT;
- #endif
- #ifdef EIO
- case EIO: return DROPUS_IO_ERROR;
- #endif
- #ifdef ENXIO
- case ENXIO: return DROPUS_DOES_NOT_EXIST;
- #endif
- #ifdef E2BIG
- case E2BIG: return DROPUS_INVALID_ARGS;
- #endif
- #ifdef ENOEXEC
- case ENOEXEC: return DROPUS_INVALID_FILE;
- #endif
- #ifdef EBADF
- case EBADF: return DROPUS_INVALID_FILE;
- #endif
- #ifdef ECHILD
- case ECHILD: return DROPUS_ERROR;
- #endif
- #ifdef EAGAIN
- case EAGAIN: return DROPUS_UNAVAILABLE;
- #endif
- #ifdef ENOMEM
- case ENOMEM: return DROPUS_OUT_OF_MEMORY;
- #endif
- #ifdef EACCES
- case EACCES: return DROPUS_ACCESS_DENIED;
- #endif
- #ifdef EFAULT
- case EFAULT: return DROPUS_BAD_ADDRESS;
- #endif
- #ifdef ENOTBLK
- case ENOTBLK: return DROPUS_ERROR;
- #endif
- #ifdef EBUSY
- case EBUSY: return DROPUS_BUSY;
- #endif
- #ifdef EEXIST
- case EEXIST: return DROPUS_ALREADY_EXISTS;
- #endif
- #ifdef EXDEV
- case EXDEV: return DROPUS_ERROR;
- #endif
- #ifdef ENODEV
- case ENODEV: return DROPUS_DOES_NOT_EXIST;
- #endif
- #ifdef ENOTDIR
- case ENOTDIR: return DROPUS_NOT_DIRECTORY;
- #endif
- #ifdef EISDIR
- case EISDIR: return DROPUS_IS_DIRECTORY;
- #endif
- #ifdef EINVAL
- case EINVAL: return DROPUS_INVALID_ARGS;
- #endif
- #ifdef ENFILE
- case ENFILE: return DROPUS_TOO_MANY_OPEN_FILES;
- #endif
- #ifdef EMFILE
- case EMFILE: return DROPUS_TOO_MANY_OPEN_FILES;
- #endif
- #ifdef ENOTTY
- case ENOTTY: return DROPUS_INVALID_OPERATION;
- #endif
- #ifdef ETXTBSY
- case ETXTBSY: return DROPUS_BUSY;
- #endif
- #ifdef EFBIG
- case EFBIG: return DROPUS_TOO_BIG;
- #endif
- #ifdef ENOSPC
- case ENOSPC: return DROPUS_NO_SPACE;
- #endif
- #ifdef ESPIPE
- case ESPIPE: return DROPUS_BAD_SEEK;
- #endif
- #ifdef EROFS
- case EROFS: return DROPUS_ACCESS_DENIED;
- #endif
- #ifdef EMLINK
- case EMLINK: return DROPUS_TOO_MANY_LINKS;
- #endif
- #ifdef EPIPE
- case EPIPE: return DROPUS_BAD_PIPE;
- #endif
- #ifdef EDOM
- case EDOM: return DROPUS_OUT_OF_RANGE;
- #endif
- #ifdef ERANGE
- case ERANGE: return DROPUS_OUT_OF_RANGE;
- #endif
- #ifdef EDEADLK
- case EDEADLK: return DROPUS_DEADLOCK;
- #endif
- #ifdef ENAMETOOLONG
- case ENAMETOOLONG: return DROPUS_PATH_TOO_LONG;
- #endif
- #ifdef ENOLCK
- case ENOLCK: return DROPUS_ERROR;
- #endif
- #ifdef ENOSYS
- case ENOSYS: return DROPUS_NOT_IMPLEMENTED;
- #endif
- #ifdef ENOTEMPTY
- case ENOTEMPTY: return DROPUS_DIRECTORY_NOT_EMPTY;
- #endif
- #ifdef ELOOP
- case ELOOP: return DROPUS_TOO_MANY_LINKS;
- #endif
- #ifdef ENOMSG
- case ENOMSG: return DROPUS_NO_MESSAGE;
- #endif
- #ifdef EIDRM
- case EIDRM: return DROPUS_ERROR;
- #endif
- #ifdef ECHRNG
- case ECHRNG: return DROPUS_ERROR;
- #endif
- #ifdef EL2NSYNC
- case EL2NSYNC: return DROPUS_ERROR;
- #endif
- #ifdef EL3HLT
- case EL3HLT: return DROPUS_ERROR;
- #endif
- #ifdef EL3RST
- case EL3RST: return DROPUS_ERROR;
- #endif
- #ifdef ELNRNG
- case ELNRNG: return DROPUS_OUT_OF_RANGE;
- #endif
- #ifdef EUNATCH
- case EUNATCH: return DROPUS_ERROR;
- #endif
- #ifdef ENOCSI
- case ENOCSI: return DROPUS_ERROR;
- #endif
- #ifdef EL2HLT
- case EL2HLT: return DROPUS_ERROR;
- #endif
- #ifdef EBADE
- case EBADE: return DROPUS_ERROR;
- #endif
- #ifdef EBADR
- case EBADR: return DROPUS_ERROR;
- #endif
- #ifdef EXFULL
- case EXFULL: return DROPUS_ERROR;
- #endif
- #ifdef ENOANO
- case ENOANO: return DROPUS_ERROR;
- #endif
- #ifdef EBADRQC
- case EBADRQC: return DROPUS_ERROR;
- #endif
- #ifdef EBADSLT
- case EBADSLT: return DROPUS_ERROR;
- #endif
- #ifdef EBFONT
- case EBFONT: return DROPUS_INVALID_FILE;
- #endif
- #ifdef ENOSTR
- case ENOSTR: return DROPUS_ERROR;
- #endif
- #ifdef ENODATA
- case ENODATA: return DROPUS_NO_DATA_AVAILABLE;
- #endif
- #ifdef ETIME
- case ETIME: return DROPUS_TIMEOUT;
- #endif
- #ifdef ENOSR
- case ENOSR: return DROPUS_NO_DATA_AVAILABLE;
- #endif
- #ifdef ENONET
- case ENONET: return DROPUS_NO_NETWORK;
- #endif
- #ifdef ENOPKG
- case ENOPKG: return DROPUS_ERROR;
- #endif
- #ifdef EREMOTE
- case EREMOTE: return DROPUS_ERROR;
- #endif
- #ifdef ENOLINK
- case ENOLINK: return DROPUS_ERROR;
- #endif
- #ifdef EADV
- case EADV: return DROPUS_ERROR;
- #endif
- #ifdef ESRMNT
- case ESRMNT: return DROPUS_ERROR;
- #endif
- #ifdef ECOMM
- case ECOMM: return DROPUS_ERROR;
- #endif
- #ifdef EPROTO
- case EPROTO: return DROPUS_ERROR;
- #endif
- #ifdef EMULTIHOP
- case EMULTIHOP: return DROPUS_ERROR;
- #endif
- #ifdef EDOTDOT
- case EDOTDOT: return DROPUS_ERROR;
- #endif
- #ifdef EBADMSG
- case EBADMSG: return DROPUS_BAD_MESSAGE;
- #endif
- #ifdef EOVERFLOW
- case EOVERFLOW: return DROPUS_TOO_BIG;
- #endif
- #ifdef ENOTUNIQ
- case ENOTUNIQ: return DROPUS_NOT_UNIQUE;
- #endif
- #ifdef EBADFD
- case EBADFD: return DROPUS_ERROR;
- #endif
- #ifdef EREMCHG
- case EREMCHG: return DROPUS_ERROR;
- #endif
- #ifdef ELIBACC
- case ELIBACC: return DROPUS_ACCESS_DENIED;
- #endif
- #ifdef ELIBBAD
- case ELIBBAD: return DROPUS_INVALID_FILE;
- #endif
- #ifdef ELIBSCN
- case ELIBSCN: return DROPUS_INVALID_FILE;
- #endif
- #ifdef ELIBMAX
- case ELIBMAX: return DROPUS_ERROR;
- #endif
- #ifdef ELIBEXEC
- case ELIBEXEC: return DROPUS_ERROR;
- #endif
- #ifdef EILSEQ
- case EILSEQ: return DROPUS_INVALID_DATA;
- #endif
- #ifdef ERESTART
- case ERESTART: return DROPUS_ERROR;
- #endif
- #ifdef ESTRPIPE
- case ESTRPIPE: return DROPUS_ERROR;
- #endif
- #ifdef EUSERS
- case EUSERS: return DROPUS_ERROR;
- #endif
- #ifdef ENOTSOCK
- case ENOTSOCK: return DROPUS_NOT_SOCKET;
- #endif
- #ifdef EDESTADDRREQ
- case EDESTADDRREQ: return DROPUS_NO_ADDRESS;
- #endif
- #ifdef EMSGSIZE
- case EMSGSIZE: return DROPUS_TOO_BIG;
- #endif
- #ifdef EPROTOTYPE
- case EPROTOTYPE: return DROPUS_BAD_PROTOCOL;
- #endif
- #ifdef ENOPROTOOPT
- case ENOPROTOOPT: return DROPUS_PROTOCOL_UNAVAILABLE;
- #endif
- #ifdef EPROTONOSUPPORT
- case EPROTONOSUPPORT: return DROPUS_PROTOCOL_NOT_SUPPORTED;
- #endif
- #ifdef ESOCKTNOSUPPORT
- case ESOCKTNOSUPPORT: return DROPUS_SOCKET_NOT_SUPPORTED;
- #endif
- #ifdef EOPNOTSUPP
- case EOPNOTSUPP: return DROPUS_INVALID_OPERATION;
- #endif
- #ifdef EPFNOSUPPORT
- case EPFNOSUPPORT: return DROPUS_PROTOCOL_FAMILY_NOT_SUPPORTED;
- #endif
- #ifdef EAFNOSUPPORT
- case EAFNOSUPPORT: return DROPUS_ADDRESS_FAMILY_NOT_SUPPORTED;
- #endif
- #ifdef EADDRINUSE
- case EADDRINUSE: return DROPUS_ALREADY_IN_USE;
- #endif
- #ifdef EADDRNOTAVAIL
- case EADDRNOTAVAIL: return DROPUS_ERROR;
- #endif
- #ifdef ENETDOWN
- case ENETDOWN: return DROPUS_NO_NETWORK;
- #endif
- #ifdef ENETUNREACH
- case ENETUNREACH: return DROPUS_NO_NETWORK;
- #endif
- #ifdef ENETRESET
- case ENETRESET: return DROPUS_NO_NETWORK;
- #endif
- #ifdef ECONNABORTED
- case ECONNABORTED: return DROPUS_NO_NETWORK;
- #endif
- #ifdef ECONNRESET
- case ECONNRESET: return DROPUS_CONNECTION_RESET;
- #endif
- #ifdef ENOBUFS
- case ENOBUFS: return DROPUS_NO_SPACE;
- #endif
- #ifdef EISCONN
- case EISCONN: return DROPUS_ALREADY_CONNECTED;
- #endif
- #ifdef ENOTCONN
- case ENOTCONN: return DROPUS_NOT_CONNECTED;
- #endif
- #ifdef ESHUTDOWN
- case ESHUTDOWN: return DROPUS_ERROR;
- #endif
- #ifdef ETOOMANYREFS
- case ETOOMANYREFS: return DROPUS_ERROR;
- #endif
- #ifdef ETIMEDOUT
- case ETIMEDOUT: return DROPUS_TIMEOUT;
- #endif
- #ifdef ECONNREFUSED
- case ECONNREFUSED: return DROPUS_CONNECTION_REFUSED;
- #endif
- #ifdef EHOSTDOWN
- case EHOSTDOWN: return DROPUS_NO_HOST;
- #endif
- #ifdef EHOSTUNREACH
- case EHOSTUNREACH: return DROPUS_NO_HOST;
- #endif
- #ifdef EALREADY
- case EALREADY: return DROPUS_IN_PROGRESS;
- #endif
- #ifdef EINPROGRESS
- case EINPROGRESS: return DROPUS_IN_PROGRESS;
- #endif
- #ifdef ESTALE
- case ESTALE: return DROPUS_INVALID_FILE;
- #endif
- #ifdef EUCLEAN
- case EUCLEAN: return DROPUS_ERROR;
- #endif
- #ifdef ENOTNAM
- case ENOTNAM: return DROPUS_ERROR;
- #endif
- #ifdef ENAVAIL
- case ENAVAIL: return DROPUS_ERROR;
- #endif
- #ifdef EISNAM
- case EISNAM: return DROPUS_ERROR;
- #endif
- #ifdef EREMOTEIO
- case EREMOTEIO: return DROPUS_IO_ERROR;
- #endif
- #ifdef EDQUOT
- case EDQUOT: return DROPUS_NO_SPACE;
- #endif
- #ifdef ENOMEDIUM
- case ENOMEDIUM: return DROPUS_DOES_NOT_EXIST;
- #endif
- #ifdef EMEDIUMTYPE
- case EMEDIUMTYPE: return DROPUS_ERROR;
- #endif
- #ifdef ECANCELED
- case ECANCELED: return DROPUS_CANCELLED;
- #endif
- #ifdef ENOKEY
- case ENOKEY: return DROPUS_ERROR;
- #endif
- #ifdef EKEYEXPIRED
- case EKEYEXPIRED: return DROPUS_ERROR;
- #endif
- #ifdef EKEYREVOKED
- case EKEYREVOKED: return DROPUS_ERROR;
- #endif
- #ifdef EKEYREJECTED
- case EKEYREJECTED: return DROPUS_ERROR;
- #endif
- #ifdef EOWNERDEAD
- case EOWNERDEAD: return DROPUS_ERROR;
- #endif
- #ifdef ENOTRECOVERABLE
- case ENOTRECOVERABLE: return DROPUS_ERROR;
- #endif
- #ifdef ERFKILL
- case ERFKILL: return DROPUS_ERROR;
- #endif
- #ifdef EHWPOISON
- case EHWPOISON: return DROPUS_ERROR;
- #endif
- default: return DROPUS_ERROR;
- }
-}
-
-static dropus_result dropus_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
-{
-#if defined(_MSC_VER) && _MSC_VER >= 1400
- errno_t err;
-#endif
-
- if (ppFile != NULL) {
- *ppFile = NULL; /* Safety. */
- }
-
- if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
- return DROPUS_INVALID_ARGS;
- }
-
-#if defined(_MSC_VER) && _MSC_VER >= 1400
- err = fopen_s(ppFile, pFilePath, pOpenMode);
- if (err != 0) {
- return dropus_result_from_errno(err);
- }
-#else
-#if defined(_WIN32) || defined(__APPLE__)
- *ppFile = fopen(pFilePath, pOpenMode);
-#else
- #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
- *ppFile = fopen64(pFilePath, pOpenMode);
- #else
- *ppFile = fopen(pFilePath, pOpenMode);
- #endif
-#endif
- if (*ppFile == NULL) {
- dropus_result result = dropus_result_from_errno(errno);
- if (result == DROPUS_SUCCESS) {
- result = DROPUS_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */
- }
-
- return result;
- }
-#endif
-
- return DROPUS_SUCCESS;
-}
-
-/*
-_wfopen() isn't always available in all compilation environments.
-
- * Windows only.
- * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back).
- * MinGW-64 (both 32- and 64-bit) seems to support it.
- * MinGW wraps it in !defined(__STRICT_ANSI__).
-
-This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs()
-fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support.
-*/
-#if defined(_WIN32)
- #if defined(_MSC_VER) || defined(__MINGW64__) || !defined(__STRICT_ANSI__)
- #define DROPUS_HAS_WFOPEN
- #endif
-#endif
-
-static dropus_result dropus_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const dropus_allocation_callbacks* pAllocationCallbacks)
-{
- if (ppFile != NULL) {
- *ppFile = NULL; /* Safety. */
- }
-
- if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
- return DROPUS_INVALID_ARGS;
- }
-
-#if defined(DROPUS_HAS_WFOPEN)
- {
- /* Use _wfopen() on Windows. */
- #if defined(_MSC_VER) && _MSC_VER >= 1400
- errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
- if (err != 0) {
- return dropus_result_from_errno(err);
- }
- #else
- *ppFile = _wfopen(pFilePath, pOpenMode);
- if (*ppFile == NULL) {
- return dropus_result_from_errno(errno);
- }
- #endif
- (void)pAllocationCallbacks;
- }
-#else
- /*
- Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can
- think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for
- maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility.
- */
- {
- mbstate_t mbs;
- size_t lenMB;
- const wchar_t* pFilePathTemp = pFilePath;
- char* pFilePathMB = NULL;
- char pOpenModeMB[32] = {0};
-
- /* Get the length first. */
- DROPUS_ZERO_OBJECT(&mbs);
- lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
- if (lenMB == (size_t)-1) {
- return dropus_result_from_errno(errno);
- }
-
- pFilePathMB = (char*)dropus__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);
- if (pFilePathMB == NULL) {
- return DROPUS_OUT_OF_MEMORY;
- }
-
- pFilePathTemp = pFilePath;
- DROPUS_ZERO_OBJECT(&mbs);
- wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
-
- /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */
- {
- size_t i = 0;
- for (;;) {
- if (pOpenMode[i] == 0) {
- pOpenModeMB[i] = '\0';
- break;
- }
-
- pOpenModeMB[i] = (char)pOpenMode[i];
- i += 1;
- }
- }
-
- *ppFile = fopen(pFilePathMB, pOpenModeMB);
-
- dropus__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
- }
-
- if (*ppFile == NULL) {
- return DROPUS_ERROR;
- }
-#endif
-
- return DROPUS_SUCCESS;
-}
-
-
-static size_t dropus_on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
-{
- return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
-}
-
-static dropus_bool32 dropus_on_seek_stdio(void* pUserData, int offset, dropus_seek_origin origin)
-{
- return fseek((FILE*)pUserData, offset, (origin == dropus_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
-}
-
-DROPUS_API dropus_result dropus_init_file(dropus* pOpus, const char* pFilePath, const dropus_allocation_callbacks* pAllocationCallbacks)
-{
- dropus_result result;
- FILE* pFile;
-
- if (pOpus == NULL) {
- return DROPUS_INVALID_ARGS;
- }
-
- DROPUS_ZERO_OBJECT(pOpus);
-
- if (pFilePath == NULL || pFilePath[0] == '\0') {
- return DROPUS_INVALID_ARGS;
- }
-
- result = dropus_fopen(&pFile, pFilePath, "rb");
- if (result != DROPUS_SUCCESS) {
- return result;
- }
-
- pOpus->pFile = (void*)pFile;
-
- result = dropus_init_internal(pOpus, dropus_on_read_stdio, dropus_on_seek_stdio, NULL, pAllocationCallbacks);
- if (result != DROPUS_SUCCESS) {
- fclose(pFile);
- return result;
- }
-
- return DROPUS_SUCCESS;
-}
-
-DROPUS_API dropus_result dropus_init_file_w(dropus* pOpus, const wchar_t* pFilePath, const dropus_allocation_callbacks* pAllocationCallbacks)
-{
- dropus_result result;
- FILE* pFile;
-
- if (pOpus == NULL) {
- return DROPUS_INVALID_ARGS;
- }
-
- DROPUS_ZERO_OBJECT(pOpus);
-
- if (pFilePath == NULL || pFilePath[0] == '\0') {
- return DROPUS_INVALID_ARGS;
- }
-
- result = dropus_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks);
- if (result != DROPUS_SUCCESS) {
- return result;
- }
-
- pOpus->pFile = (void*)pFile;
-
- result = dropus_init_internal(pOpus, dropus_on_read_stdio, dropus_on_seek_stdio, NULL, pAllocationCallbacks);
- if (result != DROPUS_SUCCESS) {
- fclose(pFile);
- return result;
- }
-
- return DROPUS_SUCCESS;
-}
-#endif
-
-static size_t dropus_on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
-{
- size_t bytesRemaining;
- dropus* pOpus = (dropus*)pUserData;
-
- DROPUS_ASSERT(pOpus != NULL);
- DROPUS_ASSERT(pOpus->memory.dataSize >= pOpus->memory.currentReadPos);
-
- bytesRemaining = pOpus->memory.dataSize - pOpus->memory.currentReadPos;
- if (bytesToRead > bytesRemaining) {
- bytesToRead = bytesRemaining;
- }
-
- if (bytesToRead > 0) {
- DROPUS_COPY_MEMORY(pBufferOut, pOpus->memory.pData + pOpus->memory.currentReadPos, bytesToRead);
- pOpus->memory.currentReadPos += bytesToRead;
- }
-
- return bytesToRead;
-}
-
-static dropus_bool32 dropus_on_seek_memory(void* pUserData, int byteOffset, dropus_seek_origin origin)
-{
- dropus* pOpus = (dropus*)pUserData;
- DROPUS_ASSERT(pOpus != NULL);
-
- if (origin == dropus_seek_origin_current) {
- if (byteOffset > 0) {
- if (pOpus->memory.currentReadPos + byteOffset > pOpus->memory.dataSize) {
- byteOffset = (int)(pOpus->memory.dataSize - pOpus->memory.currentReadPos); /* Trying to seek too far forward. */
- }
- } else {
- if (pOpus->memory.currentReadPos < (size_t)-byteOffset) {
- byteOffset = -(int)pOpus->memory.currentReadPos; /* Trying to seek too far backwards. */
- }
- }
-
- /* This will never underflow thanks to the clamps above. */
- pOpus->memory.currentReadPos += byteOffset;
- } else {
- if ((dropus_uint32)byteOffset <= pOpus->memory.dataSize) {
- pOpus->memory.currentReadPos = byteOffset;
- } else {
- pOpus->memory.currentReadPos = pOpus->memory.dataSize; /* Trying to seek too far forward. */
- }
- }
-
- return DROPUS_TRUE;
-}
-
-DROPUS_API dropus_result dropus_init_memory(dropus* pOpus, const void* pData, size_t dataSize, const dropus_allocation_callbacks* pAllocationCallbacks)
-{
- if (pOpus == NULL) {
- return DROPUS_INVALID_ARGS;
- }
-
- DROPUS_ZERO_OBJECT(pOpus);
-
- if (pData == NULL || dataSize == 0) {
- return DROPUS_INVALID_ARGS;
- }
-
- pOpus->memory.pData = (const dropus_uint8*)pData;
- pOpus->memory.dataSize = dataSize;
- pOpus->memory.currentReadPos = 0;
-
- return dropus_init_internal(pOpus, dropus_on_read_memory, dropus_on_seek_memory, NULL, pAllocationCallbacks);
-}
-
-
-DROPUS_API void dropus_uninit(dropus* pOpus)
-{
- if (pOpus == NULL) {
- return;
- }
-
-#ifndef DR_OPUS_NO_STDIO
- /* Since dr_opus manages the stdio FILE object make sure it's closed on uninitialization. */
- if (pOpus->pFile != NULL) {
- fclose((FILE*)pOpus->pFile);
- }
-#endif
-}
-
-
-
-DROPUS_API const char* dropus_result_description(dropus_result result)
-{
- switch (result)
- {
- case DROPUS_SUCCESS: return "No error";
- case DROPUS_ERROR: return "Unknown error";
- case DROPUS_INVALID_ARGS: return "Invalid argument";
- case DROPUS_INVALID_OPERATION: return "Invalid operation";
- case DROPUS_OUT_OF_MEMORY: return "Out of memory";
- case DROPUS_OUT_OF_RANGE: return "Out of range";
- case DROPUS_ACCESS_DENIED: return "Permission denied";
- case DROPUS_DOES_NOT_EXIST: return "Resource does not exist";
- case DROPUS_ALREADY_EXISTS: return "Resource already exists";
- case DROPUS_TOO_MANY_OPEN_FILES: return "Too many open files";
- case DROPUS_INVALID_FILE: return "Invalid file";
- case DROPUS_TOO_BIG: return "Too large";
- case DROPUS_PATH_TOO_LONG: return "Path too long";
- case DROPUS_NAME_TOO_LONG: return "Name too long";
- case DROPUS_NOT_DIRECTORY: return "Not a directory";
- case DROPUS_IS_DIRECTORY: return "Is a directory";
- case DROPUS_DIRECTORY_NOT_EMPTY: return "Directory not empty";
- case DROPUS_END_OF_FILE: return "End of file";
- case DROPUS_NO_SPACE: return "No space available";
- case DROPUS_BUSY: return "Device or resource busy";
- case DROPUS_IO_ERROR: return "Input/output error";
- case DROPUS_INTERRUPT: return "Interrupted";
- case DROPUS_UNAVAILABLE: return "Resource unavailable";
- case DROPUS_ALREADY_IN_USE: return "Resource already in use";
- case DROPUS_BAD_ADDRESS: return "Bad address";
- case DROPUS_BAD_SEEK: return "Illegal seek";
- case DROPUS_BAD_PIPE: return "Broken pipe";
- case DROPUS_DEADLOCK: return "Deadlock";
- case DROPUS_TOO_MANY_LINKS: return "Too many links";
- case DROPUS_NOT_IMPLEMENTED: return "Not implemented";
- case DROPUS_NO_MESSAGE: return "No message of desired type";
- case DROPUS_BAD_MESSAGE: return "Invalid message";
- case DROPUS_NO_DATA_AVAILABLE: return "No data available";
- case DROPUS_INVALID_DATA: return "Invalid data";
- case DROPUS_TIMEOUT: return "Timeout";
- case DROPUS_NO_NETWORK: return "Network unavailable";
- case DROPUS_NOT_UNIQUE: return "Not unique";
- case DROPUS_NOT_SOCKET: return "Socket operation on non-socket";
- case DROPUS_NO_ADDRESS: return "Destination address required";
- case DROPUS_BAD_PROTOCOL: return "Protocol wrong type for socket";
- case DROPUS_PROTOCOL_UNAVAILABLE: return "Protocol not available";
- case DROPUS_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported";
- case DROPUS_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported";
- case DROPUS_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported";
- case DROPUS_SOCKET_NOT_SUPPORTED: return "Socket type not supported";
- case DROPUS_CONNECTION_RESET: return "Connection reset";
- case DROPUS_ALREADY_CONNECTED: return "Already connected";
- case DROPUS_NOT_CONNECTED: return "Not connected";
- case DROPUS_CONNECTION_REFUSED: return "Connection refused";
- case DROPUS_NO_HOST: return "No host";
- case DROPUS_IN_PROGRESS: return "Operation in progress";
- case DROPUS_CANCELLED: return "Operation cancelled";
- case DROPUS_MEMORY_ALREADY_MAPPED: return "Memory already mapped";
- case DROPUS_AT_END: return "Reached end of collection";
-
- /* dr_opus specific result codes. */
- case DROPUS_CRC_MISMATCH: return "CRC mismatch";
- case DROPUS_BAD_DATA: return "Bad data";
-
- default: return "Unknown error";
- }
-}
-
-DROPUS_API void* dropus_malloc(size_t sz, const dropus_allocation_callbacks* pAllocationCallbacks)
-{
- if (pAllocationCallbacks == NULL) {
- return dropus__malloc_default(sz, NULL);
- } else {
- return dropus__malloc_from_callbacks(sz, pAllocationCallbacks);
- }
-}
-
-DROPUS_API void* dropus_realloc(void* p, size_t sz, const dropus_allocation_callbacks* pAllocationCallbacks)
-{
- if (pAllocationCallbacks == NULL) {
- return dropus__realloc_default(p, sz, NULL);
- } else {
- /*
- We need to do an explicit check for onRealloc because dropus__realloc_from_callbacks() will fall back to a malloc based emulation if onRealloc is missing
- which we do not want to do dropus_realloc().
- */
- if (pAllocationCallbacks->onRealloc == NULL) {
- return NULL;
- }
-
- return dropus__realloc_from_callbacks(p, sz, /*szOld*/ 0, pAllocationCallbacks); /* Safe to pass 0 for szOld thanks to the onRealloc check above. */
- }
-}
-
-DROPUS_API void dropus_free(void* p, const dropus_allocation_callbacks* pAllocationCallbacks)
-{
- if (pAllocationCallbacks == NULL) {
- dropus__free_default(p, NULL);
- } else {
- dropus__free_from_callbacks(p, pAllocationCallbacks);
- }
-}
-#endif /* DR_OPUS_IMPLEMENTATION */
-
-/*
-This software is available as a choice of the following licenses. Choose
-whichever you prefer.
-
-===============================================================================
-ALTERNATIVE 1 - Public Domain (www.unlicense.org)
-===============================================================================
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
-software, either in source code form or as a compiled binary, for any purpose,
-commercial or non-commercial, and by any means.
-
-In jurisdictions that recognize copyright laws, the author or authors of this
-software dedicate any and all copyright interest in the software to the public
-domain. We make this dedication for the benefit of the public at large and to
-the detriment of our heirs and successors. We intend this dedication to be an
-overt act of relinquishment in perpetuity of all present and future rights to
-this software under copyright law.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-For more information, please refer to
-
-===============================================================================
-ALTERNATIVE 2 - MIT No Attribution
-===============================================================================
-Copyright 2020 David Reid
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
diff --git a/source/engine/thirdparty/dr_libs/wip/dr_opus_log.txt b/source/engine/thirdparty/dr_libs/wip/dr_opus_log.txt
deleted file mode 100644
index 69fc4cf..0000000
--- a/source/engine/thirdparty/dr_libs/wip/dr_opus_log.txt
+++ /dev/null
@@ -1,318 +0,0 @@
-This is a log of my development of dr_opus - a single file public domain Opus decoder in C. The purpose of this log is
-to track the development of the project and log how I solved various problems.
-
-References:
- RFC 6716 - https://tools.ietf.org/html/rfc6716 - Definition of the Opus Audio Codec
- RFC 7845 - https://tools.ietf.org/html/rfc7845 - Ogg Encapsulation for the Opus Audio Codec
-
-
-The main reference for development is RFC 6716. An important detail with this specification is in Section 1
-
- The primary normative part of this specification is provided by the
- source code in Appendix A. Only the decoder portion of this software
- is normative, though a significant amount of code is shared by both
- the encoder and decoder. Section 6 provides a decoder conformance
- test. The decoder contains a great deal of integer and fixed-point
- arithmetic that needs to be performed exactly, including all rounding
- considerations, so any useful specification requires domain-specific
- symbolic language to adequately define these operations.
- Additionally, any conflict between the symbolic representation and
- the included reference implementation must be resolved. For the
- practical reasons of compatibility and testability, it would be
- advantageous to give the reference implementation priority in any
- disagreement. The C language is also one of the most widely
- understood, human-readable symbolic representations for machine
- behavior. For these reasons, this RFC uses the reference
- implementation as the sole symbolic representation of the codec.
-
-What this is basically saying is that the source code should be considered the main point of reference for the format of
-the Opus bit stream. We cannot, however, just willy nilly take code from the reference implementation because that would
-cause licensing problems. However, I'm going to make a prediction before I even begin: I think RFC 6716 with a bit
-intuition, problem solving and help from others it can be figured out. Indeed, I think RFC 6716 may even be enough, and
-that perhaps the authors of said specification refer to the source code as a self-defence mechanism to guard themselves
-from possible subtle errors in the specification. If I'm wrong, the log below will track it.
-
-Lets begin...
-
-
-2018/11/30 6:55 PM
-==================
-- Inserted skeleton code with licensing information. Dual licensed as Public Domain _or_ MIT, whichever you prefer. API
- is undecided as of yet.
-- Decided on supporting C89. Not yet decided on how the standard library will be used.
-- Deciding for now to use the init() style of API from dr_wav and dr_mp3 which takes a pre-allocated decoder object. May
- need to change this to dr_flac style open() (or perhaps new()/delete()) if the size of the structure is too big.
-- dr_wav, etc. use booleans for results status so I'm copying that for consistency.
-- Added boilerplate for standard sized types (copied from mini_al).
-- Added a comment at the top of the file to show that this isn't complete (even though it's in the "wip" folder!).
-- Added boilerplate for read and seek callbacks (copied from dr_flac).
-- Added shell APIs:
- - dropus_init()
- - Not requiring onSeek for the moment because I'm hoping that we can make that optional somehow, with restriction.
- - dropus_uninit()
- - Does nothing at the moment, but in future will need to close a file when the decoder is opened against a file.
-- Added some common overrideable macros (taken from dr_flac):
- - DROPUS_ASSERT
- - DROPUS_COPY_MEMORY
- - DROPUS_ZERO_MEMORY
- - DROPUS_ZERO_OBJECT
-- Decided to use the C standard library for now. This simplifies things like memset() and assert(). Currently using the
- following headers:
- - stddef.h
- - stdint.h
- - stdlib.h
- - string.h
- - assert.h
-- Added some infrastructure for future APIs: dropus_init_file() and dropus_init_memory()
- - In dr_flac, dr_wav and dr_mp3, a mistake was made with how the internal file handle is managed by the decoder. In
- these libraries the pUserData member of the decoder structure is recycled to hold the FILE pointer. dr_opus is
- changing this to be it's own member of the main decoder structure.
- - The internal members for use with dropus_init_memory() use the same system as dr_mp3. See drmp3.memory.
-- Added dropus_init_file()
- - All init() APIs call dropus_init_internal() which is where the main initialization work is done.
- - This is using the stdio FILE API.
- - dropus_uninit() has been updated to close the file.
- - Added dropus_fopen() for compiler compatibility. Taken from dr_flac, but added the "mode" parameter for consistency
- with fopen().
- - Added dropus_fclose() for namespace consistency with dropus_fopen().
-- Added dropus_init_memory()
- - onRead and onSeek callbacks taken from dr_mp3.
-- INITIAL COMMIT
-
-2018/12/01 12:46 PM
-===================
-- Downloading all official test vectors in preparation for future testing.
-
-2018/12/01 03:58 PM
-===================
-- Decided to figure out the different encapsulations for Opus so I can figure out a design for data flow.
- - Ogg: https://tools.ietf.org/html/rfc7845
- - Matroska: https://wiki.xiph.org/MatroskaOpus
- - WebM: https://www.webmproject.org/docs/container/
- - MPEG-TS: https://wiki.xiph.org/OpusTS
- - Mysterious encapsulation used by the official test vectors. Looking at this it's hard to tell what they are using for
- this, but I'm assuming it's not documented. Will need to figure that one out for myself later.
-- Since WebM is a subset of Matroska, can we combine those together to avoid code duplication?
-- Upon reading into Ogg and MPEG-TS encapsulation (not looked into Matroska in detail yet) it looks like handling large
- channel counts is going to be a pain. It can support up to 255 channels, which are made up of multiple streams. My
- instict is telling me that we won't be able to use a fixed sized data structure and instead might require dynamic
- allocations. Darn...
-- Leaning towards a two-tier architecture - a low-level decoder for raw Opus streams, and a high-level API for encapsulated
- streams. Opus packets support only 1 or 2 channels which means the size of the structure _can_ be fixed.
- - Currently I'm thinking something like the following:
- - struct dropus_stream - low-level Opus stream decoding
- - struct dropus - high-level encapsulated Opus stream decoding
- - struct dropus_ogg (Ogg Opus)
- - struct dropus_mk (Matroska)
- - struct dropus_ts (MPEG-TS)
- - struct dropus_tv (That annoying non-standard and undocumented encapsulation used by the test vectors)
-- The maximum number of samples per frame (per-channel) is 960 because:
- - The maximum frame size for wideband (16kHz) is 60: 16*60 = 960
- - The maximum frame size for fullband (48kHz) is 20: 48*20 = 960
- - To account for stereo frames we would need to multiply 960 by 2: 960*2 = 1920
- - This is small enough that it can be stored statically inside the dropus_stream structure.
-- It looks like low level Opus streams won't be able to do efficient seeking without an encapsulation.
-- An Opus stream is made up of packets. Each packet has 1 or more frames. Each frame can be made up of 1 or 2 channels.
-- The size of the packet in bytes is unknown at the Opus stream level - it depends on the encapsulation to know the
- size of the packet. This could be a bit of a problem because we may need to know the size of a frame in order to know
- how much data we need from the client. I'm wondering if the low-level API should look something like the following:
- - dropus_result dropus_stream_decode_packet(dropus_stream* pOpusStream, const void* pCompressedData, size_t compressedDataSize);
- - This will require the caller to have information on the size of the packet.
-- Added struct dropus_stream_packet, dropus_stream_frame.
-- Each packet contains a byte that defines the structure of the packet. It's called the TOC byte
- (Section 3.1. The TOC Byte) in the RFC 6716.
-- Added skeleton implementations for dropus_stream_init() and dropus_stream_decode_packet().
-- Added helpers for decoding the different parts of TOC byte.
- - dropus_toc_config(), dropus_toc_s() and dropus_toc_c()
-- Added enum for the three different modes as outlined in Section 3.1 of RFC 6716. Also added an API to extract this
- mode from the config component of the TOC: dropus_toc_config_mode() and dropus_toc_mode().
-- Added API to extract sample rate from the TOC: dropus_toc_config_sample_rate() and dropus_toc_sample_rate()
-- Added API to extract the size of an Opus frame in PCM frames from the TOC:
- - dropus_toc_config_frame_size_in_pcm_frames() and dropus_toc_frame_size_in_pcm_frames().
-- COMMIT
-
-2018/12/02 09:45 AM
-===================
-- Compiling for the first time. Targetting C89 so need to get it all working property right from the start.
-- "test" folder added to my local repository. Not version controlled yet as I'm not sure I want to include all of
- the test stuff in the repository. Will definitely not be version controlling the test vectors. Perhaps use a submodule?
-- Have decided to not support the mysterious encapsulation format used by the test vectors in dr_opus itself. Instead
- I'm just doing it manually in the test program.
-- After attempting to build the C++ version, it appears Clang will attempt to #include stdint.h from Visual Studio's
- standard library which fails. May need to come up with a way to avoid the dependency on stdint.h. Same thing also
- happens with stdlib.h.
-- Have cleaned up the sized types stuff for Clang to avoid the dependency on stdint, however errors are still happening
- due to the use of stdlib.h. Leaving the Clang C++ issue for now. It's technically a Clang issue rather than dr_opus.
-- COMMIT
-- Have decided to version control test programs, but not test vectors. Instead I'm putting a note in a readme about
- where to download and place the test vectors.
-- In my experience I have found that having a testing infrastructure set up earlier saves a lot of time in the long run
- so the next thing I have decided to work on is a test program. It makes sense to test against the official test vectors,
- so the first thing is to figure out this undocumented encapsulation format...
-
-Test Vector Encapsulation Format
---------------------------------
-- All test vectors seem to start with two bytes of 0x00.
- - This does not feel like a magic number or identifier.
-- Noticed that first 4 bytes add up to a relatively small number. A counter of some kind in little endian format?
-- A pattern can be seen in testvector02.bit:
- - The bytes 00 00 00 XX can been seen repeating. This indicates some kind of blocking.
- - From the first 4 bytes you see the hex value of 0x0000001E which equals 30 in decimal
- - Counting forward 30 bytes brings us 4 bytes before the next set of 00 00 00 XX bytes. Definitely starting to feel like
- the first 4 bytes are a byte counter.
- - Skipping past those four bytes and looking at the next 00 00 00 XX bytes we get 0x0000001D which is 29 in decimal.
- - Again, counting forward by that number of bytes brings us to 4 bytes prior to the next 00 00 00 XX block. First 4
- bytes are definitely a byte counter.
-- It makes sense that each of these blocks are an Opus packet.
-- Still don't know what the second set of 4 bytes could represent... Does it matter for our purposes? All we care about
- is the raw Opus bit stream.
- - A Google search for "opus test vector format" brings up the following result (a PDF document):
- - https://www.ietf.org/proceedings/82/slides/codec-4.pdf
- - From the "Force Multipliers" page:
- - "The range coder has 32 bits of state which must match between the encoder and decoder"
- - "Provides a checksum of all encoding and decoding decisions"
- - "opus_demo bitstreams include the range value with every packet and test for mismatches"
- - Willing to bet that the extra 4 bytes are that "32 bits of state". This is actually good for dr_opus.
-- I think the format is just a list of Opus packets with 8 bytes of header data for each one:
- 4 bytes: Opus packet size in bytes
- 4 bytes: 32 bit of range coder state
- [Opus Packet]
---------------------------------
-
-- The test program can either hard code a list of vector files or we can enumerate over files in known directories. I'm
- preferring the latter to make it easier to add new vectors. It's also consistent with the way dr_flac does it which has
- been pretty good in the past.
-- Started work on a "common" lib for test programs. First thing is the new file iterator.
- - File iterator now done, but only supporting Windows currently.
-- Have implemented the logic for loading test vectors and the byte counter (and I'm assuming the range state) is actually
- in big-endian, not little-endian. I'm an idiot!
-- Copying endian management APIs from dr_flac over to dr_opus.
-- Basic infrastructure for testing against the standard test vectors is now done.
-- COMMIT
-
-2018/12/03 6:15 PM
-==================
-- The packet enumeration in the test program seems to work so far, but I'm still not 100% sure that my assumptions are
- correct. Adding more validation to dropus_stream_decode_packet() to increase my certainty. The next stage is to
- implement all of the main packet validation.
-- Question for later. How are "Discontinuous Transmission (DTX) or lost packet" handled? Should
- dropus_stream_decode_packet() return an error in this case?
-- Code for validating packet codes 0, 1 and 2 are now in, however untested so far. Code 3 is a bit more complicated and
- right now I need to eat dinner...
-- COMMIT
-
-2018/12/03 6:40 PM
-==================
-- Code 3 CBR is done (no VBR yet). Still untested.
-- Code 3 VBR is done. Still untested.
-- Testing and debugging against the standard test vectors coming next.
-- Fixed a few compiler warnings.
-- COMMIT
-
-2019/03/08 6:28 PM
-==================
-- Starting on range decoding. From figure 4 in RFC 6716 the diagram shows that both the SILK and CELT decoders draw
- their data from the Range Decoder. Therefore the Range Decoder needs to be figured out before SILK and CELT decoding.
- I have no prior knowledge on range coding, so hopefully RFC 6716 has enough information for a complete enough
- implementation for purpose of Opus decoding.
-- Section 4.1.1 of RGC 6716 is how to initialize the decoder. It references a variable called "rng", but doesn't
- specificy it's possible ranges. Setting to a 32-bit unsigned integer for now.
- - In this section, it says "or containing zero if there are no bytes in this Opus frame". I'm assuming we do this
- by looping over each frame in order...
- - Just noticed that the end of section 4.1.1 says "rng > 2**23". So "rng" needs to be 32-bits.
-- And now I've just noticed that section 4.1 explicitly says the following: "Both val and rng are 32-bit unsigned integer
- values." Problem solved.
-- Having read over the range coding stuff in RFC 6716 I had some difficulty figuring out where symbols frequencies are
- being stored. They're not stored, but rather specified in the specification in a per-context basis. In the spec there
- are references to "PDFs" which is like this "{f[0], f[1]}/ft". Example: "{1, 1}/2". Simple, now that I've finally
- figured it out...
-- I can therefore not do the range decoder independant of some higher level context. I will start with the SILK decoder
- since that's the next section...
-
-2019/03/09 6:52 AM
-==================
-- I think I'm getting a feel for the requirements for the range decoding API. I've decided for now to use a structure
- for this called dropus_range_decoder.
-- dropus_range_decoder_init() implemented based on section 4.1.1.
-- Added stub for dropus_stream_decode_frame(). I think the idea is that we iterate over each frame in the packet and
- decode them in order? Not fully sure yet how frame transitions will work, so may need to rethink this.
-- I'm still not sure if the range decoder is supposed to be initialized once per frame or once per packet...
-- After more testing it turns out my test program wasn't reporting errors properly. *Sigh*. These bugs were originating
- from the packet decoding section.
-- COMMIT
-- Section 4.1.2 mentions that there's two steps to decoding a symbol. The first step seems easy enough. Implemented in
- dropus_range_decoder_fs().
-- The second step requires updating "val" and "rng" of the range decoder based on (fl[k], fh[k] and ft). Implemented in
- dropus_range_decoder_update(pRangeDecoder, fl, fh, ft).
-- Normalization of the range decoder is easy (RFC 6716 - 4.1.2.1). Implemented in dropus_range_decoder_normalize().
-- I think at this point we have the foundations done for the range decoder. Still more to do for CELT, but I think I
- now have enough to hack away a bit more at the SILK-specific data.
-- COMMIT
-- I'm still not 100% sure how range decoding works - I know it depends on the context of the specific thing being
- decoded, but not sure how the index "k" is handled. The first part of the SILK frame is the VAD flags PDF={1,1}/2.
- Do I use the value of k (which I think is 0 or 1) to know whether or not the bit is set? I am going to assume this
- for now.
-
-2019/03/10 7:40 AM
-==================
-- At the moment I am using what feels like a very cumbersome way of decoding "k" from the range decoder. I'm calling
- dropus_range_decoder_fs(), followed by a function called dropus_range_decoder_k() to retrieve "k", and then
- dropus_range_decoder_update() at the end of it. I can simplify this into a single function, I think, which I'm
- calling dropus_range_decode() which will perform the "fs" decode, perform the update, then return "k".
-- As I'm writing dropus_range_decoder_decode() I've realized that I can simplify my dropus_range_decoder_update()
- function to do both the "k" lookup and the the update from section 4.1.2. It just needs to be changed to take
- the "context" probabilities and "fs" and to return "k".
-- This _seems_ to work. Or at least, the results are consistent with my first attempt. Committing.
-- Added dropus_Q13() for doing the stereo weight lookup (RFC 7616 - Table 7).
-- Section 4.2.7.1 mentions that the weights are linearly interpolated with the weights from the previous frame. What
- factor do I use for the interpolation?
-- Finished initial work on RFC 7616 - Section 4.2.7.1. Still don't know how the interpolation part works. I'm hoping
- this is explained later in the spec.
-- COMMIT
-- Completed initial implementation of RFC 7616 - Section 4.2.7.2.
-
-2020/03/29 6:33 AM
-==================
-- After a whole year, time to take another look at this. Only problem is, I've forgotten everything! Let's do a
- quick pass on the high level stuff to get it consistent with dr_flac. Two mistakes I made with dr_flac: Not having
- a low-level API that works on the FLAC frame level; and returning booleans instead of result codes. The first one
- is already being handled, but lets update the high level APIs to use result codes.
-- High level APIs now updated to return result codes.
-- People are going to want to log errors: added dropus_result_description() to retrieve a human readable description
- of a result code for logging and error reporting purposes.
-- I've had reports of people having compilation errors with inlining before. Need to bring over the INLINE macro from
- dr_flac to ensure we don't get more reports about that.
-- I've had people request the ability to customize whether or not public APIs are marked as static or extern in other
- libraries, so adding the DROPUS_API decoration. It's better to do this sooner than later, because this happened with
- miniaudio recently which was a ~40K line library at the time, and it was nightmare to update!
-- There will be a requirement to allocate memory for high level APIs. Support for allocation macros and allocation
- callbacks need to be added now so we can avoid breaking APIs later.
-- There have been requests in the past to support loading files from a wchar_t encoded path. Adding support by pulling
- in custom fopen() and wfopen() implementations from dr_flac.
-- Have not yet compiled on VC6 so lets do a pass on that to make it easier for us later.
-- VC6 is now compiling clean. Time to check GCC and Clang in strict C89 mode.
-- Getting compiler errors about some Linux specific code for handling byte swaps. Removed and replaced with a cross-
- platform solution.
-- From what I can see it looks like our boilerplate is now up to date with dr_flac, dr_wav and dr_mp3. Time to get onto
- some actual Opus decoding.
-- Looks like a typo in dropus_range_decoder_update() for handling the `fl[k] > 0` case. Changed this, but now getting
- a division by zero. Debugging...
- - And fixed. The last paragraph in RFC 6716 - Section 4.1.2 is the part I was missing:
- After the updates, implemented by ec_dec_update() (entdec.c), the
- decoder normalizes the range using the procedure in the next section,
- and returns the index k.
-- Reading through dropus_stream_decode_packet() to refresh my memory and some stuff needs cleaning up. Important from
- now on to distinguish between PCM frames and Opus frames. All variables referring to one of the other needs to be
- prefixed with "pcm" or "opus", depending on it's use. Example: opusFrameCount / pcmFrameCount.
-- Added infrastructure for handling CELT and Hybrid frames when the time comes. For now still focused on getting SILK
- frames working.
-- Reviewing SILK decoding logic to refresh my memory and looks like I've misinterpreted the encoding for per-frame
- LBRR flags. When there's more than one SILK frame, the first LBRR flag is just used to determine whether or not
- the per-frame LBRR flags is present. I was _always_ reading the LBRR flags regardless of the value of the primary
- LBRR flag.
-
-2020/03/30 7:30 AM
-==================
-- The decoding of SILK frames needs to be put in their own function in order to handle LBRR and regular SILK frames.
- Looking at the spec, there's some complicated branching logic for determine what needs to be decoded. I've put in
- a rough draft as a start, but needs some work. Will continue on this later.
\ No newline at end of file
diff --git a/source/engine/thirdparty/quickjs/.github/workflows/ci.yml b/source/engine/thirdparty/quickjs/.github/workflows/ci.yml
deleted file mode 100644
index 9eab46f..0000000
--- a/source/engine/thirdparty/quickjs/.github/workflows/ci.yml
+++ /dev/null
@@ -1,166 +0,0 @@
-name: ci
-
-on:
- pull_request:
- paths:
- - '**'
- - '!.gitignore'
- - '!LICENSE'
- - '!TODO'
- - '!doc/**'
- - '!examples/**'
- - '.github/workflows/ci.yml'
- push:
- branches:
- - '*'
-
-jobs:
- linux:
- name: Linux (Ubuntu)
- runs-on: ubuntu-latest
- strategy:
- fail-fast: false
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: true
- - name: Build
- run: |
- make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y
- - name: Stats
- run: |
- ./qjs -qd
- - name: Run built-in tests
- run: |
- make test
- - name: Run microbench
- run: |
- make microbench
-
- linux-asan:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: true
- - name: Build
- run: |
- make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y CONFIG_ASAN=y
- - name: Run built-in tests
- env:
- ASAN_OPTIONS: halt_on_error=1
- run: |
- make CONFIG_ASAN=y test
-
- linux-msan:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: true
- - name: Build
- env:
- CC: clang
- run: |
- make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y CONFIG_MSAN=y CONFIG_CLANG=y
- - name: Run built-in tests
- env:
- MSAN_OPTIONS: halt_on_error=1
- run: |
- make CONFIG_MSAN=y CONFIG_CLANG=y test
-
- linux-ubsan:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: true
- - name: Build
- run: |
- make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y CONFIG_UBSAN=y
- - name: Run built-in tests
- env:
- UBSAN_OPTIONS: halt_on_error=1
- run: |
- make CONFIG_UBSAN=y test
-
- macos:
- name: macOS
- runs-on: macos-latest
- strategy:
- fail-fast: false
- steps:
- - uses: actions/checkout@v4
- - name: Build
- run: |
- make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y
- - name: Stats
- run: |
- ./qjs -qd
- - name: Run built-in tests
- run: |
- make test
-
- macos-asan:
- runs-on: macos-latest
- steps:
- - uses: actions/checkout@v4
- - name: Build
- run: |
- make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y CONFIG_ASAN=y
- - name: Run built-in tests
- env:
- ASAN_OPTIONS: halt_on_error=1
- run: |
- make CONFIG_ASAN=y test
-
- macos-ubsan:
- runs-on: macos-latest
- steps:
- - uses: actions/checkout@v4
- - name: Build
- run: |
- make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y CONFIG_UBSAN=y
- - name: Run built-in tests
- env:
- UBSAN_OPTIONS: halt_on_error=1
- run: |
- make CONFIG_UBSAN=y test
-
- freebsd:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - name: Build + test
- uses: vmactions/freebsd-vm@v1
- with:
- usesh: true
- prepare: |
- pkg install -y gmake
- run: |
- gmake
- ./qjs -qd
- gmake test
-
- qemu-alpine:
- runs-on: ubuntu-latest
-
- strategy:
- fail-fast: false
- matrix:
- platform:
- - i386
- - arm32v6
- - arm32v7
- - arm64v8
- - s390x
-
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: recursive
- - name: Get qemu
- run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
- - name: Run tests on ${{ matrix.platform }}
- run: docker run --rm --interactive --mount type=bind,source=$(pwd),target=/host ${{ matrix.platform }}/alpine sh -c "apk add git patch make gcc libc-dev && cd /host && make test"
-
diff --git a/source/engine/thirdparty/quickjs/examples/fib.c b/source/engine/thirdparty/quickjs/examples/fib.c
deleted file mode 100644
index be90af5..0000000
--- a/source/engine/thirdparty/quickjs/examples/fib.c
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * QuickJS: Example of C module
- *
- * Copyright (c) 2017-2018 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "../quickjs.h"
-
-#define countof(x) (sizeof(x) / sizeof((x)[0]))
-
-static int fib(int n)
-{
- if (n <= 0)
- return 0;
- else if (n == 1)
- return 1;
- else
- return fib(n - 1) + fib(n - 2);
-}
-
-static JSValue js_fib(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- int n, res;
- if (JS_ToInt32(ctx, &n, argv[0]))
- return JS_EXCEPTION;
- res = fib(n);
- return JS_NewInt32(ctx, res);
-}
-
-static const JSCFunctionListEntry js_fib_funcs[] = {
- JS_CFUNC_DEF("fib", 1, js_fib ),
-};
-
-static int js_fib_init(JSContext *ctx, JSModuleDef *m)
-{
- return JS_SetModuleExportList(ctx, m, js_fib_funcs,
- countof(js_fib_funcs));
-}
-
-#ifdef JS_SHARED_LIBRARY
-#define JS_INIT_MODULE js_init_module
-#else
-#define JS_INIT_MODULE js_init_module_fib
-#endif
-
-JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
-{
- JSModuleDef *m;
- m = JS_NewCModule(ctx, module_name, js_fib_init);
- if (!m)
- return NULL;
- JS_AddModuleExportList(ctx, m, js_fib_funcs, countof(js_fib_funcs));
- return m;
-}
diff --git a/source/engine/thirdparty/quickjs/examples/fib_module.js b/source/engine/thirdparty/quickjs/examples/fib_module.js
deleted file mode 100644
index 6a81071..0000000
--- a/source/engine/thirdparty/quickjs/examples/fib_module.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/* fib module */
-export function fib(n)
-{
- if (n <= 0)
- return 0;
- else if (n == 1)
- return 1;
- else
- return fib(n - 1) + fib(n - 2);
-}
diff --git a/source/engine/thirdparty/quickjs/examples/hello.js b/source/engine/thirdparty/quickjs/examples/hello.js
deleted file mode 100644
index accefce..0000000
--- a/source/engine/thirdparty/quickjs/examples/hello.js
+++ /dev/null
@@ -1 +0,0 @@
-console.log("Hello World");
diff --git a/source/engine/thirdparty/quickjs/examples/hello_module.js b/source/engine/thirdparty/quickjs/examples/hello_module.js
deleted file mode 100644
index 463660f..0000000
--- a/source/engine/thirdparty/quickjs/examples/hello_module.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/* example of JS module */
-
-import { fib } from "./fib_module.js";
-
-console.log("Hello World");
-console.log("fib(10)=", fib(10));
diff --git a/source/engine/thirdparty/quickjs/examples/pi_bigdecimal.js b/source/engine/thirdparty/quickjs/examples/pi_bigdecimal.js
deleted file mode 100644
index 7cb7ad6..0000000
--- a/source/engine/thirdparty/quickjs/examples/pi_bigdecimal.js
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * PI computation in Javascript using the QuickJS bigdecimal type
- * (decimal floating point)
- */
-"use strict";
-
-/* compute PI with a precision of 'prec' digits */
-function calc_pi(prec) {
- const CHUD_A = 13591409m;
- const CHUD_B = 545140134m;
- const CHUD_C = 640320m;
- const CHUD_C3 = 10939058860032000m; /* C^3/24 */
- const CHUD_DIGITS_PER_TERM = 14.18164746272548; /* log10(C/12)*3 */
-
- /* return [P, Q, G] */
- function chud_bs(a, b, need_G) {
- var c, P, Q, G, P1, Q1, G1, P2, Q2, G2, b1;
- if (a == (b - 1n)) {
- b1 = BigDecimal(b);
- G = (2m * b1 - 1m) * (6m * b1 - 1m) * (6m * b1 - 5m);
- P = G * (CHUD_B * b1 + CHUD_A);
- if (b & 1n)
- P = -P;
- G = G;
- Q = b1 * b1 * b1 * CHUD_C3;
- } else {
- c = (a + b) >> 1n;
- [P1, Q1, G1] = chud_bs(a, c, true);
- [P2, Q2, G2] = chud_bs(c, b, need_G);
- P = P1 * Q2 + P2 * G1;
- Q = Q1 * Q2;
- if (need_G)
- G = G1 * G2;
- else
- G = 0m;
- }
- return [P, Q, G];
- }
-
- var n, P, Q, G;
- /* number of serie terms */
- n = BigInt(Math.ceil(prec / CHUD_DIGITS_PER_TERM)) + 10n;
- [P, Q, G] = chud_bs(0n, n, false);
- Q = BigDecimal.div(Q, (P + Q * CHUD_A),
- { roundingMode: "half-even",
- maximumSignificantDigits: prec });
- G = (CHUD_C / 12m) * BigDecimal.sqrt(CHUD_C,
- { roundingMode: "half-even",
- maximumSignificantDigits: prec });
- return Q * G;
-}
-
-(function() {
- var r, n_digits, n_bits;
- if (typeof scriptArgs != "undefined") {
- if (scriptArgs.length < 2) {
- print("usage: pi n_digits");
- return;
- }
- n_digits = scriptArgs[1] | 0;
- } else {
- n_digits = 1000;
- }
- /* we add more digits to reduce the probability of bad rounding for
- the last digits */
- r = calc_pi(n_digits + 20);
- print(r.toFixed(n_digits, "down"));
-})();
diff --git a/source/engine/thirdparty/quickjs/examples/pi_bigfloat.js b/source/engine/thirdparty/quickjs/examples/pi_bigfloat.js
deleted file mode 100644
index 8372379..0000000
--- a/source/engine/thirdparty/quickjs/examples/pi_bigfloat.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * PI computation in Javascript using the QuickJS bigfloat type
- * (binary floating point)
- */
-"use strict";
-
-/* compute PI with a precision of 'prec' bits */
-function calc_pi() {
- const CHUD_A = 13591409n;
- const CHUD_B = 545140134n;
- const CHUD_C = 640320n;
- const CHUD_C3 = 10939058860032000n; /* C^3/24 */
- const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */
-
- /* return [P, Q, G] */
- function chud_bs(a, b, need_G) {
- var c, P, Q, G, P1, Q1, G1, P2, Q2, G2;
- if (a == (b - 1n)) {
- G = (2n * b - 1n) * (6n * b - 1n) * (6n * b - 5n);
- P = BigFloat(G * (CHUD_B * b + CHUD_A));
- if (b & 1n)
- P = -P;
- G = BigFloat(G);
- Q = BigFloat(b * b * b * CHUD_C3);
- } else {
- c = (a + b) >> 1n;
- [P1, Q1, G1] = chud_bs(a, c, true);
- [P2, Q2, G2] = chud_bs(c, b, need_G);
- P = P1 * Q2 + P2 * G1;
- Q = Q1 * Q2;
- if (need_G)
- G = G1 * G2;
- else
- G = 0l;
- }
- return [P, Q, G];
- }
-
- var n, P, Q, G;
- /* number of serie terms */
- n = BigInt(Math.ceil(BigFloatEnv.prec / CHUD_BITS_PER_TERM)) + 10n;
- [P, Q, G] = chud_bs(0n, n, false);
- Q = Q / (P + Q * BigFloat(CHUD_A));
- G = BigFloat((CHUD_C / 12n)) * BigFloat.sqrt(BigFloat(CHUD_C));
- return Q * G;
-}
-
-(function() {
- var r, n_digits, n_bits;
- if (typeof scriptArgs != "undefined") {
- if (scriptArgs.length < 2) {
- print("usage: pi n_digits");
- return;
- }
- n_digits = scriptArgs[1];
- } else {
- n_digits = 1000;
- }
- n_bits = Math.ceil(n_digits * Math.log2(10));
- /* we add more bits to reduce the probability of bad rounding for
- the last digits */
- BigFloatEnv.setPrec( () => {
- r = calc_pi();
- print(r.toFixed(n_digits, BigFloatEnv.RNDZ));
- }, n_bits + 32);
-})();
diff --git a/source/engine/thirdparty/quickjs/examples/pi_bigint.js b/source/engine/thirdparty/quickjs/examples/pi_bigint.js
deleted file mode 100644
index e15abd1..0000000
--- a/source/engine/thirdparty/quickjs/examples/pi_bigint.js
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * PI computation in Javascript using the BigInt type
- */
-"use strict";
-
-/* return floor(log2(a)) for a > 0 and 0 for a = 0 */
-function floor_log2(a)
-{
- var k_max, a1, k, i;
- k_max = 0n;
- while ((a >> (2n ** k_max)) != 0n) {
- k_max++;
- }
- k = 0n;
- a1 = a;
- for(i = k_max - 1n; i >= 0n; i--) {
- a1 = a >> (2n ** i);
- if (a1 != 0n) {
- a = a1;
- k |= (1n << i);
- }
- }
- return k;
-}
-
-/* return ceil(log2(a)) for a > 0 */
-function ceil_log2(a)
-{
- return floor_log2(a - 1n) + 1n;
-}
-
-/* return floor(sqrt(a)) (not efficient but simple) */
-function int_sqrt(a)
-{
- var l, u, s;
- if (a == 0n)
- return a;
- l = ceil_log2(a);
- u = 1n << ((l + 1n) / 2n);
- /* u >= floor(sqrt(a)) */
- for(;;) {
- s = u;
- u = ((a / s) + s) / 2n;
- if (u >= s)
- break;
- }
- return s;
-}
-
-/* return pi * 2**prec */
-function calc_pi(prec) {
- const CHUD_A = 13591409n;
- const CHUD_B = 545140134n;
- const CHUD_C = 640320n;
- const CHUD_C3 = 10939058860032000n; /* C^3/24 */
- const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */
-
- /* return [P, Q, G] */
- function chud_bs(a, b, need_G) {
- var c, P, Q, G, P1, Q1, G1, P2, Q2, G2;
- if (a == (b - 1n)) {
- G = (2n * b - 1n) * (6n * b - 1n) * (6n * b - 5n);
- P = G * (CHUD_B * b + CHUD_A);
- if (b & 1n)
- P = -P;
- Q = b * b * b * CHUD_C3;
- } else {
- c = (a + b) >> 1n;
- [P1, Q1, G1] = chud_bs(a, c, true);
- [P2, Q2, G2] = chud_bs(c, b, need_G);
- P = P1 * Q2 + P2 * G1;
- Q = Q1 * Q2;
- if (need_G)
- G = G1 * G2;
- else
- G = 0n;
- }
- return [P, Q, G];
- }
-
- var n, P, Q, G;
- /* number of serie terms */
- n = BigInt(Math.ceil(Number(prec) / CHUD_BITS_PER_TERM)) + 10n;
- [P, Q, G] = chud_bs(0n, n, false);
- Q = (CHUD_C / 12n) * (Q << prec) / (P + Q * CHUD_A);
- G = int_sqrt(CHUD_C << (2n * prec));
- return (Q * G) >> prec;
-}
-
-function main(args) {
- var r, n_digits, n_bits, out;
- if (args.length < 1) {
- print("usage: pi n_digits");
- return;
- }
- n_digits = args[0] | 0;
-
- /* we add more bits to reduce the probability of bad rounding for
- the last digits */
- n_bits = BigInt(Math.ceil(n_digits * Math.log2(10))) + 32n;
- r = calc_pi(n_bits);
- r = ((10n ** BigInt(n_digits)) * r) >> n_bits;
- out = r.toString();
- print(out[0] + "." + out.slice(1));
-}
-
-var args;
-if (typeof scriptArgs != "undefined") {
- args = scriptArgs;
- args.shift();
-} else if (typeof arguments != "undefined") {
- args = arguments;
-} else {
- /* default: 1000 digits */
- args=[1000];
-}
-
-main(args);
diff --git a/source/engine/thirdparty/quickjs/examples/point.c b/source/engine/thirdparty/quickjs/examples/point.c
deleted file mode 100644
index 0386230..0000000
--- a/source/engine/thirdparty/quickjs/examples/point.c
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * QuickJS: Example of C module with a class
- *
- * Copyright (c) 2019 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "../quickjs.h"
-#include
-
-#define countof(x) (sizeof(x) / sizeof((x)[0]))
-
-/* Point Class */
-
-typedef struct {
- int x;
- int y;
-} JSPointData;
-
-static JSClassID js_point_class_id;
-
-static void js_point_finalizer(JSRuntime *rt, JSValue val)
-{
- JSPointData *s = JS_GetOpaque(val, js_point_class_id);
- /* Note: 's' can be NULL in case JS_SetOpaque() was not called */
- js_free_rt(rt, s);
-}
-
-static JSValue js_point_ctor(JSContext *ctx,
- JSValueConst new_target,
- int argc, JSValueConst *argv)
-{
- JSPointData *s;
- JSValue obj = JS_UNDEFINED;
- JSValue proto;
-
- s = js_mallocz(ctx, sizeof(*s));
- if (!s)
- return JS_EXCEPTION;
- if (JS_ToInt32(ctx, &s->x, argv[0]))
- goto fail;
- if (JS_ToInt32(ctx, &s->y, argv[1]))
- goto fail;
- /* using new_target to get the prototype is necessary when the
- class is extended. */
- proto = JS_GetPropertyStr(ctx, new_target, "prototype");
- if (JS_IsException(proto))
- goto fail;
- obj = JS_NewObjectProtoClass(ctx, proto, js_point_class_id);
- JS_FreeValue(ctx, proto);
- if (JS_IsException(obj))
- goto fail;
- JS_SetOpaque(obj, s);
- return obj;
- fail:
- js_free(ctx, s);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
-}
-
-static JSValue js_point_get_xy(JSContext *ctx, JSValueConst this_val, int magic)
-{
- JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id);
- if (!s)
- return JS_EXCEPTION;
- if (magic == 0)
- return JS_NewInt32(ctx, s->x);
- else
- return JS_NewInt32(ctx, s->y);
-}
-
-static JSValue js_point_set_xy(JSContext *ctx, JSValueConst this_val, JSValue val, int magic)
-{
- JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id);
- int v;
- if (!s)
- return JS_EXCEPTION;
- if (JS_ToInt32(ctx, &v, val))
- return JS_EXCEPTION;
- if (magic == 0)
- s->x = v;
- else
- s->y = v;
- return JS_UNDEFINED;
-}
-
-static JSValue js_point_norm(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id);
- if (!s)
- return JS_EXCEPTION;
- return JS_NewFloat64(ctx, sqrt((double)s->x * s->x + (double)s->y * s->y));
-}
-
-static JSClassDef js_point_class = {
- "Point",
- .finalizer = js_point_finalizer,
-};
-
-static const JSCFunctionListEntry js_point_proto_funcs[] = {
- JS_CGETSET_MAGIC_DEF("x", js_point_get_xy, js_point_set_xy, 0),
- JS_CGETSET_MAGIC_DEF("y", js_point_get_xy, js_point_set_xy, 1),
- JS_CFUNC_DEF("norm", 0, js_point_norm),
-};
-
-static int js_point_init(JSContext *ctx, JSModuleDef *m)
-{
- JSValue point_proto, point_class;
-
- /* create the Point class */
- JS_NewClassID(&js_point_class_id);
- JS_NewClass(JS_GetRuntime(ctx), js_point_class_id, &js_point_class);
-
- point_proto = JS_NewObject(ctx);
- JS_SetPropertyFunctionList(ctx, point_proto, js_point_proto_funcs, countof(js_point_proto_funcs));
-
- point_class = JS_NewCFunction2(ctx, js_point_ctor, "Point", 2, JS_CFUNC_constructor, 0);
- /* set proto.constructor and ctor.prototype */
- JS_SetConstructor(ctx, point_class, point_proto);
- JS_SetClassProto(ctx, js_point_class_id, point_proto);
-
- JS_SetModuleExport(ctx, m, "Point", point_class);
- return 0;
-}
-
-JSModuleDef *js_init_module(JSContext *ctx, const char *module_name)
-{
- JSModuleDef *m;
- m = JS_NewCModule(ctx, module_name, js_point_init);
- if (!m)
- return NULL;
- JS_AddModuleExport(ctx, m, "Point");
- return m;
-}
diff --git a/source/engine/thirdparty/quickjs/examples/test_fib.js b/source/engine/thirdparty/quickjs/examples/test_fib.js
deleted file mode 100644
index 70d26bd..0000000
--- a/source/engine/thirdparty/quickjs/examples/test_fib.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/* example of JS module importing a C module */
-
-import { fib } from "./fib.so";
-
-console.log("Hello World");
-console.log("fib(10)=", fib(10));
diff --git a/source/engine/thirdparty/quickjs/examples/test_point.js b/source/engine/thirdparty/quickjs/examples/test_point.js
deleted file mode 100644
index 0659bc3..0000000
--- a/source/engine/thirdparty/quickjs/examples/test_point.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/* example of JS module importing a C module */
-import { Point } from "./point.so";
-
-function assert(b, str)
-{
- if (b) {
- return;
- } else {
- throw Error("assertion failed: " + str);
- }
-}
-
-class ColorPoint extends Point {
- constructor(x, y, color) {
- super(x, y);
- this.color = color;
- }
- get_color() {
- return this.color;
- }
-};
-
-function main()
-{
- var pt, pt2;
-
- pt = new Point(2, 3);
- assert(pt.x === 2);
- assert(pt.y === 3);
- pt.x = 4;
- assert(pt.x === 4);
- assert(pt.norm() == 5);
-
- pt2 = new ColorPoint(2, 3, 0xffffff);
- assert(pt2.x === 2);
- assert(pt2.color === 0xffffff);
- assert(pt2.get_color() === 0xffffff);
-}
-
-main();
diff --git a/source/engine/thirdparty/sokol/bindgen/.gitignore b/source/engine/thirdparty/sokol/bindgen/.gitignore
deleted file mode 100644
index 4528e59..0000000
--- a/source/engine/thirdparty/sokol/bindgen/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-*.json
-*.nim
-*.zig
-__pycache__/
-sokol-nim/
-sokol-zig/
-sokol-odin/
-sokol-rust/
diff --git a/source/engine/thirdparty/sokol/bindgen/README.md b/source/engine/thirdparty/sokol/bindgen/README.md
deleted file mode 100644
index aef1ec1..0000000
--- a/source/engine/thirdparty/sokol/bindgen/README.md
+++ /dev/null
@@ -1,40 +0,0 @@
-## Language Binding Generation Scripts
-
-> REMINDER: we can pass `-fparse-all-comments` to the clang ast-dump command line which adds the following node types to the ast-dump.json: FullComment, ParagraphComment, TextComment. This might allow us to preserve comments in the language bindings (might be useful as part of a bigger change to make sokol header comments autodoc and Intellisense-friendly)
-
-### Zig
-
-First make sure that clang and python3 are in the path:
-
-```
-> clang --version
-> python3 --version
-```
-
-...on Windows I simply install those with scoop:
-
-```
-> scoop install llvm
-> scoop install python
-```
-
-To update the Zig bindings:
-
-```
-> cd sokol/bindgen
-> git clone https://github.com/floooh/sokol-zig
-> git clone https://github.com/floooh/sokol-nim
-> git clone https://github.com/floooh/sokol-odin
-> git clone https://github.com/floooh/sokol-rust
-> python3 gen_all.py
-```
-
-Test and run samples:
-
-```
-> cd sokol/bindgen/sokol-zig
-> zig build run-clear
-> zig build run-triangle
-> zig build run-cube
-...
-```
diff --git a/source/engine/thirdparty/sokol/bindgen/gen_all.py b/source/engine/thirdparty/sokol/bindgen/gen_all.py
deleted file mode 100644
index 24389e7..0000000
--- a/source/engine/thirdparty/sokol/bindgen/gen_all.py
+++ /dev/null
@@ -1,37 +0,0 @@
-import os, gen_nim, gen_zig, gen_odin, gen_rust
-
-tasks = [
- [ '../sokol_log.h', 'slog_', [] ],
- [ '../sokol_gfx.h', 'sg_', [] ],
- [ '../sokol_app.h', 'sapp_', [] ],
- [ '../sokol_glue.h', 'sglue_', ['sg_'] ],
- [ '../sokol_time.h', 'stm_', [] ],
- [ '../sokol_audio.h', 'saudio_', [] ],
- [ '../util/sokol_gl.h', 'sgl_', ['sg_'] ],
- [ '../util/sokol_debugtext.h', 'sdtx_', ['sg_'] ],
- [ '../util/sokol_shape.h', 'sshape_', ['sg_'] ],
-]
-
-# Odin
-gen_odin.prepare()
-for task in tasks:
- [c_header_path, main_prefix, dep_prefixes] = task
- gen_odin.gen(c_header_path, main_prefix, dep_prefixes)
-
-# Nim
-gen_nim.prepare()
-for task in tasks:
- [c_header_path, main_prefix, dep_prefixes] = task
- gen_nim.gen(c_header_path, main_prefix, dep_prefixes)
-
-# Zig
-gen_zig.prepare()
-for task in tasks:
- [c_header_path, main_prefix, dep_prefixes] = task
- gen_zig.gen(c_header_path, main_prefix, dep_prefixes)
-
-# Rust
-gen_rust.prepare()
-for task in tasks:
- [c_header_path, main_prefix, dep_prefixes] = task
- gen_rust.gen(c_header_path, main_prefix, dep_prefixes)
diff --git a/source/engine/thirdparty/sokol/bindgen/gen_ir.py b/source/engine/thirdparty/sokol/bindgen/gen_ir.py
deleted file mode 100644
index 0089ae2..0000000
--- a/source/engine/thirdparty/sokol/bindgen/gen_ir.py
+++ /dev/null
@@ -1,124 +0,0 @@
-#-------------------------------------------------------------------------------
-# Generate an intermediate representation of a clang AST dump.
-#-------------------------------------------------------------------------------
-import json, sys, subprocess
-
-def is_api_decl(decl, prefix):
- if 'name' in decl:
- return decl['name'].startswith(prefix)
- elif decl['kind'] == 'EnumDecl':
- # an anonymous enum, check if the items start with the prefix
- return decl['inner'][0]['name'].lower().startswith(prefix)
- else:
- return False
-
-def is_dep_decl(decl, dep_prefixes):
- for prefix in dep_prefixes:
- if is_api_decl(decl, prefix):
- return True
- return False
-
-def dep_prefix(decl, dep_prefixes):
- for prefix in dep_prefixes:
- if is_api_decl(decl, prefix):
- return prefix
- return None
-
-def filter_types(str):
- return str.replace('_Bool', 'bool')
-
-def parse_struct(decl):
- outp = {}
- outp['kind'] = 'struct'
- outp['name'] = decl['name']
- outp['fields'] = []
- for item_decl in decl['inner']:
- if item_decl['kind'] != 'FieldDecl':
- sys.exit(f"ERROR: Structs must only contain simple fields ({decl['name']})")
- item = {}
- if 'name' in item_decl:
- item['name'] = item_decl['name']
- item['type'] = filter_types(item_decl['type']['qualType'])
- outp['fields'].append(item)
- return outp
-
-def parse_enum(decl):
- outp = {}
- if 'name' in decl:
- outp['kind'] = 'enum'
- outp['name'] = decl['name']
- needs_value = False
- else:
- outp['kind'] = 'consts'
- needs_value = True
- outp['items'] = []
- for item_decl in decl['inner']:
- if item_decl['kind'] == 'EnumConstantDecl':
- item = {}
- item['name'] = item_decl['name']
- if 'inner' in item_decl:
- const_expr = item_decl['inner'][0]
- if const_expr['kind'] != 'ConstantExpr':
- sys.exit(f"ERROR: Enum values must be a ConstantExpr ({item_decl['name']}), is '{const_expr['kind']}'")
- if const_expr['valueCategory'] != 'rvalue' and const_expr['valueCategory'] != 'prvalue':
- sys.exit(f"ERROR: Enum value ConstantExpr must be 'rvalue' or 'prvalue' ({item_decl['name']}), is '{const_expr['valueCategory']}'")
- if not ((len(const_expr['inner']) == 1) and (const_expr['inner'][0]['kind'] == 'IntegerLiteral')):
- sys.exit(f"ERROR: Enum value ConstantExpr must have exactly one IntegerLiteral ({item_decl['name']})")
- item['value'] = const_expr['inner'][0]['value']
- if needs_value and 'value' not in item:
- sys.exit(f"ERROR: anonymous enum items require an explicit value")
- outp['items'].append(item)
- return outp
-
-def parse_func(decl):
- outp = {}
- outp['kind'] = 'func'
- outp['name'] = decl['name']
- outp['type'] = filter_types(decl['type']['qualType'])
- outp['params'] = []
- if 'inner' in decl:
- for param in decl['inner']:
- if param['kind'] != 'ParmVarDecl':
- print(f" >> warning: ignoring func {decl['name']} (unsupported parameter type)")
- return None
- outp_param = {}
- outp_param['name'] = param['name']
- outp_param['type'] = filter_types(param['type']['qualType'])
- outp['params'].append(outp_param)
- return outp
-
-def parse_decl(decl):
- kind = decl['kind']
- if kind == 'RecordDecl':
- return parse_struct(decl)
- elif kind == 'EnumDecl':
- return parse_enum(decl)
- elif kind == 'FunctionDecl':
- return parse_func(decl)
- else:
- return None
-
-def clang(csrc_path):
- cmd = ['clang', '-Xclang', '-ast-dump=json', '-c' ]
- cmd.append(csrc_path)
- return subprocess.check_output(cmd)
-
-def gen(header_path, source_path, module, main_prefix, dep_prefixes):
- ast = clang(source_path)
- inp = json.loads(ast)
- outp = {}
- outp['module'] = module
- outp['prefix'] = main_prefix
- outp['dep_prefixes'] = dep_prefixes
- outp['decls'] = []
- for decl in inp['inner']:
- is_dep = is_dep_decl(decl, dep_prefixes)
- if is_api_decl(decl, main_prefix) or is_dep:
- outp_decl = parse_decl(decl)
- if outp_decl is not None:
- outp_decl['is_dep'] = is_dep
- outp_decl['dep_prefix'] = dep_prefix(decl, dep_prefixes)
- outp['decls'].append(outp_decl)
- with open(f'{module}.json', 'w') as f:
- f.write(json.dumps(outp, indent=2));
- return outp
diff --git a/source/engine/thirdparty/sokol/bindgen/gen_nim.py b/source/engine/thirdparty/sokol/bindgen/gen_nim.py
deleted file mode 100644
index 700305a..0000000
--- a/source/engine/thirdparty/sokol/bindgen/gen_nim.py
+++ /dev/null
@@ -1,613 +0,0 @@
-#-------------------------------------------------------------------------------
-# Generate Nim bindings
-#
-# Nim coding style:
-# - type identifiers are PascalCase, everything else is camelCase
-# - reference: https://nim-lang.org/docs/nep1.html
-#-------------------------------------------------------------------------------
-import gen_ir
-import gen_util as util
-import os, shutil, sys
-
-module_names = {
- 'slog_': 'log',
- 'sg_': 'gfx',
- 'sapp_': 'app',
- 'stm_': 'time',
- 'saudio_': 'audio',
- 'sgl_': 'gl',
- 'sdtx_': 'debugtext',
- 'sshape_': 'shape',
- 'sglue_': 'glue',
-}
-
-c_source_paths = {
- 'slog_': 'sokol-nim/src/sokol/c/sokol_log.c',
- 'sg_': 'sokol-nim/src/sokol/c/sokol_gfx.c',
- 'sapp_': 'sokol-nim/src/sokol/c/sokol_app.c',
- 'stm_': 'sokol-nim/src/sokol/c/sokol_time.c',
- 'saudio_': 'sokol-nim/src/sokol/c/sokol_audio.c',
- 'sgl_': 'sokol-nim/src/sokol/c/sokol_gl.c',
- 'sdtx_': 'sokol-nim/src/sokol/c/sokol_debugtext.c',
- 'sshape_': 'sokol-nim/src/sokol/c/sokol_shape.c',
- 'sglue_': 'sokol-nim/src/sokol/c/sokol_glue.c',
-}
-
-c_callbacks = [
- 'slog_func',
-]
-
-ignores = [
- 'sdtx_printf',
- 'sdtx_vprintf',
-]
-
-overrides = {
- 'sgl_error': 'sgl_get_error',
- 'sgl_deg': 'sgl_as_degrees',
- 'sgl_rad': 'sgl_as_radians',
- 'SGL_NO_ERROR': 'SGL_ERROR_NO_ERROR',
- 'SG_BUFFERTYPE_VERTEXBUFFER': 'SG_BUFFERTYPE_VERTEX_BUFFER',
- 'SG_BUFFERTYPE_INDEXBUFFER': 'SG_BUFFERTYPE_INDEX_BUFFER',
- 'SG_ACTION_DONTCARE': 'SG_ACTION_DONT_CARE',
- 'ptr': 'addr', # range ptr
- 'func': 'fn',
- 'slog_func': 'fn',
-}
-
-enumPrefixOverrides = {
- # sokol_gfx.h
- 'LOADACTION': 'loadAction',
- 'STOREACTION': 'storeAction',
- 'PIXELFORMAT': 'pixelFormat',
- 'RESOURCESTATE': 'resourceState',
- 'BUFFERTYPE': 'bufferType',
- 'INDEXTYPE': 'indexType',
- 'IMAGETYPE': 'imageType',
- 'SAMPLERTYPE': 'samplerType',
- 'CUBEFACE': 'cubeFace',
- 'SHADERSTAGE': 'shaderStage',
- 'PRIMITIVETYPE': 'primitiveType',
- 'BORDERCOLOR': 'borderColor',
- 'VERTEXFORMAT': 'vertexFormat',
- 'VERTEXSTEP': 'vertexStep',
- 'UNIFORMTYPE': 'uniformType',
- 'UNIFORMLAYOUT': 'uniformLayout',
- 'CULLMODE': 'cullMode',
- 'FACEWINDING': 'faceWinding',
- 'COMPAREFUNC': 'compareFunc',
- 'STENCILOP': 'stencilOp',
- 'BLENDFACTOR': 'blendFactor',
- 'BLENDOP': 'blendOp',
- 'COLORMASK': 'colorMask',
- # sokol_app.h
- 'EVENTTYPE': 'eventType',
- 'KEYCODE': 'keyCode',
- 'MOUSEBUTTON': 'mouseButton',
-}
-
-prim_types = {
- 'int': 'int32',
- 'bool': 'bool',
- 'char': 'char',
- 'int8_t': 'int8',
- 'uint8_t': 'uint8',
- 'int16_t': 'int16',
- 'uint16_t': 'uint16',
- 'int32_t': 'int32',
- 'uint32_t': 'uint32',
- 'int64_t': 'int64',
- 'uint64_t': 'uint64',
- 'float': 'float32',
- 'double': 'float64',
- 'uintptr_t': 'uint',
- 'intptr_t': 'int',
- 'size_t': 'int', # not a bug, Nim's sizeof() returns int
-}
-
-prim_defaults = {
- 'int': '0',
- 'bool': 'false',
- 'int8_t': '0',
- 'uint8_t': '0',
- 'int16_t': '0',
- 'uint16_t': '0',
- 'int32_t': '0',
- 'uint32_t': '0',
- 'int64_t': '0',
- 'uint64_t': '0',
- 'float': '0.0f',
- 'double': '0.0',
- 'uintptr_t': '0',
- 'intptr_t': '0',
- 'size_t': '0'
-}
-
-common_prim_types = """
-array
-untyped typed void
-bool byte char
-int int8 int16 int32 int64
-uint uint8 uint16 uint32 uint64
-float float32 float64
-string
-cchar cint csize_t
-cfloat cdouble
-cstring
-pointer
-""".split()
-
-keywords = """
-addr and as asm
-bind block break
-case cast concept const continue converter
-defer discard distinct div do
-elif else end enum except export
-finally for from func
-if import in include interface is isnot iterator
-let
-macro method mixin mod
-nil not notin
-object of or out
-proc ptr
-raise ref return
-shl shr static
-template try tuple type
-using
-var
-when while
-xor
-yield
-""".split() + common_prim_types
-
-struct_types = []
-enum_types = []
-out_lines = ''
-
-def reset_globals():
- global struct_types
- global enum_types
- global out_lines
- struct_types = []
- enum_types = []
- out_lines = ''
-
-def l(s):
- global out_lines
- out_lines += s + '\n'
-
-def as_nim_prim_type(s):
- return prim_types[s]
-
-# prefix_bla_blub(_t) => (dep.)BlaBlub
-def as_nim_type_name(s, prefix):
- parts = s.lower().split('_')
- dep = parts[0] + '_'
- outp = ''
- if not s.startswith(prefix) and dep in module_names:
- outp = module_names[dep] + '.'
- for part in parts[1:]:
- # ignore '_t' type postfix
- if (part != 't'):
- outp += part.capitalize()
- return outp
-
-def check_override(name, default=None):
- if name in overrides:
- return overrides[name]
- elif default is None:
- return name
- else:
- return default
-
-def check_ignore(name):
- return name in ignores
-
-def is_power_of_two(val):
- return val == 0 or val & (val - 1) == 0
-
-def wrap_keywords(s):
- if s in keywords:
- return f'`{s}`'
- else:
- return s
-
-# prefix_bla_blub => blaBlub
-def as_camel_case(s, prefix, wrap=True):
- outp = s.lower()
- if outp.startswith(prefix):
- outp = outp[len(prefix):]
- parts = outp.lstrip('_').split('_')
- outp = parts[0]
- for part in parts[1:]:
- outp += part.capitalize()
- if wrap:
- outp = wrap_keywords(outp)
- return outp
-
-# PREFIX_ENUM_BLA_BLO => blaBlo
-def as_enum_item_name(s, wrap=True):
- outp = s.lstrip('_')
- parts = outp.split('_')[1:]
- if parts[0] in enumPrefixOverrides:
- parts[0] = enumPrefixOverrides[parts[0]]
- else:
- parts[0] = parts[0].lower()
- outp = parts[0]
- for part in parts[1:]:
- outp += part.capitalize()
- if wrap:
- outp = wrap_keywords(outp)
- return outp
-
-def is_prim_type(s):
- return s in prim_types
-
-def is_struct_type(s):
- return s in struct_types
-
-def is_enum_type(s):
- return s in enum_types
-
-def is_const_prim_ptr(s):
- for prim_type in prim_types:
- if s == f"const {prim_type} *":
- return True
- return False
-
-def is_prim_ptr(s):
- for prim_type in prim_types:
- if s == f"{prim_type} *":
- return True
- return False
-
-def is_const_struct_ptr(s):
- for struct_type in struct_types:
- if s == f"const {struct_type} *":
- return True
- return False
-
-def type_default_value(s):
- return prim_defaults[s]
-
-def funcptr_args(field_type, prefix):
- tokens = field_type[field_type.index('(*)')+4:-1].split(',')
- s = ""
- n = 0
- for token in tokens:
- n += 1
- arg_ctype = token.strip()
- if s != "":
- s += ", "
- arg_nimtype = as_nim_type(arg_ctype, prefix)
- if arg_nimtype == "":
- return "" # fun(void)
- s += f"a{n}:{arg_nimtype}"
- if s == "a1:void":
- s = ""
- return s
-
-def funcptr_result(field_type, prefix):
- ctype = field_type[:field_type.index('(*)')].strip()
- return as_nim_type(ctype, prefix)
-
-def as_nim_type(ctype, prefix, struct_ptr_as_value=False):
- if ctype == "void":
- return ""
- elif is_prim_type(ctype):
- return as_nim_prim_type(ctype)
- elif is_struct_type(ctype):
- return as_nim_type_name(ctype, prefix)
- elif is_enum_type(ctype):
- return as_nim_type_name(ctype, prefix)
- elif util.is_string_ptr(ctype):
- return "cstring"
- elif util.is_void_ptr(ctype) or util.is_const_void_ptr(ctype):
- return "pointer"
- elif is_const_struct_ptr(ctype):
- nim_type = as_nim_type(util.extract_ptr_type(ctype), prefix)
- if struct_ptr_as_value:
- return f"{nim_type}"
- else:
- return f"ptr {nim_type}"
- elif is_prim_ptr(ctype) or is_const_prim_ptr(ctype):
- return f"ptr {as_nim_type(util.extract_ptr_type(ctype), prefix)}"
- elif util.is_func_ptr(ctype):
- args = funcptr_args(ctype, prefix)
- res = funcptr_result(ctype, prefix)
- if res != "":
- res = ":" + res
- return f"proc({args}){res} {{.cdecl.}}"
- elif util.is_1d_array_type(ctype):
- array_ctype = util.extract_array_type(ctype)
- array_sizes = util.extract_array_sizes(ctype)
- return f'array[{array_sizes[0]}, {as_nim_type(array_ctype, prefix)}]'
- elif util.is_2d_array_type(ctype):
- array_ctype = util.extract_array_type(ctype)
- array_sizes = util.extract_array_sizes(ctype)
- return f'array[{array_sizes[0]}, array[{array_sizes[1]}, {as_nim_type(array_ctype, prefix)}]]'
- else:
- sys.exit(f"ERROR as_nim_type: {ctype}")
-
-def as_nim_struct_name(struct_decl, prefix):
- struct_name = check_override(struct_decl['name'])
- nim_type = f'{as_nim_type_name(struct_name, prefix)}'
- return nim_type
-
-def as_nim_field_name(field_decl, prefix, check_private=True):
- field_name = as_camel_case(check_override(field_decl['name']), prefix)
- if check_private:
- is_private = field_decl['name'].startswith('_')
- if not is_private:
- field_name += "*"
- return field_name
-
-def as_nim_field_type(struct_decl, field_decl, prefix):
- return as_nim_type(check_override(f"{struct_decl['name']}.{field_decl['name']}", default=field_decl['type']), prefix)
-
-def gen_struct(decl, prefix):
- l(f"type {as_nim_struct_name(decl, prefix)}* = object")
- for field in decl['fields']:
- l(f" {as_nim_field_name(field, prefix)}:{as_nim_field_type(decl, field, prefix)}")
- l("")
-
-def gen_consts(decl, prefix):
- l("const")
- for item in decl['items']:
- item_name = check_override(item['name'])
- l(f" {as_camel_case(item_name, prefix)}* = {item['value']}")
- l("")
-
-def gen_enum(decl, prefix):
- item_names_by_value = {}
- value = -1
- has_explicit_values = False
- for item in decl['items']:
- item_name = check_override(item['name'])
- if item_name.endswith("_NUM") or item_name.endswith("_FORCE_U32"):
- continue
- else:
- if 'value' in item:
- has_explicit_values = True
- value = int(item['value'])
- else:
- value += 1
- item_names_by_value[value] = as_enum_item_name(item_name)
- enum_name_nim = as_nim_type_name(decl['name'], prefix)
- l('type')
- l(f" {enum_name_nim}* {{.size:sizeof(int32).}} = enum")
- if has_explicit_values:
- # Nim requires explicit enum values to be declared in ascending order
- for value in sorted(item_names_by_value):
- name = item_names_by_value[value]
- l(f" {name} = {value},")
- else:
- for name in item_names_by_value.values():
- l(f" {name},")
- l("")
-
-# returns C prototype compatible function args (with pointers)
-def funcdecl_args_c(decl, prefix):
- s = ""
- func_name = decl['name']
- for param_decl in decl['params']:
- if s != "":
- s += ", "
- arg_name = param_decl['name']
- arg_type = check_override(f'{func_name}.{arg_name}', default=param_decl['type'])
- s += f"{as_camel_case(arg_name, prefix)}:{as_nim_type(arg_type, prefix)}"
- return s
-
-# returns Nim function args (pass structs by value)
-def funcdecl_args_nim(decl, prefix):
- s = ""
- func_name = decl['name']
- for param_decl in decl['params']:
- if s != "":
- s += ", "
- arg_name = param_decl['name']
- arg_type = check_override(f'{func_name}.{arg_name}', default=param_decl['type'])
- s += f"{as_camel_case(arg_name, prefix)}:{as_nim_type(arg_type, prefix, struct_ptr_as_value=True)}"
- return s
-
-def funcdecl_result(decl, prefix):
- func_name = decl['name']
- decl_type = decl['type']
- result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip())
- nim_res_type = as_nim_type(result_type, prefix)
- if nim_res_type == "":
- nim_res_type = "void"
- return nim_res_type
-
-def gen_func_nim(decl, prefix):
- c_func_name = decl['name']
- nim_func_name = as_camel_case(check_override(c_func_name), prefix, wrap=False)
- nim_res_type = funcdecl_result(decl, prefix)
- if c_func_name in c_callbacks:
- l(f"proc {nim_func_name}*({funcdecl_args_c(decl, prefix)}):{nim_res_type} {{.cdecl, importc:\"{c_func_name}\".}}")
- else:
- l(f"proc c_{nim_func_name}({funcdecl_args_c(decl, prefix)}):{nim_res_type} {{.cdecl, importc:\"{c_func_name}\".}}")
- l(f"proc {wrap_keywords(nim_func_name)}*({funcdecl_args_nim(decl, prefix)}):{nim_res_type} =")
- s = f" c_{nim_func_name}("
- for i, param_decl in enumerate(decl['params']):
- if i > 0:
- s += ", "
- arg_name = param_decl['name']
- arg_type = param_decl['type']
- if is_const_struct_ptr(arg_type):
- s += f"addr({arg_name})"
- else:
- s += arg_name
- s += ")"
- l(s)
- l("")
-
-def gen_array_converters(decl, prefix):
- for field in decl['fields']:
- if util.is_array_type(field['type']):
- array_type = util.extract_array_type(field['type'])
- array_sizes = util.extract_array_sizes(field['type'])
- struct_name = as_nim_struct_name(decl, prefix)
- field_name = as_nim_field_name(field, prefix, check_private=False)
- array_base_type = as_nim_type(array_type, prefix)
- if util.is_1d_array_type(field['type']):
- n = array_sizes[0]
- l(f'converter to{struct_name}{field_name}*[N:static[int]](items: array[N, {array_base_type}]): array[{n}, {array_base_type}] =')
- l(f' static: assert(N <= {n})')
- l(f' for index,item in items.pairs: result[index]=item')
- l('')
- elif util.is_2d_array_type(field['type']):
- x = array_sizes[1]
- y = array_sizes[0]
- l(f'converter to{struct_name}{field_name}*[Y:static[int], X:static[int]](items: array[Y, array[X, {array_base_type}]]): array[{y}, array[{x}, {array_base_type}]] =')
- l(f' static: assert(X <= {x})')
- l(f' static: assert(Y <= {y})')
- l(f' for indexY,itemY in items.pairs:')
- l(f' for indexX, itemX in itemY.pairs:')
- l(f' result[indexY][indexX] = itemX')
- l('')
- else:
- sys.exit('Unsupported converter array dimension (> 2)!')
-
-def pre_parse(inp):
- global struct_types
- global enum_types
- for decl in inp['decls']:
- kind = decl['kind']
- if kind == 'struct':
- struct_types.append(decl['name'])
- elif kind == 'enum':
- enum_name = decl['name']
- enum_types.append(enum_name)
-
-def gen_imports(inp, dep_prefixes):
- for dep_prefix in dep_prefixes:
- dep_module_name = module_names[dep_prefix]
- l(f'import {dep_module_name}')
- l('')
-
-def gen_extra(inp):
- if inp['prefix'] in ['sg_']:
- # FIXME: remove when sokol-shdc has been integrated!
- l('when defined gl:')
- l(' const gl* = true')
- l(' const d3d11* = false')
- l(' const metal* = false')
- l('elif defined windows:')
- l(' const gl* = false')
- l(' const d3d11* = true')
- l(' const metal* = false')
- l('elif defined macosx:')
- l(' const gl* = false')
- l(' const d3d11* = false')
- l(' const metal* = true')
- l('elif defined linux:')
- l(' const gl* = true')
- l(' const d3d11* = false')
- l(' const metal* = false')
- l('else:')
- l(' error("unsupported platform")')
- l('')
- if inp['prefix'] in ['sg_', 'sapp_']:
- l('when defined windows:')
- l(' when not defined vcc:')
- l(' {.passl:"-lkernel32 -luser32 -lshell32 -lgdi32".}')
- l(' when defined gl:')
- l(' {.passc:"-DSOKOL_GLCORE33".}')
- l(' else:')
- l(' {.passc:"-DSOKOL_D3D11".}')
- l(' when not defined vcc:')
- l(' {.passl:"-ld3d11 -ldxgi".}')
- l('elif defined macosx:')
- l(' {.passc:"-x objective-c".}')
- l(' {.passl:"-framework Cocoa -framework QuartzCore".}')
- l(' when defined gl:')
- l(' {.passc:"-DSOKOL_GLCORE33".}')
- l(' {.passl:"-framework OpenGL".}')
- l(' else:')
- l(' {.passc:"-DSOKOL_METAL".}')
- l(' {.passl:"-framework Metal -framework MetalKit".}')
- l('elif defined linux:')
- l(' {.passc:"-DSOKOL_GLCORE33".}')
- l(' {.passl:"-lX11 -lXi -lXcursor -lGL -lm -ldl -lpthread".}')
- l('else:')
- l(' error("unsupported platform")')
- l('')
- if inp['prefix'] in ['saudio_']:
- l('when defined windows:')
- l(' when not defined vcc:')
- l(' {.passl:"-lkernel32 -lole32".}')
- l('elif defined macosx:')
- l(' {.passl:"-framework AudioToolbox".}')
- l('elif defined linux:')
- l(' {.passl:"-lasound -lm -lpthread".}')
- l('else:')
- l(' error("unsupported platform")')
- l('')
- if inp['prefix'] in ['sg_']:
- l('## Convert a 4-element tuple of numbers to a gfx.Color')
- l('converter toColor*[R:SomeNumber,G:SomeNumber,B:SomeNumber,A:SomeNumber](rgba: tuple [r:R,g:G,b:B,a:A]):Color =')
- l(' Color(r:rgba.r.float32, g:rgba.g.float32, b:rgba.b.float32, a:rgba.a.float32)')
- l('')
- l('## Convert a 3-element tuple of numbers to a gfx.Color')
- l('converter toColor*[R:SomeNumber,G:SomeNumber,B:SomeNumber](rgba: tuple [r:R,g:G,b:B]):Color =')
- l(' Color(r:rgba.r.float32, g:rgba.g.float32, b:rgba.b.float32, a:1.float32)')
- l('')
- # NOTE: this simplistic to_Range() converter has various issues, some of them dangerous:
- # - doesn't work as expected for slice types
- # - it's very easy to create a range that points to invalid memory
- # (so far observed for stack-allocated structs <= 16 bytes)
- #if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_']:
- # l('# helper function to convert "anything" into a Range')
- # l('converter to_Range*[T](source: T): Range =')
- # l(' Range(addr: source.addr, size: source.sizeof.uint)')
- # l('')
- c_source_path = '/'.join(c_source_paths[inp['prefix']].split('/')[3:])
- l('{.passc:"-DSOKOL_NIM_IMPL".}')
- l('when defined(release):')
- l(' {.passc:"-DNDEBUG".}')
- l(f'{{.compile:"{c_source_path}".}}')
-
-def gen_module(inp, dep_prefixes):
- l('## machine generated, do not edit')
- l('')
- gen_imports(inp, dep_prefixes)
- pre_parse(inp)
- prefix = inp['prefix']
- for decl in inp['decls']:
- if not decl['is_dep']:
- kind = decl['kind']
- if kind == 'consts':
- gen_consts(decl, prefix)
- elif not check_ignore(decl['name']):
- if kind == 'struct':
- gen_struct(decl, prefix)
- gen_array_converters(decl, prefix)
- elif kind == 'enum':
- gen_enum(decl, prefix)
- elif kind == 'func':
- gen_func_nim(decl, prefix)
- gen_extra(inp)
-
-def prepare():
- print('=== Generating Nim bindings:')
- if not os.path.isdir('sokol-nim/src/sokol'):
- os.makedirs('sokol-nim/src/sokol')
- if not os.path.isdir('sokol-nim/src/sokol/c'):
- os.makedirs('sokol-nim/src/sokol/c')
-
-def gen(c_header_path, c_prefix, dep_c_prefixes):
- if not c_prefix in module_names:
- print(f' >> warning: skipping generation for {c_prefix} prefix...')
- return
- global out_lines
- module_name = module_names[c_prefix]
- c_source_path = c_source_paths[c_prefix]
- print(f' {c_header_path} => {module_name}')
- reset_globals()
- shutil.copyfile(c_header_path, f'sokol-nim/src/sokol/c/{os.path.basename(c_header_path)}')
- ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes)
- gen_module(ir, dep_c_prefixes)
- output_path = f"sokol-nim/src/sokol/{ir['module']}.nim"
- with open(output_path, 'w', newline='\n') as f_outp:
- f_outp.write(out_lines)
diff --git a/source/engine/thirdparty/sokol/bindgen/gen_odin.py b/source/engine/thirdparty/sokol/bindgen/gen_odin.py
deleted file mode 100644
index 6ba84f3..0000000
--- a/source/engine/thirdparty/sokol/bindgen/gen_odin.py
+++ /dev/null
@@ -1,500 +0,0 @@
-#-------------------------------------------------------------------------------
-# gen_odin.py
-#
-# Generate Odin bindings.
-#-------------------------------------------------------------------------------
-import gen_ir
-import gen_util as util
-import os, shutil, sys
-
-bindings_root = 'sokol-odin'
-c_root = f'{bindings_root}/c'
-module_root = f'{bindings_root}/sokol'
-
-module_names = {
- 'slog_': 'log',
- 'sg_': 'gfx',
- 'sapp_': 'app',
- 'stm_': 'time',
- 'saudio_': 'audio',
- 'sgl_': 'gl',
- 'sdtx_': 'debugtext',
- 'sshape_': 'shape',
- 'sglue_': 'glue',
-}
-
-system_libs = {
- 'sg_': {
- 'windows': {
- 'd3d11': "",
- 'gl': "",
- },
- 'macos': {
- 'metal': '"system:Cocoa.framework","system:QuartzCore.framework","system:Metal.framework","system:MetalKit.framework"',
- 'gl': '"system:Cocoa.framework","system:QuartzCore.framework","system:OpenGL.framework"'
- },
- 'linux': {
- 'gl': '"system:GL", "system:dl", "system:pthread"'
- }
- },
- 'sapp_': {
- 'windows': {
- 'd3d11': '',
- 'gl': '',
- },
- 'macos': {
- 'metal': '"system:Cocoa.framework","system:QuartzCore.framework","system:Metal.framework","system:MetalKit.framework"',
- 'gl': '"system:Cocoa.framework","system:QuartzCore.framework","system:OpenGL.framework"',
- },
- 'linux': {
- 'gl': '"system:X11", "system:Xi", "system:Xcursor", "system:GL", "system:dl", "system:pthread"'
- }
- },
- 'saudio_': {
- 'windows': {
- 'd3d11': '',
- 'gl': '',
- },
- 'macos': {
- 'metal': '"system:AudioToolbox.framework"',
- 'gl': '"system:AudioToolbox.framework"',
- },
- 'linux': {
- 'gl': '"system:asound", "system:dl", "system:pthread"',
- }
- }
-}
-
-c_source_names = {
- 'slog_': 'sokol_log.c',
- 'sg_': 'sokol_gfx.c',
- 'sapp_': 'sokol_app.c',
- 'sapp_sg': 'sokol_glue.c',
- 'stm_': 'sokol_time.c',
- 'saudio_': 'sokol_audio.c',
- 'sgl_': 'sokol_gl.c',
- 'sdtx_': 'sokol_debugtext.c',
- 'sshape_': 'sokol_shape.c',
- 'sglue_': 'sokol_glue.c',
-}
-
-ignores = [
- 'sdtx_printf',
- 'sdtx_vprintf',
- 'sg_install_trace_hooks',
- 'sg_trace_hooks',
-]
-
-# NOTE: syntax for function results: "func_name.RESULT"
-overrides = {
- 'context': 'ctx', # reserved keyword
- 'SGL_NO_ERROR': 'SGL_ERROR_NO_ERROR',
-}
-
-prim_types = {
- 'int': 'c.int',
- 'bool': 'bool',
- 'char': 'u8',
- 'int8_t': 'i8',
- 'uint8_t': 'u8',
- 'int16_t': 'i16',
- 'uint16_t': 'u16',
- 'int32_t': 'i32',
- 'uint32_t': 'u32',
- 'int64_t': 'i64',
- 'uint64_t': 'u64',
- 'float': 'f32',
- 'double': 'f64',
- 'uintptr_t': 'u64',
- 'intptr_t': 'i64',
- 'size_t': 'u64'
-}
-
-prim_defaults = {
- 'int': '0',
- 'bool': 'false',
- 'int8_t': '0',
- 'uint8_t': '0',
- 'int16_t': '0',
- 'uint16_t': '0',
- 'int32_t': '0',
- 'uint32_t': '0',
- 'int64_t': '0',
- 'uint64_t': '0',
- 'float': '0.0',
- 'double': '0.0',
- 'uintptr_t': '0',
- 'intptr_t': '0',
- 'size_t': '0'
-}
-
-struct_types = []
-enum_types = []
-enum_items = {}
-out_lines = ''
-
-def reset_globals():
- global struct_types
- global enum_types
- global enum_items
- global out_lines
- struct_types = []
- enum_types = []
- enum_items = {}
- out_lines = ''
-
-def l(s):
- global out_lines
- out_lines += s + '\n'
-
-def check_override(name, default=None):
- if name in overrides:
- return overrides[name]
- elif default is None:
- return name
- else:
- return default
-
-def check_ignore(name):
- return name in ignores
-
-# PREFIX_BLA_BLUB to BLA_BLUB, prefix_bla_blub to bla_blub
-def as_snake_case(s, prefix):
- outp = s
- if outp.lower().startswith(prefix):
- outp = outp[len(prefix):]
- return outp
-
-def get_odin_module_path(c_prefix):
- return f'{module_root}/{module_names[c_prefix]}'
-
-def get_csource_path(c_prefix):
- return f'{c_root}/{c_source_names[c_prefix]}'
-
-def make_odin_module_directory(c_prefix):
- path = get_odin_module_path(c_prefix)
- if not os.path.isdir(path):
- os.makedirs(path)
-
-def as_prim_type(s):
- return prim_types[s]
-
-# prefix_bla_blub(_t) => (dep.)Bla_Blub
-def as_struct_or_enum_type(s, prefix):
- parts = s.lower().split('_')
- outp = '' if s.startswith(prefix) else f'{parts[0]}.'
- for part in parts[1:]:
- # ignore '_t' type postfix
- if (part != 't'):
- outp += part.capitalize()
- outp += '_'
- outp = outp[:-1]
- return outp
-
-# PREFIX_ENUM_BLA_BLUB => BLA_BLUB, _PREFIX_ENUM_BLA_BLUB => BLA_BLUB
-def as_enum_item_name(s):
- outp = s.lstrip('_')
- parts = outp.split('_')[2:]
- outp = '_'.join(parts)
- if outp[0].isdigit():
- outp = '_' + outp
- return outp
-
-def enum_default_item(enum_name):
- return enum_items[enum_name][0]
-
-def is_prim_type(s):
- return s in prim_types
-
-def is_int_type(s):
- return s == "int"
-
-def is_struct_type(s):
- return s in struct_types
-
-def is_enum_type(s):
- return s in enum_types
-
-def is_const_prim_ptr(s):
- for prim_type in prim_types:
- if s == f"const {prim_type} *":
- return True
- return False
-
-def is_prim_ptr(s):
- for prim_type in prim_types:
- if s == f"{prim_type} *":
- return True
- return False
-
-def is_const_struct_ptr(s):
- for struct_type in struct_types:
- if s == f"const {struct_type} *":
- return True
- return False
-
-def type_default_value(s):
- return prim_defaults[s]
-
-def map_type(type, prefix, sub_type):
- if sub_type not in ['c_arg', 'odin_arg', 'struct_field']:
- sys.exit(f"Error: map_type(): unknown sub_type '{sub_type}")
- if type == "void":
- return ""
- elif is_prim_type(type):
- if sub_type == 'odin_arg':
- # for Odin args, maps C int (32-bit) to Odin int (pointer-sized),
- # and the C bool type to Odin's bool type
- if type == 'int' or type == 'uint32_t':
- return 'int'
- elif type == 'bool':
- return 'bool'
- return as_prim_type(type)
- elif is_struct_type(type):
- return as_struct_or_enum_type(type, prefix)
- elif is_enum_type(type):
- return as_struct_or_enum_type(type, prefix)
- elif util.is_void_ptr(type):
- return "rawptr"
- elif util.is_const_void_ptr(type):
- return "rawptr"
- elif util.is_string_ptr(type):
- return "cstring"
- elif is_const_struct_ptr(type):
- # pass Odin struct args by value, not by pointer
- if sub_type == 'odin_arg':
- return f"{as_struct_or_enum_type(util.extract_ptr_type(type), prefix)}"
- else:
- return f"^{as_struct_or_enum_type(util.extract_ptr_type(type), prefix)}"
- elif is_prim_ptr(type):
- return f"^{as_prim_type(util.extract_ptr_type(type))}"
- elif is_const_prim_ptr(type):
- return f"^{as_prim_type(util.extract_ptr_type(type))}"
- elif util.is_1d_array_type(type):
- array_type = util.extract_array_type(type)
- array_sizes = util.extract_array_sizes(type)
- return f"[{array_sizes[0]}]{map_type(array_type, prefix, sub_type)}"
- elif util.is_2d_array_type(type):
- array_type = util.extract_array_type(type)
- array_sizes = util.extract_array_sizes(type)
- return f"[{array_sizes[0]}][{array_sizes[1]}]{map_type(array_type, prefix, sub_type)}"
- elif util.is_func_ptr(type):
- res_type = funcptr_result_c(type, prefix)
- res_str = '' if res_type == '' else f' -> {res_type}'
- return f'proc "c" ({funcptr_args_c(type, prefix)}){res_str}'
- else:
- sys.exit(f"Error map_type(): unknown type '{type}'")
-
-def funcdecl_args_c(decl, prefix):
- s = ''
- func_name = decl['name']
- for param_decl in decl['params']:
- if s != '':
- s += ', '
- param_name = param_decl['name']
- param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type'])
- if is_const_struct_ptr(param_type):
- s += f"#by_ptr {param_name}: {map_type(param_type, prefix, 'odin_arg')}"
- elif is_int_type(param_type):
- s += f"#any_int {param_name}: {map_type(param_type, prefix, 'c_arg')}"
- else:
- s += f"{param_name}: {map_type(param_type, prefix, 'c_arg')}"
- return s
-
-def funcptr_args_c(field_type, prefix):
- tokens = field_type[field_type.index('(*)')+4:-1].split(',')
- s = ''
- arg_index = 0
- for token in tokens:
- arg_type = token.strip()
- if s != '':
- s += ', '
- c_arg = map_type(arg_type, prefix, 'c_arg')
- if c_arg == '':
- return ''
- else:
- s += f'a{arg_index}: {c_arg}'
- arg_index += 1
- return s
-
-def funcptr_result_c(field_type, prefix):
- res_type = field_type[:field_type.index('(*)')].strip()
- return map_type(res_type, prefix, 'c_arg')
-
-def funcdecl_result_c(decl, prefix):
- func_name = decl['name']
- decl_type = decl['type']
- res_c_type = decl_type[:decl_type.index('(')].strip()
- return map_type(check_override(f'{func_name}.RESULT', default=res_c_type), prefix, 'c_arg')
-
-def get_system_libs(module, platform, backend):
- if module in system_libs:
- if platform in system_libs[module]:
- if backend in system_libs[module][platform]:
- libs = system_libs[module][platform][backend]
- if libs != '':
- return f", {libs}"
- return ''
-
-def gen_c_imports(inp, c_prefix, prefix):
- clib_prefix = f'sokol_{inp["module"]}'
- clib_import = f'{clib_prefix}_clib'
- windows_d3d11_libs = get_system_libs(prefix, 'windows', 'd3d11')
- windows_gl_libs = get_system_libs(prefix, 'windows', 'gl')
- macos_metal_libs = get_system_libs(prefix, 'macos', 'metal')
- macos_gl_libs = get_system_libs(prefix, 'macos', 'gl')
- linux_gl_libs = get_system_libs(prefix, 'linux', 'gl')
- l( 'import "core:c"')
- l( 'when ODIN_OS == .Windows {')
- l( ' when #config(SOKOL_USE_GL,false) {')
- l(f' when ODIN_DEBUG == true {{ foreign import {clib_import} {{ "{clib_prefix}_windows_x64_gl_debug.lib"{windows_gl_libs} }} }}')
- l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_windows_x64_gl_release.lib"{windows_gl_libs} }} }}')
- l( ' } else {')
- l(f' when ODIN_DEBUG == true {{ foreign import {clib_import} {{ "{clib_prefix}_windows_x64_d3d11_debug.lib"{windows_d3d11_libs} }} }}')
- l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_windows_x64_d3d11_release.lib"{windows_d3d11_libs} }} }}')
- l( ' }')
- l( '} else when ODIN_OS == .Darwin {')
- l( ' when #config(SOKOL_USE_GL,false) {')
- l( ' when ODIN_ARCH == .arm64 {')
- l(f' when ODIN_DEBUG == true {{ foreign import {clib_import} {{ "{clib_prefix}_macos_arm64_gl_debug.a"{macos_gl_libs} }} }}')
- l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_macos_arm64_gl_release.a"{macos_gl_libs} }} }}')
- l( ' } else {')
- l(f' when ODIN_DEBUG == true {{ foreign import {clib_import} {{ "{clib_prefix}_macos_x64_gl_debug.a"{macos_gl_libs} }} }}')
- l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_macos_x64_gl_release.a"{macos_gl_libs} }} }}')
- l( ' }')
- l( ' } else {')
- l( ' when ODIN_ARCH == .arm64 {')
- l(f' when ODIN_DEBUG == true {{ foreign import {clib_import} {{ "{clib_prefix}_macos_arm64_metal_debug.a"{macos_metal_libs} }} }}')
- l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_macos_arm64_metal_release.a"{macos_metal_libs} }} }}')
- l( ' } else {')
- l(f' when ODIN_DEBUG == true {{ foreign import {clib_import} {{ "{clib_prefix}_macos_x64_metal_debug.a"{macos_metal_libs} }} }}')
- l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_macos_x64_metal_release.a"{macos_metal_libs} }} }}')
- l( ' }')
- l( ' }')
- l( '}')
- l( 'else {')
- l(f' when ODIN_DEBUG == true {{ foreign import {clib_import} {{ "{clib_prefix}_linux_x64_gl_debug.a"{linux_gl_libs} }} }}')
- l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_linux_x64_gl_release.a"{linux_gl_libs} }} }}')
- l( '}')
-
- # Need to special case sapp_sg to avoid Odin's context keyword
- if c_prefix == "sapp_sg":
- l(f'@(default_calling_convention="c")')
- else:
- l(f'@(default_calling_convention="c", link_prefix="{c_prefix}")')
- l(f"foreign {clib_import} {{")
- prefix = inp['prefix']
- for decl in inp['decls']:
- if decl['kind'] == 'func' and not decl['is_dep'] and not check_ignore(decl['name']):
- args = funcdecl_args_c(decl, prefix)
- res_type = funcdecl_result_c(decl, prefix)
- res_str = '' if res_type == '' else f'-> {res_type}'
- # Need to special case sapp_sg to avoid Odin's context keyword
- if c_prefix == "sapp_sg":
- l(f' @(link_name="{decl["name"]}")')
- l(f" {check_override(as_snake_case(decl['name'], c_prefix))} :: proc({args}) {res_str} ---")
- else:
- l(f" {as_snake_case(decl['name'], c_prefix)} :: proc({args}) {res_str} ---")
- l('}')
-
-def gen_consts(decl, prefix):
- for item in decl['items']:
- item_name = check_override(item['name'])
- l(f"{as_snake_case(item_name, prefix)} :: {item['value']}")
-
-def gen_struct(decl, prefix):
- c_struct_name = check_override(decl['name'])
- struct_name = as_struct_or_enum_type(c_struct_name, prefix)
- l(f'{struct_name} :: struct {{')
- for field in decl['fields']:
- field_name = check_override(field['name'])
- field_type = map_type(check_override(f'{c_struct_name}.{field_name}', default=field['type']), prefix, 'struct_field')
- # any field name starting with _ is considered private
- if field_name.startswith('_'):
- l(f' _ : {field_type},')
- else:
- l(f' {field_name} : {field_type},')
- l('}')
-
-def gen_enum(decl, prefix):
- enum_name = check_override(decl['name'])
- l(f'{as_struct_or_enum_type(enum_name, prefix)} :: enum i32 {{')
- for item in decl['items']:
- item_name = as_enum_item_name(check_override(item['name']))
- if item_name != 'FORCE_U32':
- if 'value' in item:
- l(f" {item_name} = {item['value']},")
- else:
- l(f" {item_name},")
- l('}')
-
-def gen_imports(dep_prefixes):
- for dep_prefix in dep_prefixes:
- dep_module_name = module_names[dep_prefix]
- l(f'import {dep_prefix[:-1]} "../{dep_module_name}"')
- l('')
-
-def gen_helpers(inp):
- if inp['prefix'] == 'sdtx_':
- l('import "core:fmt"')
- l('import "core:strings"')
- l('printf :: proc(s: string, args: ..any) {')
- l(' fstr := fmt.tprintf(s, ..args)')
- l(' putr(strings.unsafe_string_to_cstring(fstr), len(fstr))')
- l('}')
-
-def gen_module(inp, c_prefix, dep_prefixes):
- pre_parse(inp)
- l('// machine generated, do not edit')
- l('')
- l(f"package sokol_{inp['module']}")
- gen_imports(dep_prefixes)
- gen_helpers(inp)
- prefix = inp['prefix']
- gen_c_imports(inp, c_prefix, prefix)
- for decl in inp['decls']:
- if not decl['is_dep']:
- kind = decl['kind']
- if kind == 'consts':
- gen_consts(decl, prefix)
- elif not check_ignore(decl['name']):
- if kind == 'struct':
- gen_struct(decl, prefix)
- elif kind == 'enum':
- gen_enum(decl, prefix)
-
-def pre_parse(inp):
- global struct_types
- global enum_types
- for decl in inp['decls']:
- kind = decl['kind']
- if kind == 'struct':
- struct_types.append(decl['name'])
- elif kind == 'enum':
- enum_name = decl['name']
- enum_types.append(enum_name)
- enum_items[enum_name] = []
- for item in decl['items']:
- enum_items[enum_name].append(as_enum_item_name(item['name']))
-
-def prepare():
- print('=== Generating Odin bindings:')
- if not os.path.isdir(c_root):
- os.makedirs(c_root)
- if not os.path.isdir(module_root):
- os.makedirs(module_root)
-
-def gen(c_header_path, c_prefix, dep_c_prefixes):
- if not c_prefix in module_names:
- print(f' >> warning: skipping generation for {c_prefix} prefix...')
- return
- reset_globals()
- make_odin_module_directory(c_prefix)
- print(f' {c_header_path} => {module_names[c_prefix]}')
- shutil.copyfile(c_header_path, f'{c_root}/{os.path.basename(c_header_path)}')
- csource_path = get_csource_path(c_prefix)
- module_name = module_names[c_prefix]
- ir = gen_ir.gen(c_header_path, csource_path, module_name, c_prefix, dep_c_prefixes)
- gen_module(ir, c_prefix, dep_c_prefixes)
- with open(f"{module_root}/{ir['module']}/{ir['module']}.odin", 'w', newline='\n') as f_outp:
- f_outp.write(out_lines)
diff --git a/source/engine/thirdparty/sokol/bindgen/gen_rust.py b/source/engine/thirdparty/sokol/bindgen/gen_rust.py
deleted file mode 100644
index 0984d14..0000000
--- a/source/engine/thirdparty/sokol/bindgen/gen_rust.py
+++ /dev/null
@@ -1,893 +0,0 @@
-# -------------------------------------------------------------------------------
-# Generate rust bindings.
-#
-# rust coding style:
-# - types are PascalCase
-# - otherwise snake_case
-# -------------------------------------------------------------------------------
-import gen_ir
-import os, shutil, sys
-
-import gen_util as util
-
-module_names = {
- "slog_": "log",
- "sg_": "gfx",
- "sapp_": "app",
- "stm_": "time",
- "saudio_": "audio",
- "sgl_": "gl",
- "sdtx_": "debugtext",
- "sshape_": "shape",
- "simgui_": "imgui",
- "sglue_": "glue",
-}
-
-module_requires_rust_feature = {
- module_names["simgui_"]: "imgui",
-}
-
-c_source_paths = {
- "slog_": "sokol-rust/src/sokol/c/sokol_log.c",
- "sg_": "sokol-rust/src/sokol/c/sokol_gfx.c",
- "sapp_": "sokol-rust/src/sokol/c/sokol_app.c",
- "stm_": "sokol-rust/src/sokol/c/sokol_time.c",
- "saudio_": "sokol-rust/src/sokol/c/sokol_audio.c",
- "sgl_": "sokol-rust/src/sokol/c/sokol_gl.c",
- "sdtx_": "sokol-rust/src/sokol/c/sokol_debugtext.c",
- "sshape_": "sokol-rust/src/sokol/c/sokol_shape.c",
- "simgui_": "sokol-rust/src/sokol/c/sokol_imgui.c",
- "sglue_": "sokol-rust/src/sokol/c/sokol_glue.c",
-}
-
-ignores = [
- "sdtx_printf",
- "sdtx_vprintf",
- "simgui_add_key_event",
- # "sg_install_trace_hooks",
- # "sg_trace_hooks",
-]
-
-range_struct_name = "Range"
-
-# functions that need to be exposed as 'raw' C callbacks without a rust wrapper function
-c_callbacks = ["slog_func"]
-
-# NOTE: syntax for function results: "func_name.RESULT"
-overrides = {
- "type": "_type",
- "ref": "_ref",
-
- "sg_apply_uniforms.ub_index": "uintptr_t",
- "sg_draw.base_element": "uintptr_t",
- "sg_draw.num_elements": "uintptr_t",
- "sg_draw.num_instances": "uintptr_t",
- "sshape_element_range_t.base_element": "uintptr_t",
- "sshape_element_range_t.num_elements": "uintptr_t",
- "sdtx_font.font_index": "uintptr_t",
-
- "sdtx_move": "sdtx_move_cursor",
- "sdtx_move_x": "sdtx_move_cursor_x",
- "sdtx_move_y": "sdtx_move_cursor_y",
-
- "sg_image_type::SG_IMAGETYPE_2D": "SG_IMAGEYPE_DIM2",
- "sg_image_type::SG_IMAGETYPE_3D": "SG_IMAGETYPE_DIM3",
-
- "sapp_keycode::SAPP_KEYCODE_0": "SAPP_KEYCODE_NUM0",
- "sapp_keycode::SAPP_KEYCODE_1": "SAPP_KEYCODE_NUM1",
- "sapp_keycode::SAPP_KEYCODE_2": "SAPP_KEYCODE_NUM2",
- "sapp_keycode::SAPP_KEYCODE_3": "SAPP_KEYCODE_NUM3",
- "sapp_keycode::SAPP_KEYCODE_4": "SAPP_KEYCODE_NUM4",
- "sapp_keycode::SAPP_KEYCODE_5": "SAPP_KEYCODE_NUM5",
- "sapp_keycode::SAPP_KEYCODE_6": "SAPP_KEYCODE_NUM6",
- "sapp_keycode::SAPP_KEYCODE_7": "SAPP_KEYCODE_NUM7",
- "sapp_keycode::SAPP_KEYCODE_8": "SAPP_KEYCODE_NUM8",
- "sapp_keycode::SAPP_KEYCODE_9": "SAPP_KEYCODE_NUM9",
-
- # "sgl_error": "sgl_get_error", # 'error' is reserved in zig
- # "sgl_deg": "sgl_as_degrees",
- # "sgl_rad": "sgl_as_radians",
- # "sg_context_desc.color_format": "int",
- # "SGL_NO_ERROR": "SGL_ERROR_NO_ERROR",
- # "sg_context_desc.depth_format": "int",
-}
-
-prim_types = {
- "int": "i32",
- "bool": "bool",
- "char": "core::ffi::c_char",
- "int8_t": "i8",
- "uint8_t": "u8",
- "int16_t": "i16",
- "uint16_t": "u16",
- "int32_t": "i32",
- "uint32_t": "u32",
- "int64_t": "i64",
- "uint64_t": "u64",
- "float": "f32",
- "double": "f64",
- "uintptr_t": "usize",
- "intptr_t": "isize",
- "size_t": "usize",
-}
-
-prim_defaults = {
- "int": "0",
- "bool": "false",
- "int8_t": "0",
- "uint8_t": "0",
- "int16_t": "0",
- "uint16_t": "0",
- "int32_t": "0",
- "uint32_t": "0",
- "int64_t": "0",
- "uint64_t": "0",
- "float": "0.0",
- "double": "0.0",
- "uintptr_t": "0",
- "intptr_t": "0",
- "size_t": "0",
- "char": "0",
-}
-
-special_constant_types = {
- "SG_INVALID_ID": "u32",
- "SAPP_MODIFIER_SHIFT": "u32",
- "SAPP_MODIFIER_CTRL": "u32",
- "SAPP_MODIFIER_ALT": "u32",
- "SAPP_MODIFIER_SUPER": "u32",
- "SAPP_MODIFIER_LMB": "u32",
- "SAPP_MODIFIER_RMB": "u32",
- "SAPP_MODIFIER_MMB": "u32",
-}
-
-struct_types = []
-enum_types = []
-enum_items = {}
-out_lines = ""
-
-
-def reset_globals():
- global struct_types
- global enum_types
- global enum_items
- global out_lines
- struct_types = []
- enum_types = []
- enum_items = {}
- out_lines = ""
-
-
-def l(s):
- global out_lines
- out_lines += s + "\n"
-
-
-def as_rust_prim_type(s):
- return prim_types[s]
-
-
-def as_upper_snake_case(s, prefix):
- outp = s.lower()
- if outp.startswith(prefix):
- outp = outp[len(prefix):]
- return outp.upper()
-
-
-# prefix_bla_blub(_t) => (dep::)BlaBlub
-def as_rust_struct_type(s, prefix):
- parts = s.lower().split("_")
- outp = "" if s.startswith(prefix) else f"{parts[0]}::"
- for part in parts[1:]:
- # ignore '_t' type postfix
- if part != "t":
- outp += part.capitalize()
- return outp
-
-
-# prefix_bla_blub(_t) => (dep::)BlaBlub
-def as_rust_enum_type(s, prefix):
- parts = s.lower().split("_")
- outp = "" if s.startswith(prefix) else f"{parts[0]}::"
- for part in parts[1:]:
- # ignore '_t' type postfix
- if part != "t":
- outp += part.capitalize()
- return outp
-
-
-def check_override(name, default=None):
- if name in overrides:
- return overrides[name]
- elif default is None:
- return name
- else:
- return default
-
-
-def check_ignore(name):
- return name in ignores
-
-
-# PREFIX_ENUM_BLA_BLA => BlaBla, _PREFIX_ENUM_BLA_BLA => BlaBla
-def as_enum_item_name(s):
- parts = s.lstrip("_").split("_")
- outp = ""
- for i, part in enumerate(parts[2:]):
- # TODO: What to do with enum fields starting with numbers?
- outp += part.capitalize()
-
- return outp
-
-
-def enum_default_item(enum_name):
- return enum_items[enum_name][0]
-
-
-def is_prim_type(s):
- return s in prim_types
-
-
-def is_struct_type(s):
- return s in struct_types
-
-
-def is_enum_type(s):
- return s in enum_types
-
-
-def is_const_prim_ptr(s):
- for prim_type in prim_types:
- if s == f"const {prim_type} *":
- return True
- return False
-
-
-def is_prim_ptr(s):
- for prim_type in prim_types:
- if s == f"{prim_type} *":
- return True
- return False
-
-
-def is_const_struct_ptr(s):
- for struct_type in struct_types:
- if s == f"const {struct_type} *":
- return True
- return False
-
-
-def is_struct_ptr(s):
- for struct_type in struct_types:
- if s == f"{struct_type} *":
- return True
- return False
-
-
-def type_default_value(s):
- return prim_defaults[s]
-
-
-def as_c_arg_type(arg_prefix, arg_type, prefix):
- # NOTE: if arg_prefix is None, the result is used as return value
- pre = "" if arg_prefix is None else arg_prefix
-
- if arg_type == "void":
- return ""
- elif is_prim_type(arg_type):
- return pre + as_rust_prim_type(arg_type)
- elif is_struct_type(arg_type):
- return pre + as_rust_struct_type(arg_type, prefix)
- elif is_enum_type(arg_type):
- return pre + as_rust_enum_type(arg_type, prefix)
- elif util.is_void_ptr(arg_type):
- return pre + "*mut core::ffi::c_void"
- elif util.is_const_void_ptr(arg_type):
- return pre + "*const core::ffi::c_void"
- elif util.is_string_ptr(arg_type):
- return pre + "*const core::ffi::c_char"
- elif is_const_struct_ptr(arg_type):
- return pre + f"*const {as_rust_struct_type(util.extract_ptr_type(arg_type), prefix)}"
- elif is_struct_ptr(arg_type):
- return pre + f"*mut {as_rust_struct_type(util.extract_ptr_type(arg_type), prefix)}"
- elif is_prim_ptr(arg_type):
- return pre + f"*mut {as_rust_prim_type(util.extract_ptr_type(arg_type))}"
- elif is_const_prim_ptr(arg_type):
- return pre + f"*const {as_rust_prim_type(util.extract_ptr_type(arg_type))}"
- else:
- sys.exit(f"ERROR as_c_arg_type(): {arg_type}")
-
-
-def as_rust_arg_type(arg_prefix, arg_type, prefix):
- # NOTE: if arg_prefix is None, the result is used as return value
- pre = "" if arg_prefix is None else arg_prefix
-
- if arg_type == "void":
- return ""
- elif is_prim_type(arg_type):
- return pre + as_rust_prim_type(arg_type)
- elif is_struct_type(arg_type):
- return pre + as_rust_struct_type(arg_type, prefix)
- elif is_enum_type(arg_type):
- return pre + as_rust_enum_type(arg_type, prefix)
- elif util.is_void_ptr(arg_type):
- return pre + "*mut core::ffi::c_void"
- elif util.is_const_void_ptr(arg_type):
- return pre + "*const core::ffi::c_void"
- elif util.is_string_ptr(arg_type):
- return pre + "&str"
- elif is_const_struct_ptr(arg_type):
- return pre + f"&{as_rust_struct_type(util.extract_ptr_type(arg_type), prefix)}"
- elif is_struct_ptr(arg_type):
- return pre + f"&mut {as_rust_struct_type(util.extract_ptr_type(arg_type), prefix)}"
- elif is_prim_ptr(arg_type):
- return pre + f"&mut {as_rust_prim_type(util.extract_ptr_type(arg_type))}"
- elif is_const_prim_ptr(arg_type):
- return pre + f"&{as_rust_prim_type(util.extract_ptr_type(arg_type))}"
- else:
- sys.exit(f"ERROR as_rust_arg_type(): {arg_type}")
-
-
-def is_rust_string(rust_type):
- return rust_type == "&str" or rust_type == " -> &'static str"
-
-
-# get C-style arguments of a function pointer as string
-def funcptr_args_c(field_type, prefix):
- tokens = field_type[field_type.index("(*)") + 4: -1].split(",")
- s = ""
- for token in tokens:
- arg_type = token.strip()
- if s != "":
- s += ", "
- c_arg = as_c_arg_type(None, arg_type, prefix)
- if c_arg == "void":
- return ""
- else:
- s += c_arg
- return s
-
-
-# get C-style result of a function pointer as string
-def funcptr_result_c(field_type):
- res_type = field_type[: field_type.index("(*)")].strip()
- if res_type == "void":
- return ""
- elif is_prim_type(res_type):
- return f" -> {as_rust_prim_type(res_type)}"
- elif util.is_const_void_ptr(res_type):
- return " -> *const core::ffi::c_void"
- elif util.is_void_ptr(res_type):
- return " -> *mut core::ffi::c_void"
- else:
- sys.exit(f"ERROR funcptr_result_c(): {field_type}")
-
-
-def funcdecl_args_c(decl, prefix):
- s = ""
- func_name = decl["name"]
- for param_decl in decl["params"]:
- if s != "":
- s += ", "
- param_name = param_decl["name"]
- param_type = check_override(
- f"{func_name}.{param_name}", default=param_decl["type"]
- )
- s += f"{as_c_arg_type(f'{param_name}: ', param_type, prefix)}"
- return s
-
-
-def funcdecl_args_rust(decl, prefix):
- s = ""
- func_name = decl["name"]
- for param_decl in decl["params"]:
- if s != "":
- s += ", "
- param_name = param_decl["name"]
- param_type = check_override(
- f"{func_name}.{param_name}", default=param_decl["type"]
- )
- s += f"{as_rust_arg_type(f'{param_name}: ', param_type, prefix)}"
- return s
-
-
-def funcdecl_result_c(decl, prefix):
- func_name = decl["name"]
- decl_type = decl["type"]
- result_type = check_override(
- f"{func_name}.RESULT", default=decl_type[: decl_type.index("(")].strip()
- )
-
- it = as_c_arg_type(None, result_type, prefix)
- if it == "()" or it == "":
- return ""
- else:
- return f" -> {it}"
-
-
-def funcdecl_result_rust(decl, prefix):
- func_name = decl["name"]
- decl_type = decl["type"]
- result_type = check_override(
- f"{func_name}.RESULT", default=decl_type[: decl_type.index("(")].strip()
- )
- rust_res_type = as_rust_arg_type(None, result_type, prefix)
-
- if is_rust_string(rust_res_type):
- rust_res_type = "&'static str"
-
- if rust_res_type == "":
- return ""
- else:
- return f" -> {rust_res_type }"
-
-
-def gen_struct(decl, prefix):
- struct_name = check_override(decl["name"])
- rust_type = as_rust_struct_type(struct_name, prefix)
- rust_struct_type = rust_type
-
- struct_lines = []
- default_lines = []
-
- for field in decl["fields"]:
- field_name = check_override(field["name"])
- field_type = check_override(
- f"{struct_name}.{field_name}", default=field["type"]
- )
-
- if is_prim_type(field_type):
- struct_lines.append(
- f"pub {field_name}: {as_rust_prim_type(field_type)}"
- )
- default_lines.append(
- f"{field_name}: {type_default_value(field_type)}"
- )
- elif is_struct_type(field_type):
- struct_lines.append(
- f"pub {field_name}: {as_rust_struct_type(field_type, prefix)}"
- )
- default_lines.append(
- f"{field_name}: {as_rust_struct_type(field_type, prefix)}::new()"
- )
- elif is_enum_type(field_type):
- struct_lines.append(
- f"pub {field_name}: {as_rust_enum_type(field_type, prefix)}"
- )
- default_lines.append(
- f"{field_name}: {as_rust_enum_type(field_type, prefix)}::new()"
- )
- elif util.is_string_ptr(field_type):
- struct_lines.append(
- f"pub {field_name}: *const core::ffi::c_char"
- )
- default_lines.append(
- f"{field_name}: core::ptr::null()"
- )
- elif util.is_const_void_ptr(field_type):
- struct_lines.append(
- f"pub {field_name}: *const core::ffi::c_void"
- )
- default_lines.append(
- f"{field_name}: core::ptr::null()"
- )
- elif util.is_void_ptr(field_type):
- struct_lines.append(
- f"pub {field_name}: *mut core::ffi::c_void"
- )
- default_lines.append(
- f"{field_name}: core::ptr::null_mut()"
- )
- elif is_const_prim_ptr(field_type):
- struct_lines.append(
- f"pub {field_name}: *const {as_rust_prim_type(util.extract_ptr_type(field_type))}"
- )
- default_lines.append(
- f"{field_name}: core::ptr::null()"
- )
- elif is_prim_ptr(field_type):
- struct_lines.append(
- f"pub {field_name}: *mut {as_rust_prim_type(util.extract_ptr_type(field_type))}"
- )
- default_lines.append(
- f"{field_name}: core::ptr::null_mut()"
- )
- elif is_const_struct_ptr(field_type):
- struct_lines.append(
- f"pub {field_name}: *const {as_rust_struct_type(util.extract_ptr_type(field_type), prefix)}"
- )
- default_lines.append(
- f"{field_name}: core::ptr::null()"
- )
- elif is_struct_ptr(field_type):
- struct_lines.append(
- f"pub {field_name}: *mut {as_rust_struct_type(util.extract_ptr_type(field_type), prefix)}"
- )
- default_lines.append(
- f"{field_name}: core::ptr::null_mut()"
- )
- elif util.is_func_ptr(field_type):
- struct_lines.append(
- f"pub {field_name}: Option"
- )
- default_lines.append(
- f"{field_name}: None"
- )
- elif util.is_1d_array_type(field_type):
- array_type = util.extract_array_type(field_type)
- array_sizes = util.extract_array_sizes(field_type)
- if is_prim_type(array_type) or is_struct_type(array_type):
- if is_prim_type(array_type):
- rust_type = as_rust_prim_type(array_type)
- def_val = type_default_value(array_type)
- elif is_struct_type(array_type) or is_enum_type(array_type):
- rust_type = as_rust_struct_type(array_type, prefix)
- def_val = f"{rust_type}::new()"
- else:
- sys.exit(f"ERROR gen_struct is_1d_array_type: {array_type}")
- t0 = f"[{rust_type}; {array_sizes[0]}]"
- # t1 = f"&{rust_type}"
- struct_lines.append(
- f"pub {field_name}: {t0}"
- )
- default_lines.append(
- f"{field_name}: [{def_val}; {array_sizes[0]}]"
- )
- elif util.is_const_void_ptr(array_type):
- struct_lines.append(
- f"pub {field_name}: [*const core::ffi::c_void; {array_sizes[0]}]"
- )
- default_lines.append(
- f"{field_name}: [core::ptr::null(); {array_sizes[0]}]"
- )
- else:
- sys.exit(
- f"ERROR gen_struct: array {field_name}: {field_type} => [{array_type}: {array_sizes[0]}]"
- )
- elif util.is_2d_array_type(field_type):
- array_type = util.extract_array_type(field_type)
- array_sizes = util.extract_array_sizes(field_type)
-
- if is_prim_type(array_type):
- rust_type = as_rust_prim_type(array_type)
- def_val = type_default_value(array_type)
- elif is_struct_type(array_type):
- rust_type = as_rust_struct_type(array_type, prefix)
- def_val = f"{rust_type}::new()"
- else:
- sys.exit(f"ERROR gen_struct is_2d_array_type: {array_type}")
-
- struct_lines.append(
- f"pub {field_name}: [[{rust_type}; {array_sizes[1]}]; {array_sizes[0]}]"
- )
- default_lines.append(
- f"{field_name}: [[{def_val}; {array_sizes[1]}]; {array_sizes[0]}]"
- )
- else:
- sys.exit(f"ERROR gen_struct: {field_name}: {field_type};")
-
- #
- # TODO: Is this the best way to have zero-initialization with support for constants?
- # core::mem::zeroed() cleaner?
- #
-
- l("#[repr(C)]")
- l("#[derive(Copy, Clone, Debug)]")
- l(f"pub struct {rust_struct_type} {{")
- for line in struct_lines:
- l(f" {line},")
- l("}")
-
- l(f"impl {rust_struct_type} {{")
- l(" pub const fn new() -> Self {")
- l(" Self {")
- for line in default_lines:
- l(f" {line},")
- l(" }")
- l(" }")
- l("}")
-
- l(f"impl Default for {rust_struct_type} {{")
- l(" fn default() -> Self {")
- l(" Self::new()")
- l(" }")
- l("}")
-
-
-def gen_consts(decl, prefix):
- for item in decl["items"]:
- #
- # TODO: What type should these constants have? Currently giving all `usize`
- # unless specifically overridden by `special_constant_types`
- #
-
- item_name = check_override(item["name"])
- if item_name in special_constant_types:
- special_type = special_constant_types[item_name]
- l(f"pub const {as_upper_snake_case(item_name, prefix)}: {special_type} = {item['value']};")
- else:
- l(f"pub const {as_upper_snake_case(item_name, prefix)}: usize = {item['value']};")
-
-
-def gen_enum(decl, prefix):
- enum_name = check_override(decl["name"])
-
- names = [
- as_enum_item_name(check_override(f"{decl['name']}::{item['name']}", item['name'])) for item in decl["items"]
- ]
-
- is_u32 = False
- for name in names:
- if name == "ForceU32":
- is_u32 = True
- break
-
- l("#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]")
- if is_u32:
- l("#[repr(u32)]")
- else:
- l("#[repr(i32)]")
-
- rust_enum_name = as_rust_enum_type(enum_name, prefix)
-
- l(f"pub enum {rust_enum_name} {{")
- for item_name, item in zip(names, decl["items"]):
- if item_name != "ForceU32":
- if "value" in item:
- l(f" {item_name} = {item['value']},")
- else:
- l(f" {item_name},")
- l("}")
-
- default_item = enum_default_item(enum_name)
- l(f"impl {rust_enum_name} {{")
- l(" pub const fn new() -> Self {")
- l(f" Self::{default_item}")
- l(" }")
- l("}")
-
- l(f"impl Default for {rust_enum_name} {{")
- l(" fn default() -> Self {")
- l(f" Self::{default_item}")
- l(" }")
- l("}")
-
-
-def gen_func_c(decl, prefix):
- l("pub extern \"C\" {")
- l(f" fn {decl['name']}({funcdecl_args_c(decl, prefix)}){funcdecl_result_c(decl, prefix)};")
- l("}")
-
-
-def gen_c_funcs(funcs):
- l("pub mod ffi {")
- l(" #![allow(unused_imports)]")
- l(" use super::*;")
- l(" extern \"C\" {")
- for decl, prefix in funcs:
- l(f" pub fn {decl['name']}({funcdecl_args_c(decl, prefix)}){funcdecl_result_c(decl, prefix)};")
- l(" }")
- l("}")
-
-
-def gen_rust_funcs(funcs):
- for decl, prefix in funcs:
- gen_func_rust(decl, prefix)
-
-
-def gen_func_rust(decl, prefix):
- c_func_name = decl["name"]
- rust_func_name = util.as_lower_snake_case(check_override(decl["name"]), prefix)
- rust_res_type = funcdecl_result_rust(decl, prefix)
-
- if c_func_name in c_callbacks:
- c_res_type = funcdecl_result_c(decl, prefix)
- l("#[inline]")
- l(f'pub extern "C" fn {c_func_name}({funcdecl_args_c(decl, prefix)}){c_res_type} {{')
- l(" unsafe {")
- s = f" ffi::{c_func_name}("
- for i, param_decl in enumerate(decl["params"]):
- if i > 0:
- s += ", "
- arg_name = param_decl["name"]
- s += arg_name
- s += ")"
- l(s)
- l(" }")
- l("}")
- else:
- l("#[inline]")
- l(f"pub fn {rust_func_name}({funcdecl_args_rust(decl, prefix)}){rust_res_type} {{")
- for i, param_decl in enumerate(decl["params"]):
- arg_name = param_decl["name"]
- arg_type = param_decl["type"]
- if util.is_string_ptr(arg_type):
- l(f" let tmp_{i} = std::ffi::CString::new({arg_name}).unwrap();")
-
- l(" unsafe {")
- if is_rust_string(rust_res_type):
- # special case: convert C string to rust string slice
- s = f" c_char_ptr_to_rust_str(ffi::{c_func_name}("
- else:
- s = f" ffi::{c_func_name}("
-
- for i, param_decl in enumerate(decl["params"]):
- if i > 0:
- s += ", "
- arg_name = param_decl["name"]
- arg_type = param_decl["type"]
-
- if util.is_string_ptr(arg_type):
- s += f"tmp_{i}.as_ptr()"
- else:
- s += arg_name
-
- if is_rust_string(rust_res_type):
- s += ")"
- s += ")"
- l(s)
- l(" }")
- l("}")
-
-
-def pre_parse(inp):
- global struct_types
- global enum_types
- for decl in inp["decls"]:
- kind = decl["kind"]
- if kind == "struct":
- struct_types.append(decl["name"])
- elif kind == "enum":
- enum_name = decl["name"]
- enum_types.append(enum_name)
- enum_items[enum_name] = []
- for item in decl["items"]:
- enum_items[enum_name].append(as_enum_item_name(item["name"]))
-
-def gen_imports(inp, dep_prefixes):
- for dep_prefix in dep_prefixes:
- dep_module_name = module_names[dep_prefix]
- # l(f'const {dep_prefix[:-1]} = @import("{dep_module_name}.rs");')
- l(f'use crate::{dep_module_name} as {dep_prefix[:-1]};')
- l("")
-
-
-def gen_helpers(inp):
- l("/// Helper function to convert a C string to a rust string slice")
- l("#[inline]")
- l("fn c_char_ptr_to_rust_str(c_char_ptr: *const core::ffi::c_char) -> &'static str {")
- l(" let c_str = unsafe { core::ffi::CStr::from_ptr(c_char_ptr) };")
- l(" c_str.to_str().expect(\"c_char_ptr contained invalid Utf8 Data\")")
- l("}")
- l("")
-
- if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_', 'sapp_']:
- l("/// Helper function to cast a rust slice into a sokol Range")
- l(f"pub fn slice_as_range(data: &[T]) -> {range_struct_name} {{")
- l(f" {range_struct_name} {{ size: std::mem::size_of_val(data), ptr: data.as_ptr() as *const _ }}")
- l("}")
- l("/// Helper function to cast a rust reference into a sokol Range")
- l(f"pub fn value_as_range(value: &T) -> {range_struct_name} {{")
- l(f" {range_struct_name} {{ size: std::mem::size_of::(), ptr: value as *const T as *const _ }}")
- l("}")
- l("")
- l(f"impl From<&[T]> for {range_struct_name} {{")
- l(" #[inline]")
- l(" fn from(data: &[T]) -> Self {")
- l(" slice_as_range(data)")
- l(" }")
- l("}")
- l(f"impl From<&T> for {range_struct_name} {{")
- l(" #[inline]")
- l(" fn from(value: &T) -> Self {")
- l(" value_as_range(value)")
- l(" }")
- l("}")
- l("")
-
- # if inp["prefix"] == "sdtx_":
- # l("/// std.fmt compatible Writer")
- # l("pub const Writer = struct {")
- # l(" pub const Error = error { };")
- # l(" pub fn writeAll(self: Writer, bytes: []const u8) Error!void {")
- # l(" _ = self;")
- # l(" for (bytes) |byte| {")
- # l(" putc(byte);")
- # l(" }")
- # l(" }")
- # l(" pub fn writeByteNTimes(self: Writer, byte: u8, n: u64) Error!void {")
- # l(" _ = self;")
- # l(" var i: u64 = 0;")
- # l(" while (i < n): (i += 1) {")
- # l(" putc(byte);")
- # l(" }")
- # l(" }")
- # l("};")
- # l("// std.fmt-style formatted print")
- # l("pub fn print(comptime fmt: anytype, args: anytype) void {")
- # l(" var writer: Writer = .{};")
- # l(' @import("std").fmt.format(writer, fmt, args) catch {};')
- # l("}")
- # l("")
-
-
-def gen_module(inp, dep_prefixes):
- module = inp['module']
- if module in module_requires_rust_feature:
- feature = module_requires_rust_feature[module]
- l(f"//! To use this module, enable the feature \"{feature}\"")
-
- l("// machine generated, do not edit")
- l("")
-
-
- l("#![allow(dead_code)]")
- l("#![allow(unused_imports)]")
- l("")
- gen_imports(inp, dep_prefixes)
- gen_helpers(inp)
- pre_parse(inp)
- prefix = inp["prefix"]
-
- funcs = []
-
- for decl in inp["decls"]:
- #
- # HACK: gen_ir.py accidentally marks all sg_imgui_ declarations as is_dep since sg_imgui
- # depends on sg_a but also starts with sg_... Fix gen_ir.py to remove this hack
- #
- dep_hack = False
- if module == "gfx_imgui":
- dep_hack = "name" in decl and decl["name"].startswith("sg_imgui_")
-
- if not decl["is_dep"] or dep_hack:
- kind = decl["kind"]
- if kind == "consts":
- gen_consts(decl, prefix)
- elif not check_ignore(decl["name"]):
- if kind == "struct":
- gen_struct(decl, prefix)
- elif kind == "enum":
- gen_enum(decl, prefix)
- elif kind == "func":
- funcs.append((decl, prefix))
-
- gen_c_funcs(funcs)
- gen_rust_funcs(funcs)
-
-
-def prepare():
- print("=== Generating Rust bindings:")
- if not os.path.isdir("sokol-rust/src/sokol"):
- os.makedirs("sokol-rust/src/sokol")
- if not os.path.isdir("sokol-rust/src/sokol/c"):
- os.makedirs("sokol-rust/src/sokol/c")
-
- with open("sokol-rust/src/lib.rs", "w", newline="\n") as f_outp:
- f_outp.write("//! Automatically generated sokol bindings for Rust\n\n")
-
-
-def gen(c_header_path, c_prefix, dep_c_prefixes):
- if c_prefix not in module_names:
- print(f' >> warning: skipping generation for {c_prefix} prefix...')
- return
-
- module_name = module_names[c_prefix]
- c_source_path = c_source_paths[c_prefix]
- print(f' {c_header_path} => {module_name}')
- reset_globals()
- c_path_in_project = f'sokol-rust/src/sokol/c/{os.path.basename(c_header_path)}'
- shutil.copyfile(c_header_path, c_path_in_project)
- ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes)
- gen_module(ir, dep_c_prefixes)
- output_path = f"sokol-rust/src/{ir['module']}.rs"
- with open(output_path, 'w', newline='\n') as f_outp:
- f_outp.write(out_lines)
-
- with open("sokol-rust/src/lib.rs", "a", newline="\n") as f_outp:
- module = ir['module']
- if module in module_requires_rust_feature:
- feature = module_requires_rust_feature[module]
- f_outp.write(f"/// Enable feature \"{feature}\" to use\n")
- f_outp.write(f"#[cfg(feature=\"{feature}\")]\n")
- f_outp.write(f"pub mod {module};\n")
diff --git a/source/engine/thirdparty/sokol/bindgen/gen_util.py b/source/engine/thirdparty/sokol/bindgen/gen_util.py
deleted file mode 100644
index a76f4b2..0000000
--- a/source/engine/thirdparty/sokol/bindgen/gen_util.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# common utility functions for all bindings generators
-import re
-
-re_1d_array = re.compile("^(?:const )?\w*\s*\*?\[\d*\]$")
-re_2d_array = re.compile("^(?:const )?\w*\s*\*?\[\d*\]\[\d*\]$")
-
-def is_1d_array_type(s):
- return re_1d_array.match(s) is not None
-
-def is_2d_array_type(s):
- return re_2d_array.match(s) is not None
-
-def is_array_type(s):
- return is_1d_array_type(s) or is_2d_array_type(s)
-
-def extract_array_type(s):
- return s[:s.index('[')].strip()
-
-def extract_array_sizes(s):
- return s[s.index('['):].replace('[', ' ').replace(']', ' ').split()
-
-def is_string_ptr(s):
- return s == "const char *"
-
-def is_const_void_ptr(s):
- return s == "const void *"
-
-def is_void_ptr(s):
- return s == "void *"
-
-def is_func_ptr(s):
- return '(*)' in s
-
-def extract_ptr_type(s):
- tokens = s.split()
- if tokens[0] == 'const':
- return tokens[1]
- else:
- return tokens[0]
-
-# PREFIX_BLA_BLUB to bla_blub
-def as_lower_snake_case(s, prefix):
- outp = s.lower()
- if outp.startswith(prefix):
- outp = outp[len(prefix):]
- return outp
-
-# prefix_bla_blub => blaBlub, PREFIX_BLA_BLUB => blaBlub
-def as_lower_camel_case(s, prefix):
- outp = s.lower()
- if outp.startswith(prefix):
- outp = outp[len(prefix):]
- parts = outp.split('_')
- outp = parts[0]
- for part in parts[1:]:
- outp += part.capitalize()
- return outp
diff --git a/source/engine/thirdparty/sokol/bindgen/gen_zig.py b/source/engine/thirdparty/sokol/bindgen/gen_zig.py
deleted file mode 100644
index bf7db05..0000000
--- a/source/engine/thirdparty/sokol/bindgen/gen_zig.py
+++ /dev/null
@@ -1,552 +0,0 @@
-#-------------------------------------------------------------------------------
-# Generate Zig bindings.
-#
-# Zig coding style:
-# - types are PascalCase
-# - functions are camelCase
-# - otherwise snake_case
-#-------------------------------------------------------------------------------
-import gen_ir
-import os, shutil, sys
-
-import gen_util as util
-
-module_names = {
- 'slog_': 'log',
- 'sg_': 'gfx',
- 'sapp_': 'app',
- 'stm_': 'time',
- 'saudio_': 'audio',
- 'sgl_': 'gl',
- 'sdtx_': 'debugtext',
- 'sshape_': 'shape',
- 'sglue_': 'glue',
-}
-
-c_source_paths = {
- 'slog_': 'sokol-zig/src/sokol/c/sokol_log.c',
- 'sg_': 'sokol-zig/src/sokol/c/sokol_gfx.c',
- 'sapp_': 'sokol-zig/src/sokol/c/sokol_app.c',
- 'stm_': 'sokol-zig/src/sokol/c/sokol_time.c',
- 'saudio_': 'sokol-zig/src/sokol/c/sokol_audio.c',
- 'sgl_': 'sokol-zig/src/sokol/c/sokol_gl.c',
- 'sdtx_': 'sokol-zig/src/sokol/c/sokol_debugtext.c',
- 'sshape_': 'sokol-zig/src/sokol/c/sokol_shape.c',
- 'sglue_': 'sokol-zig/src/sokol/c/sokol_glue.c',
-}
-
-ignores = [
- 'sdtx_printf',
- 'sdtx_vprintf',
- 'sg_install_trace_hooks',
- 'sg_trace_hooks',
-]
-
-# functions that need to be exposed as 'raw' C callbacks without a Zig wrapper function
-c_callbacks = [
- 'slog_func'
-]
-
-# NOTE: syntax for function results: "func_name.RESULT"
-overrides = {
- 'sgl_error': 'sgl_get_error', # 'error' is reserved in Zig
- 'sgl_deg': 'sgl_as_degrees',
- 'sgl_rad': 'sgl_as_radians',
- 'sg_apply_uniforms.ub_index': 'uint32_t',
- 'sg_draw.base_element': 'uint32_t',
- 'sg_draw.num_elements': 'uint32_t',
- 'sg_draw.num_instances': 'uint32_t',
- 'sshape_element_range_t.base_element': 'uint32_t',
- 'sshape_element_range_t.num_elements': 'uint32_t',
- 'sdtx_font.font_index': 'uint32_t',
- 'SGL_NO_ERROR': 'SGL_ERROR_NO_ERROR',
-}
-
-prim_types = {
- 'int': 'i32',
- 'bool': 'bool',
- 'char': 'u8',
- 'int8_t': 'i8',
- 'uint8_t': 'u8',
- 'int16_t': 'i16',
- 'uint16_t': 'u16',
- 'int32_t': 'i32',
- 'uint32_t': 'u32',
- 'int64_t': 'i64',
- 'uint64_t': 'u64',
- 'float': 'f32',
- 'double': 'f64',
- 'uintptr_t': 'usize',
- 'intptr_t': 'isize',
- 'size_t': 'usize'
-}
-
-prim_defaults = {
- 'int': '0',
- 'bool': 'false',
- 'int8_t': '0',
- 'uint8_t': '0',
- 'int16_t': '0',
- 'uint16_t': '0',
- 'int32_t': '0',
- 'uint32_t': '0',
- 'int64_t': '0',
- 'uint64_t': '0',
- 'float': '0.0',
- 'double': '0.0',
- 'uintptr_t': '0',
- 'intptr_t': '0',
- 'size_t': '0'
-}
-
-
-struct_types = []
-enum_types = []
-enum_items = {}
-out_lines = ''
-
-def reset_globals():
- global struct_types
- global enum_types
- global enum_items
- global out_lines
- struct_types = []
- enum_types = []
- enum_items = {}
- out_lines = ''
-
-def l(s):
- global out_lines
- out_lines += s + '\n'
-
-def as_zig_prim_type(s):
- return prim_types[s]
-
-# prefix_bla_blub(_t) => (dep.)BlaBlub
-def as_zig_struct_type(s, prefix):
- parts = s.lower().split('_')
- outp = '' if s.startswith(prefix) else f'{parts[0]}.'
- for part in parts[1:]:
- # ignore '_t' type postfix
- if (part != 't'):
- outp += part.capitalize()
- return outp
-
-# prefix_bla_blub(_t) => (dep.)BlaBlub
-def as_zig_enum_type(s, prefix):
- parts = s.lower().split('_')
- outp = '' if s.startswith(prefix) else f'{parts[0]}.'
- for part in parts[1:]:
- if (part != 't'):
- outp += part.capitalize()
- return outp
-
-def check_override(name, default=None):
- if name in overrides:
- return overrides[name]
- elif default is None:
- return name
- else:
- return default
-
-def check_ignore(name):
- return name in ignores
-
-# PREFIX_ENUM_BLA => Bla, _PREFIX_ENUM_BLA => Bla
-def as_enum_item_name(s):
- outp = s.lstrip('_')
- parts = outp.split('_')[2:]
- outp = '_'.join(parts)
- if outp[0].isdigit():
- outp = '_' + outp
- return outp
-
-def enum_default_item(enum_name):
- return enum_items[enum_name][0]
-
-def is_prim_type(s):
- return s in prim_types
-
-def is_struct_type(s):
- return s in struct_types
-
-def is_enum_type(s):
- return s in enum_types
-
-def is_const_prim_ptr(s):
- for prim_type in prim_types:
- if s == f"const {prim_type} *":
- return True
- return False
-
-def is_prim_ptr(s):
- for prim_type in prim_types:
- if s == f"{prim_type} *":
- return True
- return False
-
-def is_const_struct_ptr(s):
- for struct_type in struct_types:
- if s == f"const {struct_type} *":
- return True
- return False
-
-def type_default_value(s):
- return prim_defaults[s]
-
-def as_c_arg_type(arg_type, prefix):
- if arg_type == "void":
- return "void"
- elif is_prim_type(arg_type):
- return as_zig_prim_type(arg_type)
- elif is_struct_type(arg_type):
- return as_zig_struct_type(arg_type, prefix)
- elif is_enum_type(arg_type):
- return as_zig_enum_type(arg_type, prefix)
- elif util.is_void_ptr(arg_type):
- return "?*anyopaque"
- elif util.is_const_void_ptr(arg_type):
- return "?*const anyopaque"
- elif util.is_string_ptr(arg_type):
- return "[*c]const u8"
- elif is_const_struct_ptr(arg_type):
- return f"[*c]const {as_zig_struct_type(util.extract_ptr_type(arg_type), prefix)}"
- elif is_prim_ptr(arg_type):
- return f"[*c]{as_zig_prim_type(util.extract_ptr_type(arg_type))}"
- elif is_const_prim_ptr(arg_type):
- return f"[*c]const {as_zig_prim_type(util.extract_ptr_type(arg_type))}"
- else:
- sys.exit(f"Error as_c_arg_type(): {arg_type}")
-
-def as_zig_arg_type(arg_prefix, arg_type, prefix):
- # NOTE: if arg_prefix is None, the result is used as return value
- pre = "" if arg_prefix is None else arg_prefix
- if arg_type == "void":
- if arg_prefix is None:
- return "void"
- else:
- return ""
- elif is_prim_type(arg_type):
- return pre + as_zig_prim_type(arg_type)
- elif is_struct_type(arg_type):
- return pre + as_zig_struct_type(arg_type, prefix)
- elif is_enum_type(arg_type):
- return pre + as_zig_enum_type(arg_type, prefix)
- elif util.is_void_ptr(arg_type):
- return pre + "?*anyopaque"
- elif util.is_const_void_ptr(arg_type):
- return pre + "?*const anyopaque"
- elif util.is_string_ptr(arg_type):
- return pre + "[:0]const u8"
- elif is_const_struct_ptr(arg_type):
- # not a bug, pass const structs by value
- return pre + f"{as_zig_struct_type(util.extract_ptr_type(arg_type), prefix)}"
- elif is_prim_ptr(arg_type):
- return pre + f"*{as_zig_prim_type(util.extract_ptr_type(arg_type))}"
- elif is_const_prim_ptr(arg_type):
- return pre + f"*const {as_zig_prim_type(util.extract_ptr_type(arg_type))}"
- else:
- sys.exit(f"ERROR as_zig_arg_type(): {arg_type}")
-
-def is_zig_string(zig_type):
- return zig_type == "[:0]const u8"
-
-# get C-style arguments of a function pointer as string
-def funcptr_args_c(field_type, prefix):
- tokens = field_type[field_type.index('(*)')+4:-1].split(',')
- s = ""
- for token in tokens:
- arg_type = token.strip()
- if s != "":
- s += ", "
- c_arg = as_c_arg_type(arg_type, prefix)
- if c_arg == "void":
- return ""
- else:
- s += c_arg
- return s
-
-# get C-style result of a function pointer as string
-def funcptr_result_c(field_type):
- res_type = field_type[:field_type.index('(*)')].strip()
- if res_type == 'void':
- return 'void'
- elif is_prim_type(res_type):
- return as_zig_prim_type(res_type)
- elif util.is_const_void_ptr(res_type):
- return '?*const anyopaque'
- elif util.is_void_ptr(res_type):
- return '?*anyopaque'
- else:
- sys.exit(f"ERROR funcptr_result_c(): {field_type}")
-
-def funcdecl_args_c(decl, prefix):
- s = ""
- func_name = decl['name']
- for param_decl in decl['params']:
- if s != "":
- s += ", "
- param_name = param_decl['name']
- param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type'])
- s += as_c_arg_type(param_type, prefix)
- return s
-
-def funcdecl_args_zig(decl, prefix):
- s = ""
- func_name = decl['name']
- for param_decl in decl['params']:
- if s != "":
- s += ", "
- param_name = param_decl['name']
- param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type'])
- s += f"{as_zig_arg_type(f'{param_name}: ', param_type, prefix)}"
- return s
-
-def funcdecl_result_c(decl, prefix):
- func_name = decl['name']
- decl_type = decl['type']
- result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip())
- return as_c_arg_type(result_type, prefix)
-
-def funcdecl_result_zig(decl, prefix):
- func_name = decl['name']
- decl_type = decl['type']
- result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip())
- zig_res_type = as_zig_arg_type(None, result_type, prefix)
- return zig_res_type
-
-def gen_struct(decl, prefix):
- struct_name = check_override(decl['name'])
- zig_type = as_zig_struct_type(struct_name, prefix)
- l(f"pub const {zig_type} = extern struct {{")
- for field in decl['fields']:
- field_name = check_override(field['name'])
- field_type = check_override(f'{struct_name}.{field_name}', default=field['type'])
- if is_prim_type(field_type):
- l(f" {field_name}: {as_zig_prim_type(field_type)} = {type_default_value(field_type)},")
- elif is_struct_type(field_type):
- l(f" {field_name}: {as_zig_struct_type(field_type, prefix)} = .{{}},")
- elif is_enum_type(field_type):
- l(f" {field_name}: {as_zig_enum_type(field_type, prefix)} = .{enum_default_item(field_type)},")
- elif util.is_string_ptr(field_type):
- l(f" {field_name}: [*c]const u8 = null,")
- elif util.is_const_void_ptr(field_type):
- l(f" {field_name}: ?*const anyopaque = null,")
- elif util.is_void_ptr(field_type):
- l(f" {field_name}: ?*anyopaque = null,")
- elif is_const_prim_ptr(field_type):
- l(f" {field_name}: ?[*]const {as_zig_prim_type(util.extract_ptr_type(field_type))} = null,")
- elif util.is_func_ptr(field_type):
- l(f" {field_name}: ?*const fn ({funcptr_args_c(field_type, prefix)}) callconv(.C) {funcptr_result_c(field_type)} = null,")
- elif util.is_1d_array_type(field_type):
- array_type = util.extract_array_type(field_type)
- array_sizes = util.extract_array_sizes(field_type)
- if is_prim_type(array_type) or is_struct_type(array_type):
- if is_prim_type(array_type):
- zig_type = as_zig_prim_type(array_type)
- def_val = type_default_value(array_type)
- elif is_struct_type(array_type):
- zig_type = as_zig_struct_type(array_type, prefix)
- def_val = '.{}'
- elif is_enum_type(array_type):
- zig_type = as_zig_enum_type(array_type, prefix)
- def_val = '.{}'
- else:
- sys.exit(f"ERROR gen_struct is_1d_array_type: {array_type}")
- t0 = f"[{array_sizes[0]}]{zig_type}"
- t1 = f"[_]{zig_type}"
- l(f" {field_name}: {t0} = {t1}{{{def_val}}} ** {array_sizes[0]},")
- elif util.is_const_void_ptr(array_type):
- l(f" {field_name}: [{array_sizes[0]}]?*const anyopaque = [_]?*const anyopaque{{null}} ** {array_sizes[0]},")
- else:
- sys.exit(f"ERROR gen_struct: array {field_name}: {field_type} => {array_type} [{array_sizes[0]}]")
- elif util.is_2d_array_type(field_type):
- array_type = util.extract_array_type(field_type)
- array_sizes = util.extract_array_sizes(field_type)
- if is_prim_type(array_type):
- zig_type = as_zig_prim_type(array_type)
- def_val = type_default_value(array_type)
- elif is_struct_type(array_type):
- zig_type = as_zig_struct_type(array_type, prefix)
- def_val = ".{}"
- else:
- sys.exit(f"ERROR gen_struct is_2d_array_type: {array_type}")
- t0 = f"[{array_sizes[0]}][{array_sizes[1]}]{zig_type}"
- l(f" {field_name}: {t0} = [_][{array_sizes[1]}]{zig_type}{{[_]{zig_type}{{{def_val}}} ** {array_sizes[1]}}} ** {array_sizes[0]},")
- else:
- sys.exit(f"ERROR gen_struct: {field_name}: {field_type};")
- l("};")
-
-def gen_consts(decl, prefix):
- for item in decl['items']:
- item_name = check_override(item['name'])
- l(f"pub const {util.as_lower_snake_case(item_name, prefix)} = {item['value']};")
-
-def gen_enum(decl, prefix):
- enum_name = check_override(decl['name'])
- l(f"pub const {as_zig_enum_type(enum_name, prefix)} = enum(i32) {{")
- for item in decl['items']:
- item_name = as_enum_item_name(check_override(item['name']))
- if item_name != "FORCE_U32":
- if 'value' in item:
- l(f" {item_name} = {item['value']},")
- else:
- l(f" {item_name},")
- l("};")
-
-def gen_func_c(decl, prefix):
- l(f"pub extern fn {decl['name']}({funcdecl_args_c(decl, prefix)}) {funcdecl_result_c(decl, prefix)};")
-
-def gen_func_zig(decl, prefix):
- c_func_name = decl['name']
- zig_func_name = util.as_lower_camel_case(check_override(decl['name']), prefix)
- if c_func_name in c_callbacks:
- # a simple forwarded C callback function
- l(f"pub const {zig_func_name} = {c_func_name};")
- else:
- zig_res_type = funcdecl_result_zig(decl, prefix)
- l(f"pub fn {zig_func_name}({funcdecl_args_zig(decl, prefix)}) {zig_res_type} {{")
- if is_zig_string(zig_res_type):
- # special case: convert C string to Zig string slice
- s = f" return cStrToZig({c_func_name}("
- elif zig_res_type != 'void':
- s = f" return {c_func_name}("
- else:
- s = f" {c_func_name}("
- for i, param_decl in enumerate(decl['params']):
- if i > 0:
- s += ", "
- arg_name = param_decl['name']
- arg_type = param_decl['type']
- if is_const_struct_ptr(arg_type):
- s += f"&{arg_name}"
- elif util.is_string_ptr(arg_type):
- s += f"@ptrCast({arg_name})"
- else:
- s += arg_name
- if is_zig_string(zig_res_type):
- s += ")"
- s += ");"
- l(s)
- l("}")
-
-def pre_parse(inp):
- global struct_types
- global enum_types
- for decl in inp['decls']:
- kind = decl['kind']
- if kind == 'struct':
- struct_types.append(decl['name'])
- elif kind == 'enum':
- enum_name = decl['name']
- enum_types.append(enum_name)
- enum_items[enum_name] = []
- for item in decl['items']:
- enum_items[enum_name].append(as_enum_item_name(item['name']))
-
-def gen_imports(inp, dep_prefixes):
- l('const builtin = @import("builtin");')
- for dep_prefix in dep_prefixes:
- dep_module_name = module_names[dep_prefix]
- l(f'const {dep_prefix[:-1]} = @import("{dep_module_name}.zig");')
- l('')
-
-def gen_helpers(inp):
- l('// helper function to convert a C string to a Zig string slice')
- l('fn cStrToZig(c_str: [*c]const u8) [:0]const u8 {')
- l(' return @import("std").mem.span(c_str);')
- l('}')
- if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_']:
- l('// helper function to convert "anything" to a Range struct')
- l('pub fn asRange(val: anytype) Range {')
- l(' const type_info = @typeInfo(@TypeOf(val));')
- l(' switch (type_info) {')
- l(' .Pointer => {')
- l(' switch (type_info.Pointer.size) {')
- l(' .One => return .{ .ptr = val, .size = @sizeOf(type_info.Pointer.child) },')
- l(' .Slice => return .{ .ptr = val.ptr, .size = @sizeOf(type_info.Pointer.child) * val.len },')
- l(' else => @compileError("FIXME: Pointer type!"),')
- l(' }')
- l(' },')
- l(' .Struct, .Array => {')
- l(' @compileError("Structs and arrays must be passed as pointers to asRange");')
- l(' },')
- l(' else => {')
- l(' @compileError("Cannot convert to range!");')
- l(' },')
- l(' }')
- l('}')
- l('')
- if inp['prefix'] == 'sdtx_':
- l('// std.fmt compatible Writer')
- l('pub const Writer = struct {')
- l(' pub const Error = error{};')
- l(' pub fn writeAll(self: Writer, bytes: []const u8) Error!void {')
- l(' _ = self;')
- l(' for (bytes) |byte| {')
- l(' putc(byte);')
- l(' }')
- l(' }')
- l(' pub fn writeByteNTimes(self: Writer, byte: u8, n: usize) Error!void {')
- l(' _ = self;')
- l(' var i: u64 = 0;')
- l(' while (i < n) : (i += 1) {')
- l(' putc(byte);')
- l(' }')
- l(' }')
- l(' pub fn writeBytesNTimes(self: Writer, bytes: []const u8, n: usize) Error!void {')
- l(' var i: usize = 0;')
- l(' while (i < n) : (i += 1) {')
- l(' try self.writeAll(bytes);')
- l(' }')
- l(' }')
- l('};')
- l('// std.fmt-style formatted print')
- l('pub fn print(comptime fmt: anytype, args: anytype) void {')
- l(' const writer: Writer = .{};')
- l(' @import("std").fmt.format(writer, fmt, args) catch {};')
- l('}')
- l('')
-
-def gen_module(inp, dep_prefixes):
- l('// machine generated, do not edit')
- l('')
- gen_imports(inp, dep_prefixes)
- gen_helpers(inp)
- pre_parse(inp)
- prefix = inp['prefix']
- for decl in inp['decls']:
- if not decl['is_dep']:
- kind = decl['kind']
- if kind == 'consts':
- gen_consts(decl, prefix)
- elif not check_ignore(decl['name']):
- if kind == 'struct':
- gen_struct(decl, prefix)
- elif kind == 'enum':
- gen_enum(decl, prefix)
- elif kind == 'func':
- gen_func_c(decl, prefix)
- gen_func_zig(decl, prefix)
-
-def prepare():
- print('=== Generating Zig bindings:')
- if not os.path.isdir('sokol-zig/src/sokol'):
- os.makedirs('sokol-zig/src/sokol')
- if not os.path.isdir('sokol-zig/src/sokol/c'):
- os.makedirs('sokol-zig/src/sokol/c')
-
-def gen(c_header_path, c_prefix, dep_c_prefixes):
- if not c_prefix in module_names:
- print(f' >> warning: skipping generation for {c_prefix} prefix...')
- return
- module_name = module_names[c_prefix]
- c_source_path = c_source_paths[c_prefix]
- print(f' {c_header_path} => {module_name}')
- reset_globals()
- shutil.copyfile(c_header_path, f'sokol-zig/src/sokol/c/{os.path.basename(c_header_path)}')
- ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes)
- gen_module(ir, dep_c_prefixes)
- output_path = f"sokol-zig/src/sokol/{ir['module']}.zig"
- with open(output_path, 'w', newline='\n') as f_outp:
- f_outp.write(out_lines)
diff --git a/source/engine/thirdparty/sokol/tests/.gitignore b/source/engine/thirdparty/sokol/tests/.gitignore
deleted file mode 100644
index 567609b..0000000
--- a/source/engine/thirdparty/sokol/tests/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-build/
diff --git a/source/engine/thirdparty/sokol/tests/CMakeLists.txt b/source/engine/thirdparty/sokol/tests/CMakeLists.txt
deleted file mode 100644
index cfaf7d4..0000000
--- a/source/engine/thirdparty/sokol/tests/CMakeLists.txt
+++ /dev/null
@@ -1,164 +0,0 @@
-cmake_minimum_required(VERSION 3.20)
-project(sokol-test)
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
-set(CMAKE_C_STANDARD 11)
-set(CMAKE_CXX_STANDARD 11)
-
-# SOKOL_GLCORE33, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU, SOKOL_DUMMY
-set(SOKOL_BACKEND "SOKOL_DUMMY_BACKEND" CACHE STRING "Select 3D backend API")
-set_property(CACHE SOKOL_BACKEND PROPERTY STRINGS SOKOL_GLCORE33 SOKOL_METAL SOKOL_D3D11 SOKOL_DUMMY_BACKEND)
-option(SOKOL_FORCE_EGL "Force EGL with GLCORE33 backend" OFF)
-option(SOKOL_FORCE_SLES "Force SLES in sokol-audio Android backend" OFF)
-option(USE_ARC "Enable/disable ARC" OFF)
-option(USE_ANALYZER "Enable/disable clang analyzer" OFF)
-
-if (CMAKE_SYSTEM_NAME STREQUAL Emscripten)
- set(EMSCRIPTEN 1)
-elseif (CMAKE_SYSTEM_NAME STREQUAL iOS)
- set(OSX_IOS 1)
-elseif (CMAKE_SYSTEM_NAME STREQUAL Android)
- set(ANDROID 1)
-elseif (CMAKE_SYSTEM_NAME STREQUAL Linux)
- set(LINUX 1)
-elseif (CMAKE_SYSTEM_NAME STREQUAL Darwin)
- set(OSX_MACOS 1)
-elseif (CMAKE_SYSTEM_NAME STREQUAL Windows)
- set(WINDOWS 1)
-else()
- message(FATAL_ERROR "Unrecognized CMAKE_SYSTEM_NAME")
-endif()
-
-message(">> CMAKE_CXX_COMPILER_ID: ${CMAKE_CXX_COMPILER_ID}")
-message(">> SOKOL_BACKEND: ${SOKOL_BACKEND}")
-message(">> SOKOL_FORCE_EGL: ${SOKOL_FORCE_EGL}")
-message(">> SOKOL_FORCE_SLES: ${SOKOL_FORCE_SLES}")
-if (OSX_IOS OR OSX_MACOS)
- if (USE_ARC)
- message(">> ObjC ARC ENABLED")
- else()
- message(">> ObjC ARC DISABLED")
- endif()
-endif()
-message(">> BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
-message(">> TOOLCHAIN: ${CMAKE_TOOLCHAIN_FILE}")
-
-set(c_flags)
-set(cxx_flags)
-set(link_flags)
-set(system_libs)
-
-if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
- set(c_flags ${c_flags} /W4 /WX /D_CRT_SECURE_NO_WARNINGS)
- set(cxx_flags ${cxx_flags} /W4 /WX /EHsc /D_CRT_SECURE_NO_WARNINGS)
-else()
- set(c_flags ${c_flags} -Wall -Wextra -Werror -Wsign-conversion -Wstrict-prototypes)
- set(cxx_flags ${cxx_flags} -Wall -Wextra -Werror -Wsign-conversion -fno-rtti -fno-exceptions)
- if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
- set(c_flags ${c_flags} -Wno-missing-field-initializers)
- set(cxx_flags ${cxx_flags} -Wno-missing-field-initializers)
- endif()
- if (USE_ANALYZER)
- # FIXME: consider using clang-tidy via CMAKE_CXX_CLANG_TIDY: https://ortogonal.github.io/cmake-clang-tidy/
- # with the default settings this spams the output with irrelevant C++ coding style warnings in 3rd party libs though
- message(">> Configuring for static code analysis")
- set(c_flags ${c_flags} --analyze -Xanalyzer -analyzer-opt-analyze-headers)
- set(cxx_flags ${cxx_flags} --analyze -Xanalyzer -analyzer-opt-analyze-headers)
- set(link_flags ${link_flags} --analyze -Wno-unused-command-line-argument)
- endif()
-endif()
-
-if (EMSCRIPTEN)
- set(CMAKE_EXECUTABLE_SUFFIX ".html")
- set(link_flags ${link_flags} -sNO_FILESYSTEM=1 -sASSERTIONS=0 -sMALLOC=emmalloc -sINITIAL_MEMORY=33554432 --closure=1)
- if (SOKOL_BACKEND STREQUAL SOKOL_WGPU)
- set(link_flags ${link_flags} -sUSE_WEBGPU=1)
- else()
- set(link_flags ${link_flags} -sUSE_WEBGL2=1)
- endif()
-elseif (OSX_IOS)
- set(exe_type MACOSX_BUNDLE)
- if (USE_ARC)
- set(c_flags ${c_flags} -fobjc-arc)
- set(cxx_flags ${cxx_flags} -fobjc-arc)
- endif()
- set(system_libs ${system_libs} "-framework Foundation" "-framework UIKit" "-framework AudioToolbox" "-framework AVFoundation")
- if (SOKOL_BACKEND STREQUAL SOKOL_METAL)
- set(system_libs ${system_libs} "-framework Metal" "-framework MetalKit")
- else()
- set(system_libs ${system_libs} "-framework OpenGLES" "-framework GLKit")
- endif()
-elseif (ANDROID)
- if (SOKOL_FORCE_SLES)
- set(system_libs ${system_libs} GLESv3 EGL OpenSLES log android)
- else()
- set(system_libs ${system_libs} GLESv3 EGL aaudio log android)
- endif()
-elseif (LINUX)
- set(THREADS_PREFER_PTHREAD_FLAG ON)
- find_package(Threads REQUIRED)
- if ((SOKOL_BACKEND STREQUAL SOKOL_GLES3) OR SOKOL_FORCE_EGL)
- set(system_libs ${system_libs} X11 Xi Xcursor EGL GL asound dl m Threads::Threads)
- else()
- set(system_libs ${system_libs} X11 Xi Xcursor GL asound dl m Threads::Threads)
- endif()
-elseif (OSX_MACOS)
- set(exe_type MACOSX_BUNDLE)
- if (USE_ARC)
- set(c_flags ${c_flags} -fobjc-arc)
- set(cxx_flags ${cxx_flags} -fobjc-arc)
- endif()
- set(system_libs ${system_libs} "-framework QuartzCore" "-framework Cocoa" "-framework AudioToolbox")
- if (SOKOL_BACKEND STREQUAL SOKOL_METAL)
- set(system_libs ${system_libs} "-framework MetalKit" "-framework Metal")
- else()
- set(system_libs ${system_libs} "-framework OpenGL")
- endif()
-elseif (WINDOWS)
- set(exe_type WIN32)
-endif()
-
-macro(configure_common target)
- if (SOKOL_FORCE_EGL)
- target_compile_definitions(${target} PRIVATE SOKOL_FORCE_EGL)
- endif()
- if (SOKOL_FORCE_SLES)
- target_compile_definitions(${target} PRIVATE SAUDIO_ANDROID_SLES)
- endif()
- target_compile_definitions(${target} PRIVATE ${SOKOL_BACKEND})
- target_link_options(${target} PRIVATE ${link_flags})
- target_link_libraries(${target} PRIVATE ${system_libs})
- target_include_directories(${target} PRIVATE ../.. ../../util)
- target_include_directories(${target} PRIVATE ../ext)
-endmacro()
-
-macro(configure_osx_properties target)
- if (OSX_IOS)
- target_compile_definitions(${target} PRIVATE GLES_SILENCE_DEPRECATION)
- endif()
- set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${target}")
- set_target_properties(${target} PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "${target}")
- set_target_properties(${target} PROPERTIES MACOSX_BUNDLE_PRODUCT_NAME "${target}")
- set_target_properties(${target} PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "${target}")
-endmacro()
-
-macro(configure_c target)
- configure_common(${target})
- target_compile_options(${target} PRIVATE ${c_flags})
- if (OSX_MACOS OR OSX_IOS)
- target_compile_options(${target} PRIVATE -x objective-c)
- configure_osx_properties(${target})
- endif()
-endmacro()
-
-macro(configure_cxx target)
- configure_common(${target})
- target_compile_options(${target} PRIVATE ${cxx_flags})
- if (OSX_MACOS OR OSX_IOS)
- target_compile_options(${target} PRIVATE -x objective-c++)
- configure_osx_properties(${target})
- endif()
-endmacro()
-
-add_subdirectory(ext)
-add_subdirectory(compile)
-add_subdirectory(functional)
diff --git a/source/engine/thirdparty/sokol/tests/CMakePresets.json b/source/engine/thirdparty/sokol/tests/CMakePresets.json
deleted file mode 100644
index fe0b896..0000000
--- a/source/engine/thirdparty/sokol/tests/CMakePresets.json
+++ /dev/null
@@ -1,720 +0,0 @@
-{
- "version": 3,
- "cmakeMinimumRequired": {
- "major": 3,
- "minor": 21,
- "patch": 0
- },
- "configurePresets": [
- {
- "name": "macos_gl_debug",
- "generator": "Ninja",
- "binaryDir": "build/macos_gl_debug",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLCORE33",
- "CMAKE_BUILD_TYPE": "Debug"
- }
- },
- {
- "name": "macos_gl_release",
- "generator": "Ninja",
- "binaryDir": "build/macos_gl_release",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLCORE33",
- "CMAKE_BUILD_TYPE": "Release"
- }
- },
- {
- "name": "macos_gl_analyze",
- "generator": "Ninja",
- "binaryDir": "build/macos_gl_analyze",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLCORE33",
- "CMAKE_BUILD_TYPE": "Debug",
- "USE_ANALYZER": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_C_COMPILER": "clang",
- "CMAKE_CXX_COMPILER": "clang++"
- }
- },
- {
- "name": "macos_metal_debug",
- "generator": "Ninja",
- "binaryDir": "build/macos_metal_debug",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_METAL",
- "CMAKE_BUILD_TYPE": "Debug"
- }
- },
- {
- "name": "macos_metal_release",
- "generator": "Ninja",
- "binaryDir": "build/macos_metal_release",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_METAL",
- "CMAKE_BUILD_TYPE": "Release"
- }
- },
- {
- "name": "macos_metal_analyze",
- "generator": "Ninja",
- "binaryDir": "build/macos_metal_analyze",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_METAL",
- "CMAKE_BUILD_TYPE": "Debug",
- "USE_ANALYZER": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_C_COMPILER": "clang",
- "CMAKE_CXX_COMPILER": "clang++"
- }
- },
- {
- "name": "macos_arc_gl_debug",
- "generator": "Ninja",
- "binaryDir": "build/macos_arc_gl_debug",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLCORE33",
- "USE_ARC": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_BUILD_TYPE": "Debug"
- }
- },
- {
- "name": "macos_arc_gl_release",
- "generator": "Ninja",
- "binaryDir": "build/macos_arc_gl_release",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLCORE33",
- "USE_ARC": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_BUILD_TYPE": "Release"
- }
- },
- {
- "name": "macos_arc_gl_analyze",
- "generator": "Ninja",
- "binaryDir": "build/macos_arc_gl_analyze",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLCORE33",
- "USE_ARC": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_BUILD_TYPE": "Debug",
- "USE_ANALYZER": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_C_COMPILER": "clang",
- "CMAKE_CXX_COMPILER": "clang++"
- }
- },
- {
- "name": "macos_arc_metal_debug",
- "generator": "Ninja",
- "binaryDir": "build/macos_arc_metal_debug",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_METAL",
- "USE_ARC": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_BUILD_TYPE": "Debug"
- }
- },
- {
- "name": "macos_arc_metal_release",
- "generator": "Ninja",
- "binaryDir": "build/macos_arc_metal_release",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_METAL",
- "USE_ARC": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_BUILD_TYPE": "Release"
- }
- },
- {
- "name": "macos_arc_metal_analyze",
- "generator": "Ninja",
- "binaryDir": "build/macos_arc_metal_analyze",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_METAL",
- "USE_ARC": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_BUILD_TYPE": "Debug",
- "USE_ANALYZER": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_C_COMPILER": "clang",
- "CMAKE_CXX_COMPILER": "clang++"
- }
- },
- {
- "name": "ios_gl",
- "generator": "Xcode",
- "binaryDir": "build/ios_gl",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLES3",
- "CMAKE_SYSTEM_NAME": "iOS"
- }
- },
- {
- "name": "ios_gl_analyze",
- "generator": "Ninja",
- "binaryDir": "build/ios_gl_analyze",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLES3",
- "CMAKE_BUILD_TYPE": "Debug",
- "CMAKE_SYSTEM_NAME": "iOS",
- "USE_ANALYZER": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_C_COMPILER": "clang",
- "CMAKE_CXX_COMPILER": "clang++"
- }
- },
- {
- "name": "ios_metal",
- "generator": "Xcode",
- "binaryDir": "build/ios_metal",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_METAL",
- "CMAKE_SYSTEM_NAME": "iOS"
- }
- },
- {
- "name": "ios_metal_analyze",
- "generator": "Ninja",
- "binaryDir": "build/ios_metal_analyze",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_METAL",
- "CMAKE_BUILD_TYPE": "Debug",
- "CMAKE_SYSTEM_NAME": "iOS",
- "USE_ANALYZER": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_C_COMPILER": "clang",
- "CMAKE_CXX_COMPILER": "clang++"
- }
- },
- {
- "name": "ios_arc_gl",
- "generator": "Xcode",
- "binaryDir": "build/ios_arc_gl",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLES3",
- "USE_ARC": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_SYSTEM_NAME": "iOS"
- }
- },
- {
- "name": "ios_arc_gl_analyze",
- "generator": "Ninja",
- "binaryDir": "build/ios_arc_gl_analyze",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLES3",
- "USE_ARC": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_BUILD_TYPE": "Debug",
- "CMAKE_SYSTEM_NAME": "iOS",
- "USE_ANALYZER": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_C_COMPILER": "clang",
- "CMAKE_CXX_COMPILER": "clang++"
- }
- },
- {
- "name": "ios_arc_metal",
- "generator": "Xcode",
- "binaryDir": "build/ios_arc_metal",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_METAL",
- "USE_ARC": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_SYSTEM_NAME": "iOS"
- }
- },
- {
- "name": "ios_arc_metal_analyze",
- "generator": "Ninja",
- "binaryDir": "build/ios_arc_metal_analyze",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_METAL",
- "USE_ARC": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_BUILD_TYPE": "Debug",
- "CMAKE_SYSTEM_NAME": "iOS",
- "USE_ANALYZER": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_C_COMPILER": "clang",
- "CMAKE_CXX_COMPILER": "clang++"
- }
- },
- {
- "name": "linux_gl_debug",
- "generator": "Ninja",
- "binaryDir": "build/linux_gl_debug",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLCORE33",
- "CMAKE_BUILD_TYPE": "Debug"
- }
- },
- {
- "name": "linux_gl_release",
- "generator": "Ninja",
- "binaryDir": "build/linux_gl_release",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLCORE33",
- "CMAKE_BUILD_TYPE": "Release"
- }
- },
- {
- "name": "linux_gl_analyze",
- "generator": "Ninja",
- "binaryDir": "build/linux_gl_analyze",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLCORE33",
- "CMAKE_BUILD_TYPE": "Debug",
- "USE_ANALYZER": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_C_COMPILER": "clang",
- "CMAKE_CXX_COMPILER": "clang++"
- }
- },
- {
- "name": "linux_gles3_debug",
- "generator": "Ninja",
- "binaryDir": "build/linux_gles3_debug",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLES3",
- "CMAKE_BUILD_TYPE": "Debug"
- }
- },
- {
- "name": "linux_gles3_release",
- "generator": "Ninja",
- "binaryDir": "build/linux_gles3_release",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLES3",
- "CMAKE_BUILD_TYPE": "Release"
- }
- },
- {
- "name": "linux_gles3_analyze",
- "generator": "Ninja",
- "binaryDir": "build/linux_gles3_analyze",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLES3",
- "CMAKE_BUILD_TYPE": "Debug",
- "USE_ANALYZER": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_C_COMPILER": "clang",
- "CMAKE_CXX_COMPILER": "clang++"
- }
- },
- {
- "name": "linux_gl_egl_debug",
- "generator": "Ninja",
- "binaryDir": "build/linux_gl_egl_debug",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLCORE33",
- "SOKOL_FORCE_EGL": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_BUILD_TYPE": "Debug"
- }
- },
- {
- "name": "linux_gl_egl_release",
- "generator": "Ninja",
- "binaryDir": "build/linux_gl_egl_release",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLCORE33",
- "SOKOL_FORCE_EGL": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_BUILD_TYPE": "Release"
- }
- },
- {
- "name": "emsc_webgl2_debug",
- "generator": "Ninja",
- "binaryDir": "build/emsc_webgl2_debug",
- "toolchainFile": "build/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLES3",
- "CMAKE_BUILD_TYPE": "Debug"
- }
- },
- {
- "name": "emsc_webgl2_release",
- "generator": "Ninja",
- "binaryDir": "build/emsc_webgl2_release",
- "toolchainFile": "build/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLES3",
- "CMAKE_BUILD_TYPE": "Release"
- }
- },
- {
- "name": "emsc_wgpu_debug",
- "generator": "Ninja",
- "binaryDir": "build/emsc_wgpu_debug",
- "toolchainFile": "build/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_WGPU",
- "CMAKE_BUILD_TYPE": "Debug"
- }
- },
- {
- "name": "emsc_wgpu_release",
- "generator": "Ninja",
- "binaryDir": "build/emsc_wgpu_release",
- "toolchainFile": "build/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_WGPU",
- "CMAKE_BUILD_TYPE": "Release"
- }
- },
- {
- "name": "android_debug",
- "generator": "Ninja",
- "binaryDir": "build/android_debug",
- "toolchainFile": "build/android_sdk/ndk-bundle/build/cmake/android.toolchain.cmake",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLES3",
- "ANDROID_ABI": "armeabi-v7a",
- "ANDROID_PLATFORM": "android-28",
- "CMAKE_BUILD_TYPE": "Debug"
- }
- },
- {
- "name": "android_release",
- "generator": "Ninja",
- "binaryDir": "build/android_release",
- "toolchainFile": "build/android_sdk/ndk-bundle/build/cmake/android.toolchain.cmake",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLES3",
- "ANDROID_ABI": "armeabi-v7a",
- "ANDROID_PLATFORM": "android-28",
- "CMAKE_BUILD_TYPE": "Release"
- }
- },
- {
- "name": "android_sles_debug",
- "generator": "Ninja",
- "binaryDir": "build/android_sles_debug",
- "toolchainFile": "build/android_sdk/ndk-bundle/build/cmake/android.toolchain.cmake",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLES3",
- "ANDROID_ABI": "armeabi-v7a",
- "ANDROID_PLATFORM": "android-28",
- "CMAKE_BUILD_TYPE": "Debug",
- "SOKOL_FORCE_SLES": {
- "type": "BOOL",
- "value": "ON"
- }
- }
- },
- {
- "name": "android_sles_release",
- "generator": "Ninja",
- "binaryDir": "build/android_sles_release",
- "toolchainFile": "build/android_sdk/ndk-bundle/build/cmake/android.toolchain.cmake",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLES3",
- "ANDROID_ABI": "armeabi-v7a",
- "ANDROID_PLATFORM": "android-28",
- "CMAKE_BUILD_TYPE": "Release",
- "SOKOL_FORCE_SLES": {
- "type": "BOOL",
- "value": "ON"
- }
- }
- },
- {
- "name": "win_gl",
- "binaryDir": "build/win_gl",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLCORE33"
- }
- },
- {
- "name": "win_gl_analyze",
- "generator": "Ninja",
- "binaryDir": "build/win_gl_analyze",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_GLCORE33",
- "CMAKE_BUILD_TYPE": "Debug",
- "USE_ANALYZER": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_C_COMPILER": "clang",
- "CMAKE_CXX_COMPILER": "clang++"
- }
- },
- {
- "name": "win_d3d11",
- "binaryDir": "build/win_d3d11",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_D3D11"
- }
- },
- {
- "name": "win_d3d11_analyze",
- "generator": "Ninja",
- "binaryDir": "build/win_d3d11_analyze",
- "cacheVariables": {
- "SOKOL_BACKEND": "SOKOL_D3D11",
- "CMAKE_BUILD_TYPE": "Debug",
- "USE_ANALYZER": {
- "type": "BOOL",
- "value": "ON"
- },
- "CMAKE_C_COMPILER": "clang",
- "CMAKE_CXX_COMPILER": "clang++"
- }
- }
- ],
- "buildPresets": [
- {
- "name": "macos_gl_debug",
- "configurePreset": "macos_gl_debug"
- },
- {
- "name": "macos_gl_release",
- "configurePreset": "macos_gl_release"
- },
- {
- "name": "macos_gl_analyze",
- "configurePreset": "macos_gl_analyze"
- },
- {
- "name": "macos_metal_debug",
- "configurePreset": "macos_metal_debug"
- },
- {
- "name": "macos_metal_release",
- "configurePreset": "macos_metal_release"
- },
- {
- "name": "macos_metal_analyze",
- "configurePreset": "macos_metal_analyze"
- },
- {
- "name": "macos_arc_gl_debug",
- "configurePreset": "macos_arc_gl_debug"
- },
- {
- "name": "macos_arc_gl_release",
- "configurePreset": "macos_arc_gl_release"
- },
- {
- "name": "macos_arc_gl_analyze",
- "configurePreset": "macos_arc_gl_analyze"
- },
- {
- "name": "macos_arc_metal_debug",
- "configurePreset": "macos_arc_metal_debug"
- },
- {
- "name": "macos_arc_metal_release",
- "configurePreset": "macos_arc_metal_release"
- },
- {
- "name": "macos_arc_metal_analyze",
- "configurePreset": "macos_arc_metal_analyze"
- },
- {
- "name": "ios_gl_debug",
- "configurePreset": "ios_gl",
- "configuration": "Debug",
- "nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ]
- },
- {
- "name": "ios_gl_release",
- "configurePreset": "ios_gl",
- "configuration": "Release",
- "nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ]
- },
- {
- "name": "ios_gl_analyze",
- "configurePreset": "ios_gl_analyze"
- },
- {
- "name": "ios_metal_debug",
- "configurePreset": "ios_metal",
- "configuration": "Debug",
- "nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ]
- },
- {
- "name": "ios_metal_release",
- "configurePreset": "ios_metal",
- "configuration": "Release",
- "nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ]
- },
- {
- "name": "ios_metal_analyze",
- "configurePreset": "ios_metal_analyze"
- },
- {
- "name": "ios_arc_gl_debug",
- "configurePreset": "ios_arc_gl",
- "configuration": "Debug",
- "nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ]
- },
- {
- "name": "ios_arc_gl_release",
- "configurePreset": "ios_arc_gl",
- "configuration": "Release",
- "nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ]
- },
- {
- "name": "ios_arc_gl_analyze",
- "configurePreset": "ios_arc_gl_analyze"
- },
- {
- "name": "ios_arc_metal_debug",
- "configurePreset": "ios_arc_metal",
- "configuration": "Debug",
- "nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ]
- },
- {
- "name": "ios_arc_metal_release",
- "configurePreset": "ios_arc_metal",
- "configuration": "Release",
- "nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ]
- },
- {
- "name": "ios_arc_metal_analyze",
- "configurePreset": "ios_arc_metal_analyze"
- },
- {
- "name": "linux_gl_debug",
- "configurePreset": "linux_gl_debug"
- },
- {
- "name": "linux_gl_release",
- "configurePreset": "linux_gl_release"
- },
- {
- "name": "linux_gl_analyze",
- "configurePreset": "linux_gl_analyze"
- },
- {
- "name": "linux_gles3_debug",
- "configurePreset": "linux_gles3_debug"
- },
- {
- "name": "linux_gles3_release",
- "configurePreset": "linux_gles3_release"
- },
- {
- "name": "linux_gles3_analyze",
- "configurePreset": "linux_gles3_analyze"
- },
- {
- "name": "linux_gl_egl_debug",
- "configurePreset": "linux_gl_egl_debug"
- },
- {
- "name": "linux_gl_egl_release",
- "configurePreset": "linux_gl_egl_release"
- },
- {
- "name": "emsc_webgl2_debug",
- "configurePreset": "emsc_webgl2_debug"
- },
- {
- "name": "emsc_webgl2_release",
- "configurePreset": "emsc_webgl2_release"
- },
- {
- "name": "emsc_wgpu_debug",
- "configurePreset": "emsc_wgpu_debug"
- },
- {
- "name": "emsc_wgpu_release",
- "configurePreset": "emsc_wgpu_release"
- },
- {
- "name": "android_debug",
- "configurePreset": "android_debug"
- },
- {
- "name": "android_release",
- "configurePreset": "android_release"
- },
- {
- "name": "android_sles_debug",
- "configurePreset": "android_sles_debug"
- },
- {
- "name": "android_sles_release",
- "configurePreset": "android_sles_release"
- },
- {
- "name": "win_gl_debug",
- "configurePreset": "win_gl",
- "configuration": "Debug"
- },
- {
- "name": "win_gl_release",
- "configurePreset": "win_gl",
- "configuration": "Release"
- },
- {
- "name": "win_gl_analyze",
- "configurePreset": "win_gl_analyze"
- },
- {
- "name": "win_d3d11_debug",
- "configurePreset": "win_d3d11",
- "configuration": "Debug"
- },
- {
- "name": "win_d3d11_release",
- "configurePreset": "win_d3d11",
- "configuration": "Release"
- },
- {
- "name": "win_d3d11_analyze",
- "configurePreset": "win_d3d11_analyze"
- }
- ]
-}
diff --git a/source/engine/thirdparty/sokol/tests/analyze_ios.sh b/source/engine/thirdparty/sokol/tests/analyze_ios.sh
deleted file mode 100755
index 5b3f474..0000000
--- a/source/engine/thirdparty/sokol/tests/analyze_ios.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-set -e
-source test_common.sh
-build ios_gl_analyze ios_gl_analyze
-build ios_metal_analyze ios_metal_analyze
-build ios_arc_gl_analyze ios_arc_gl_analyze
-build ios_arc_metal_analyze ios_arc_metal_analyze
diff --git a/source/engine/thirdparty/sokol/tests/analyze_linux.sh b/source/engine/thirdparty/sokol/tests/analyze_linux.sh
deleted file mode 100755
index 87099b9..0000000
--- a/source/engine/thirdparty/sokol/tests/analyze_linux.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/usr/bin/env bash
-set -e
-source test_common.sh
-
-build linux_gl_analyze linux_gl_analyze
-build linux_gles3_analyze linux_gles3_analyze
diff --git a/source/engine/thirdparty/sokol/tests/analyze_macos.sh b/source/engine/thirdparty/sokol/tests/analyze_macos.sh
deleted file mode 100755
index 3134070..0000000
--- a/source/engine/thirdparty/sokol/tests/analyze_macos.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-set -e
-source test_common.sh
-
-build macos_gl_analyze macos_gl_analyze
-build macos_metal_analyze macos_metal_analyze
-
-build macos_arc_gl_analyze macos_arc_gl_analyze
-build macos_arc_metal_analyze macos_arc_metal_analyze
diff --git a/source/engine/thirdparty/sokol/tests/analyze_win.cmd b/source/engine/thirdparty/sokol/tests/analyze_win.cmd
deleted file mode 100644
index 39aa798..0000000
--- a/source/engine/thirdparty/sokol/tests/analyze_win.cmd
+++ /dev/null
@@ -1,4 +0,0 @@
-cmake --preset win_gl_analyze || exit /b 10
-cmake --build --preset win_gl_analyze || exit /b 10
-cmake --preset win_d3d11_analyze || exit /b 10
-cmake --build --preset win_d3d11_analyze || exit /b 10
diff --git a/source/engine/thirdparty/sokol/tests/compile/CMakeLists.txt b/source/engine/thirdparty/sokol/tests/compile/CMakeLists.txt
deleted file mode 100644
index e664555..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/CMakeLists.txt
+++ /dev/null
@@ -1,58 +0,0 @@
-set(c_sources
- sokol_app.c
- sokol_glue.c
- sokol_gfx.c
- sokol_time.c
- sokol_args.c
- sokol_audio.c
- sokol_debugtext.c
- sokol_gl.c
- sokol_fontstash.c
- sokol_imgui.c
- sokol_gfx_imgui.c
- sokol_shape.c
- sokol_nuklear.c
- sokol_color.c
- sokol_spine.c
- sokol_log.c
- sokol_main.c)
-if (NOT ANDROID)
- set(c_sources ${c_sources} sokol_fetch.c)
-endif()
-
-set(cxx_sources
- sokol_app.cc
- sokol_glue.cc
- sokol_gfx.cc
- sokol_time.cc
- sokol_args.cc
- sokol_audio.cc
- sokol_debugtext.cc
- sokol_gl.cc
- sokol_fontstash.cc
- sokol_imgui.cc
- sokol_gfx_imgui.cc
- sokol_shape.cc
- sokol_color.cc
- sokol_spine.cc
- sokol_log.cc
- sokol_main.cc)
-if (NOT ANDROID)
- set(cxx_sources ${cxx_sources} sokol_fetch.cc)
-endif()
-
-if (ANDROID)
- add_library(sokol-compiletest-c SHARED ${c_sources})
-else()
- add_executable(sokol-compiletest-c ${exe_type} sokol_app.c sokol_glue.c ${c_sources})
-endif()
-target_link_libraries(sokol-compiletest-c PUBLIC cimgui nuklear spine)
-configure_c(sokol-compiletest-c)
-
-if (ANDROID)
- add_library(sokol-compiletest-cxx SHARED ${cxx_sources})
-else()
- add_executable(sokol-compiletest-cxx ${exe_type} ${cxx_sources})
-endif()
-target_link_libraries(sokol-compiletest-cxx PUBLIC imgui nuklear spine)
-configure_cxx(sokol-compiletest-cxx)
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_app.c b/source/engine/thirdparty/sokol/tests/compile/sokol_app.c
deleted file mode 100644
index 33304cf..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_app.c
+++ /dev/null
@@ -1,6 +0,0 @@
-#define SOKOL_IMPL
-#include "sokol_app.h"
-
-void use_app_impl(void) {
- sapp_run(&(sapp_desc){0});
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_app.cc b/source/engine/thirdparty/sokol/tests/compile/sokol_app.cc
deleted file mode 100644
index 49c254e..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_app.cc
+++ /dev/null
@@ -1,6 +0,0 @@
-#define SOKOL_IMPL
-#include "sokol_app.h"
-
-void use_app_impl() {
- sapp_run({ });
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_args.c b/source/engine/thirdparty/sokol/tests/compile/sokol_args.c
deleted file mode 100644
index 4005283..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_args.c
+++ /dev/null
@@ -1,6 +0,0 @@
-#define SOKOL_IMPL
-#include "sokol_args.h"
-
-void use_args_impl(void) {
- sargs_setup(&(sargs_desc){0});
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_args.cc b/source/engine/thirdparty/sokol/tests/compile/sokol_args.cc
deleted file mode 100644
index 7e31d46..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_args.cc
+++ /dev/null
@@ -1,6 +0,0 @@
-#define SOKOL_IMPL
-#include "sokol_args.h"
-
-void use_args_impl() {
- sargs_setup({});
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_audio.c b/source/engine/thirdparty/sokol/tests/compile/sokol_audio.c
deleted file mode 100644
index 1dc12f2..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_audio.c
+++ /dev/null
@@ -1,6 +0,0 @@
-#define SOKOL_IMPL
-#include "sokol_audio.h"
-
-void use_audio_impl(void) {
- saudio_setup(&(saudio_desc){0});
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_audio.cc b/source/engine/thirdparty/sokol/tests/compile/sokol_audio.cc
deleted file mode 100644
index 4205d51..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_audio.cc
+++ /dev/null
@@ -1,6 +0,0 @@
-#define SOKOL_IMPL
-#include "sokol_audio.h"
-
-void use_audio_impl() {
- saudio_setup({});
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_color.c b/source/engine/thirdparty/sokol/tests/compile/sokol_color.c
deleted file mode 100644
index ea3a354..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_color.c
+++ /dev/null
@@ -1,8 +0,0 @@
-#include "sokol_gfx.h"
-#define SOKOL_IMPL
-#include "sokol_color.h"
-
-void use_color_impl(void) {
- sg_color c = sg_make_color_4b(255, 0, 0, 255);
- (void)c;
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_color.cc b/source/engine/thirdparty/sokol/tests/compile/sokol_color.cc
deleted file mode 100644
index ccb6d2b..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_color.cc
+++ /dev/null
@@ -1,9 +0,0 @@
-#include "sokol_gfx.h"
-#define SOKOL_IMPL
-#include "sokol_color.h"
-
-void use_color_impl(void) {
- sg_color c = sg_make_color(255, 0, 0, 255);
- (void)c;
-}
-
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_debugtext.c b/source/engine/thirdparty/sokol/tests/compile/sokol_debugtext.c
deleted file mode 100644
index 1d22c63..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_debugtext.c
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "sokol_gfx.h"
-#define SOKOL_IMPL
-#include "sokol_debugtext.h"
-
-void use_debugtext_impl(void) {
- sdtx_setup(&(sdtx_desc_t){0});
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_debugtext.cc b/source/engine/thirdparty/sokol/tests/compile/sokol_debugtext.cc
deleted file mode 100644
index 6889405..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_debugtext.cc
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "sokol_gfx.h"
-#define SOKOL_IMPL
-#include "sokol_debugtext.h"
-
-void use_debugtext_impl() {
- sdtx_setup({});
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_fetch.c b/source/engine/thirdparty/sokol/tests/compile/sokol_fetch.c
deleted file mode 100644
index 2509056..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_fetch.c
+++ /dev/null
@@ -1,6 +0,0 @@
-#define SOKOL_IMPL
-#include "sokol_fetch.h"
-
-void use_fetch_impl(void) {
- sfetch_setup(&(sfetch_desc_t){0});
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_fetch.cc b/source/engine/thirdparty/sokol/tests/compile/sokol_fetch.cc
deleted file mode 100644
index 69cc228..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_fetch.cc
+++ /dev/null
@@ -1,6 +0,0 @@
-#define SOKOL_IMPL
-#include "sokol_fetch.h"
-
-void use_fetch_impl() {
- sfetch_setup({});
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_fontstash.c b/source/engine/thirdparty/sokol/tests/compile/sokol_fontstash.c
deleted file mode 100644
index 0232de8..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_fontstash.c
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "sokol_gfx.h"
-#include "sokol_gl.h"
-
-#define FONTSTASH_IMPLEMENTATION
-#if defined(_MSC_VER )
-#pragma warning(disable:4996) // strncpy use in fontstash.h
-#endif
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-function"
-#pragma GCC diagnostic ignored "-Wsign-conversion"
-#endif
-#include // malloc/free
-#include "fontstash.h"
-#define SOKOL_IMPL
-#include "sokol_fontstash.h"
-
-void use_fontstash_impl(void) {
- FONScontext* ctx = sfons_create(&(sfons_desc_t){ 0 });
- sfons_destroy(ctx);
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_fontstash.cc b/source/engine/thirdparty/sokol/tests/compile/sokol_fontstash.cc
deleted file mode 100644
index 96d7c31..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_fontstash.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "sokol_gfx.h"
-#include "sokol_gl.h"
-
-#define FONTSTASH_IMPLEMENTATION
-#if defined(_MSC_VER )
-#pragma warning(disable:4996) // strncpy use in fontstash.h
-#pragma warning(disable:4505) // unreferenced local function has been removed
-#pragma warning(disable:4100) // unreferenced formal parameter
-#endif
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic ignored "-Wunused-function"
-#pragma GCC diagnostic ignored "-Wsign-conversion"
-#endif
-#include // malloc/free
-#include "fontstash.h"
-#define SOKOL_IMPL
-#include "sokol_fontstash.h"
-
-void use_fontstash_impl() {
- const sfons_desc_t desc = { };
- FONScontext* ctx = sfons_create(&desc);
- sfons_destroy(ctx);
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_gfx.c b/source/engine/thirdparty/sokol/tests/compile/sokol_gfx.c
deleted file mode 100644
index 8926310..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_gfx.c
+++ /dev/null
@@ -1,6 +0,0 @@
-#define SOKOL_IMPL
-#include "sokol_gfx.h"
-
-void use_gfx_impl(void) {
- sg_setup(&(sg_desc){0});
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_gfx.cc b/source/engine/thirdparty/sokol/tests/compile/sokol_gfx.cc
deleted file mode 100644
index 58f6592..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_gfx.cc
+++ /dev/null
@@ -1,6 +0,0 @@
-#define SOKOL_IMPL
-#include "sokol_gfx.h"
-
-void use_gfx_impl() {
- sg_setup({});
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_gfx_imgui.c b/source/engine/thirdparty/sokol/tests/compile/sokol_gfx_imgui.c
deleted file mode 100644
index 5b59501..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_gfx_imgui.c
+++ /dev/null
@@ -1,17 +0,0 @@
-#include "sokol_app.h"
-#include "sokol_gfx.h"
-#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS
-#if defined(_MSC_VER )
-#pragma warning(disable:4201) // nonstandard extension used: nameless struct/union
-#pragma warning(disable:4214) // nonstandard extension used: bit field types other than int
-#endif
-#include "cimgui/cimgui.h"
-#include "sokol_imgui.h"
-#define SOKOL_IMPL
-#include "sokol_gfx_imgui.h"
-
-void use_gfx_imgui_impl(void) {
- sgimgui_t ctx = {0};
- sgimgui_init(&ctx, &(sgimgui_desc_t){0});
- sgimgui_discard(&ctx);
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_gfx_imgui.cc b/source/engine/thirdparty/sokol/tests/compile/sokol_gfx_imgui.cc
deleted file mode 100644
index 2e64dbc..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_gfx_imgui.cc
+++ /dev/null
@@ -1,12 +0,0 @@
-#include "sokol_app.h"
-#include "sokol_gfx.h"
-#include "imgui.h"
-#include "sokol_imgui.h"
-#define SOKOL_IMPL
-#include "sokol_gfx_imgui.h"
-
-void use_gfx_imgui_impl() {
- sgimgui_t ctx = {};
- sgimgui_init(&ctx, { });
- sgimgui_discard(&ctx);
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_gl.c b/source/engine/thirdparty/sokol/tests/compile/sokol_gl.c
deleted file mode 100644
index 6dbeea1..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_gl.c
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "sokol_gfx.h"
-#define SOKOL_IMPL
-#include "sokol_gl.h"
-
-void use_gl_impl(void) {
- sgl_setup(&(sgl_desc_t){0});
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_gl.cc b/source/engine/thirdparty/sokol/tests/compile/sokol_gl.cc
deleted file mode 100644
index a4177cb..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_gl.cc
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "sokol_gfx.h"
-#define SOKOL_IMPL
-#include "sokol_gl.h"
-
-void use_gl_impl() {
- sgl_setup({});
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_glue.c b/source/engine/thirdparty/sokol/tests/compile/sokol_glue.c
deleted file mode 100644
index 114b6e2..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_glue.c
+++ /dev/null
@@ -1,9 +0,0 @@
-#include "sokol_app.h"
-#include "sokol_gfx.h"
-#define SOKOL_IMPL
-#include "sokol_glue.h"
-
-void use_glue_impl(void) {
- const sg_environment env = sglue_environment();
- (void)env;
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_glue.cc b/source/engine/thirdparty/sokol/tests/compile/sokol_glue.cc
deleted file mode 100644
index 4aaf21c..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_glue.cc
+++ /dev/null
@@ -1,9 +0,0 @@
-#include "sokol_app.h"
-#include "sokol_gfx.h"
-#define SOKOL_IMPL
-#include "sokol_glue.h"
-
-void use_glue_impl() {
- const sg_environment ctx = sglue_environment();
- (void)ctx;
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_imgui.c b/source/engine/thirdparty/sokol/tests/compile/sokol_imgui.c
deleted file mode 100644
index 90f0bb7..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_imgui.c
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "sokol_app.h"
-#include "sokol_gfx.h"
-#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS
-#if defined(_MSC_VER )
-#pragma warning(disable:4201) // nonstandard extension used: nameless struct/union
-#pragma warning(disable:4214) // nonstandard extension used: bit field types other than int
-#endif
-#include "cimgui/cimgui.h"
-#define SOKOL_IMPL
-#if defined(SOKOL_DUMMY_BACKEND)
-#define SOKOL_IMGUI_NO_SOKOL_APP
-#endif
-#include "sokol_imgui.h"
-
-void use_imgui_impl(void) {
- simgui_setup(&(simgui_desc_t){0});
-}
-
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_imgui.cc b/source/engine/thirdparty/sokol/tests/compile/sokol_imgui.cc
deleted file mode 100644
index 5a6a32f..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_imgui.cc
+++ /dev/null
@@ -1,12 +0,0 @@
-#include "sokol_app.h"
-#include "sokol_gfx.h"
-#include "imgui.h"
-#define SOKOL_IMPL
-#if defined(SOKOL_DUMMY_BACKEND)
-#define SOKOL_IMGUI_NO_SOKOL_APP
-#endif
-#include "sokol_imgui.h"
-
-void use_imgui_impl() {
- simgui_setup({});
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_log.c b/source/engine/thirdparty/sokol/tests/compile/sokol_log.c
deleted file mode 100644
index ac66ec5..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_log.c
+++ /dev/null
@@ -1,6 +0,0 @@
-#define SOKOL_IMPL
-#include "sokol_log.h"
-
-void use_sokol_log(void) {
- slog_func("bla", 1, 123, "123", 42, "bla.c", 0);
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_log.cc b/source/engine/thirdparty/sokol/tests/compile/sokol_log.cc
deleted file mode 100644
index ac66ec5..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_log.cc
+++ /dev/null
@@ -1,6 +0,0 @@
-#define SOKOL_IMPL
-#include "sokol_log.h"
-
-void use_sokol_log(void) {
- slog_func("bla", 1, 123, "123", 42, "bla.c", 0);
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_main.c b/source/engine/thirdparty/sokol/tests/compile/sokol_main.c
deleted file mode 100644
index d1e34b6..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_main.c
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "sokol_app.h"
-
-#if defined(SOKOL_DUMMY_BACKEND)
-int main() {
- return 0;
-}
-#else
-sapp_desc sokol_main(int argc, char* argv[]) {
- (void)argc;
- (void)argv;
- return (sapp_desc){0};
-}
-#endif
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_main.cc b/source/engine/thirdparty/sokol/tests/compile/sokol_main.cc
deleted file mode 100644
index c27ed3e..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_main.cc
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "sokol_app.h"
-
-sapp_desc sokol_main(int argc, char* argv[]) {
- (void)argc;
- (void)argv;
- return { };
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_nuklear.c b/source/engine/thirdparty/sokol/tests/compile/sokol_nuklear.c
deleted file mode 100644
index e5311b4..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_nuklear.c
+++ /dev/null
@@ -1,22 +0,0 @@
-#include "sokol_app.h"
-#include "sokol_gfx.h"
-
-// include nuklear.h before the sokol_nuklear.h implementation
-#define NK_INCLUDE_FIXED_TYPES
-#define NK_INCLUDE_STANDARD_IO
-#define NK_INCLUDE_DEFAULT_ALLOCATOR
-#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
-#define NK_INCLUDE_FONT_BAKING
-#define NK_INCLUDE_DEFAULT_FONT
-#define NK_INCLUDE_STANDARD_VARARGS
-#include "nuklear.h"
-
-#define SOKOL_IMPL
-#if defined(SOKOL_DUMMY_BACKEND)
-#define SOKOL_NUKLEAR_NO_SOKOL_APP
-#endif
-#include "sokol_nuklear.h"
-
-void use_nuklear_impl(void) {
- snk_setup(&(snk_desc_t){0});
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_shape.c b/source/engine/thirdparty/sokol/tests/compile/sokol_shape.c
deleted file mode 100644
index 72c48b6..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_shape.c
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "sokol_gfx.h"
-#define SOKOL_IMPL
-#include "sokol_shape.h"
-
-void use_shape_impl(void) {
- sshape_plane_sizes(10);
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_shape.cc b/source/engine/thirdparty/sokol/tests/compile/sokol_shape.cc
deleted file mode 100644
index 0013ea6..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_shape.cc
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "sokol_gfx.h"
-#define SOKOL_IMPL
-#include "sokol_shape.h"
-
-void use_shape_impl() {
- sshape_plane_sizes(10);
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_spine.c b/source/engine/thirdparty/sokol/tests/compile/sokol_spine.c
deleted file mode 100644
index 4669003..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_spine.c
+++ /dev/null
@@ -1,8 +0,0 @@
-#include "sokol_gfx.h"
-#define SOKOL_IMPL
-#include "spine/spine.h"
-#include "sokol_spine.h"
-
-void use_sspine_impl(void) {
- sspine_setup(&(sspine_desc){0});
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_spine.cc b/source/engine/thirdparty/sokol/tests/compile/sokol_spine.cc
deleted file mode 100644
index e484dbe..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_spine.cc
+++ /dev/null
@@ -1,9 +0,0 @@
-#include "sokol_gfx.h"
-#define SOKOL_IMPL
-#include "spine/spine.h"
-#include "sokol_spine.h"
-
-void use_sspine_impl(void) {
- const sspine_desc desc = {};
- sspine_setup(&desc);
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_time.c b/source/engine/thirdparty/sokol/tests/compile/sokol_time.c
deleted file mode 100644
index 416e500..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_time.c
+++ /dev/null
@@ -1,6 +0,0 @@
-#define SOKOL_IMPL
-#include "sokol_time.h"
-
-void use_time_impl(void) {
- stm_setup();
-}
diff --git a/source/engine/thirdparty/sokol/tests/compile/sokol_time.cc b/source/engine/thirdparty/sokol/tests/compile/sokol_time.cc
deleted file mode 100644
index f0c19c9..0000000
--- a/source/engine/thirdparty/sokol/tests/compile/sokol_time.cc
+++ /dev/null
@@ -1,7 +0,0 @@
-#define SOKOL_IMPL
-#include "sokol_time.h"
-
-void use_time_impl() {
- stm_setup();
-}
-
diff --git a/source/engine/thirdparty/sokol/tests/ext/CMakeLists.txt b/source/engine/thirdparty/sokol/tests/ext/CMakeLists.txt
deleted file mode 100644
index 4ded7c4..0000000
--- a/source/engine/thirdparty/sokol/tests/ext/CMakeLists.txt
+++ /dev/null
@@ -1,97 +0,0 @@
-# external dependencies
-
-# NOTE FetchContent is so frigging slow that we just run git directly
-set(cimgui_dir ${CMAKE_BINARY_DIR}/../_deps/cimgui)
-set(spineruntimes_dir ${CMAKE_BINARY_DIR}/../_deps/spineruntimes)
-
-if (IS_DIRECTORY ${cimgui_dir})
- message("### ${cimgui_dir} exists...")
-else()
- message("### Fetching cimgui to ${cimgui_dir} (this may take a while...)")
- execute_process(COMMAND git clone --depth=1 --recursive https://github.com/fips-libs/fips-cimgui ${cimgui_dir})
-endif()
-if (IS_DIRECTORY ${spineruntimes_dir})
- message("### ${spineruntimes_dir} exists...")
-else()
- message("### Fetching spine runtimes to ${spineruntimes_dir} (this may take a while...)")
- execute_process(COMMAND git clone --depth=1 --recursive https://github.com/EsotericSoftware/spine-runtimes ${spineruntimes_dir})
-endif()
-
-add_library(cimgui
- ${cimgui_dir}/cimgui/cimgui.cpp
- ${cimgui_dir}/cimgui/imgui/imgui.cpp
- ${cimgui_dir}/cimgui/imgui/imgui_demo.cpp
- ${cimgui_dir}/cimgui/imgui/imgui_draw.cpp
- ${cimgui_dir}/cimgui/imgui/imgui_tables.cpp
- ${cimgui_dir}/cimgui/imgui/imgui_widgets.cpp)
-target_include_directories(cimgui SYSTEM PUBLIC ${cimgui_dir})
-
-add_library(imgui
- ${cimgui_dir}/cimgui/imgui/imgui.cpp
- ${cimgui_dir}/cimgui/imgui/imgui_demo.cpp
- ${cimgui_dir}/cimgui/imgui/imgui_draw.cpp
- ${cimgui_dir}/cimgui/imgui/imgui_tables.cpp
- ${cimgui_dir}/cimgui/imgui/imgui_widgets.cpp)
-target_include_directories(imgui SYSTEM PUBLIC ${cimgui_dir}/cimgui/imgui)
-
-add_library(spine
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/Animation.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/AnimationState.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/AnimationStateData.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/Array.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/Atlas.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/AtlasAttachmentLoader.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/Attachment.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/AttachmentLoader.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/Bone.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/BoneData.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/BoundingBoxAttachment.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/ClippingAttachment.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/Color.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/Debug.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/Event.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/EventData.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/IkConstraint.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/IkConstraintData.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/Json.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/Json.h
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/MeshAttachment.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/PathAttachment.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/PathConstraint.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/PathConstraintData.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/PointAttachment.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/RegionAttachment.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/Sequence.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/Skeleton.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/SkeletonBinary.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/SkeletonBounds.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/SkeletonClipping.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/SkeletonData.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/SkeletonJson.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/Skin.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/Slot.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/SlotData.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/TransformConstraint.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/TransformConstraintData.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/Triangulator.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/VertexAttachment.c
- ${spineruntimes_dir}/spine-c/spine-c/src/spine/extension.c)
-target_include_directories(spine SYSTEM PUBLIC ${spineruntimes_dir}/spine-c/spine-c/include)
-if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
- target_compile_options(spine PRIVATE /wd4267 /wd4244) # conversion from 'x' to 'y' possible loss of data
-endif()
-if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
- target_compile_options(spine PRIVATE -Wno-shorten-64-to-32)
-endif()
-
-file(COPY ${spineruntimes_dir}/examples/spineboy/export/spineboy-pro.json DESTINATION ${CMAKE_BINARY_DIR})
-file(COPY ${spineruntimes_dir}/examples/spineboy/export/spineboy-pro.skel DESTINATION ${CMAKE_BINARY_DIR})
-file(COPY ${spineruntimes_dir}/examples/spineboy/export/spineboy.atlas DESTINATION ${CMAKE_BINARY_DIR})
-file(COPY ${spineruntimes_dir}/examples/spineboy/export/spineboy.png DESTINATION ${CMAKE_BINARY_DIR})
-
-file(COPY ${spineruntimes_dir}/examples/spineboy/export/spineboy-pro.json DESTINATION ${CMAKE_BINARY_DIR}/Debug)
-file(COPY ${spineruntimes_dir}/examples/spineboy/export/spineboy-pro.skel DESTINATION ${CMAKE_BINARY_DIR}/Debug)
-file(COPY ${spineruntimes_dir}/examples/spineboy/export/spineboy.atlas DESTINATION ${CMAKE_BINARY_DIR}/Debug)
-file(COPY ${spineruntimes_dir}/examples/spineboy/export/spineboy.png DESTINATION ${CMAKE_BINARY_DIR}/Debug)
-
-add_library(nuklear nuklear.c)
diff --git a/source/engine/thirdparty/sokol/tests/ext/fontstash.h b/source/engine/thirdparty/sokol/tests/ext/fontstash.h
deleted file mode 100644
index a09e6f1..0000000
--- a/source/engine/thirdparty/sokol/tests/ext/fontstash.h
+++ /dev/null
@@ -1,1714 +0,0 @@
-//
-// NOTE sokol: all IO functions have been removed
-//
-// Copyright (c) 2009-2013 Mikko Mononen memon@inside.org
-//
-// This software is provided 'as-is', without any express or implied
-// warranty. In no event will the authors be held liable for any damages
-// arising from the use of this software.
-// Permission is granted to anyone to use this software for any purpose,
-// including commercial applications, and to alter it and redistribute it
-// freely, subject to the following restrictions:
-// 1. The origin of this software must not be misrepresented; you must not
-// claim that you wrote the original software. If you use this software
-// in a product, an acknowledgment in the product documentation would be
-// appreciated but is not required.
-// 2. Altered source versions must be plainly marked as such, and must not be
-// misrepresented as being the original software.
-// 3. This notice may not be removed or altered from any source distribution.
-//
-
-#ifndef FONS_H
-#define FONS_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// To make the implementation private to the file that generates the implementation
-#ifdef FONS_STATIC
-#define FONS_DEF static
-#else
-#define FONS_DEF extern
-#endif
-
-#define FONS_INVALID -1
-
-enum FONSflags {
- FONS_ZERO_TOPLEFT = 1,
- FONS_ZERO_BOTTOMLEFT = 2,
-};
-
-enum FONSalign {
- // Horizontal align
- FONS_ALIGN_LEFT = 1<<0, // Default
- FONS_ALIGN_CENTER = 1<<1,
- FONS_ALIGN_RIGHT = 1<<2,
- // Vertical align
- FONS_ALIGN_TOP = 1<<3,
- FONS_ALIGN_MIDDLE = 1<<4,
- FONS_ALIGN_BOTTOM = 1<<5,
- FONS_ALIGN_BASELINE = 1<<6, // Default
-};
-
-enum FONSerrorCode {
- // Font atlas is full.
- FONS_ATLAS_FULL = 1,
- // Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE.
- FONS_SCRATCH_FULL = 2,
- // Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES.
- FONS_STATES_OVERFLOW = 3,
- // Trying to pop too many states fonsPopState().
- FONS_STATES_UNDERFLOW = 4,
-};
-
-struct FONSparams {
- int width, height;
- unsigned char flags;
- void* userPtr;
- int (*renderCreate)(void* uptr, int width, int height);
- int (*renderResize)(void* uptr, int width, int height);
- void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data);
- void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts);
- void (*renderDelete)(void* uptr);
-};
-typedef struct FONSparams FONSparams;
-
-struct FONSquad
-{
- float x0,y0,s0,t0;
- float x1,y1,s1,t1;
-};
-typedef struct FONSquad FONSquad;
-
-struct FONStextIter {
- float x, y, nextx, nexty, scale, spacing;
- unsigned int codepoint;
- short isize, iblur;
- struct FONSfont* font;
- int prevGlyphIndex;
- const char* str;
- const char* next;
- const char* end;
- unsigned int utf8state;
-};
-typedef struct FONStextIter FONStextIter;
-
-typedef struct FONScontext FONScontext;
-
-// Contructor and destructor.
-FONS_DEF FONScontext* fonsCreateInternal(FONSparams* params);
-FONS_DEF void fonsDeleteInternal(FONScontext* s);
-
-FONS_DEF void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr);
-// Returns current atlas size.
-FONS_DEF void fonsGetAtlasSize(FONScontext* s, int* width, int* height);
-// Expands the atlas size.
-FONS_DEF int fonsExpandAtlas(FONScontext* s, int width, int height);
-// Resets the whole stash.
-FONS_DEF int fonsResetAtlas(FONScontext* stash, int width, int height);
-
-// Add fonts
-FONS_DEF int fonsGetFontByName(FONScontext* s, const char* name);
-FONS_DEF int fonsAddFallbackFont(FONScontext* stash, int base, int fallback);
-
-// State handling
-FONS_DEF void fonsPushState(FONScontext* s);
-FONS_DEF void fonsPopState(FONScontext* s);
-FONS_DEF void fonsClearState(FONScontext* s);
-
-// State setting
-FONS_DEF void fonsSetSize(FONScontext* s, float size);
-FONS_DEF void fonsSetColor(FONScontext* s, unsigned int color);
-FONS_DEF void fonsSetSpacing(FONScontext* s, float spacing);
-FONS_DEF void fonsSetBlur(FONScontext* s, float blur);
-FONS_DEF void fonsSetAlign(FONScontext* s, int align);
-FONS_DEF void fonsSetFont(FONScontext* s, int font);
-
-// Draw text
-FONS_DEF float fonsDrawText(FONScontext* s, float x, float y, const char* string, const char* end);
-
-// Measure text
-FONS_DEF float fonsTextBounds(FONScontext* s, float x, float y, const char* string, const char* end, float* bounds);
-FONS_DEF void fonsLineBounds(FONScontext* s, float y, float* miny, float* maxy);
-FONS_DEF void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh);
-
-// Text iterator
-FONS_DEF int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end);
-FONS_DEF int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad);
-
-// Pull texture changes
-FONS_DEF const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height);
-FONS_DEF int fonsValidateTexture(FONScontext* s, int* dirty);
-
-// Draws the stash texture for debugging
-FONS_DEF void fonsDrawDebug(FONScontext* s, float x, float y);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // FONS_H
-
-
-#ifdef FONTSTASH_IMPLEMENTATION
-
-#define FONS_NOTUSED(v) (void)sizeof(v)
-
-#ifdef FONS_USE_FREETYPE
-
-#include
-#include FT_FREETYPE_H
-#include FT_ADVANCES_H
-#include
-
-struct FONSttFontImpl {
- FT_Face font;
-};
-typedef struct FONSttFontImpl FONSttFontImpl;
-
-static FT_Library ftLibrary;
-
-static int fons__tt_init()
-{
- FT_Error ftError;
- FONS_NOTUSED(context);
- ftError = FT_Init_FreeType(&ftLibrary);
- return ftError == 0;
-}
-
-static int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize)
-{
- FT_Error ftError;
- FONS_NOTUSED(context);
-
- //font->font.userdata = stash;
- ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, 0, &font->font);
- return ftError == 0;
-}
-
-static void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap)
-{
- *ascent = font->font->ascender;
- *descent = font->font->descender;
- *lineGap = font->font->height - (*ascent - *descent);
-}
-
-static float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size)
-{
- return size / (font->font->ascender - font->font->descender);
-}
-
-static int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint)
-{
- return FT_Get_Char_Index(font->font, codepoint);
-}
-
-static int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale,
- int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1)
-{
- FT_Error ftError;
- FT_GlyphSlot ftGlyph;
- FT_Fixed advFixed;
- FONS_NOTUSED(scale);
-
- ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender)));
- if (ftError) return 0;
- ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER);
- if (ftError) return 0;
- ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, &advFixed);
- if (ftError) return 0;
- ftGlyph = font->font->glyph;
- *advance = (int)advFixed;
- *lsb = (int)ftGlyph->metrics.horiBearingX;
- *x0 = ftGlyph->bitmap_left;
- *x1 = *x0 + ftGlyph->bitmap.width;
- *y0 = -ftGlyph->bitmap_top;
- *y1 = *y0 + ftGlyph->bitmap.rows;
- return 1;
-}
-
-static void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride,
- float scaleX, float scaleY, int glyph)
-{
- FT_GlyphSlot ftGlyph = font->font->glyph;
- int ftGlyphOffset = 0;
- int x, y;
- FONS_NOTUSED(outWidth);
- FONS_NOTUSED(outHeight);
- FONS_NOTUSED(scaleX);
- FONS_NOTUSED(scaleY);
- FONS_NOTUSED(glyph); // glyph has already been loaded by fons__tt_buildGlyphBitmap
-
- for ( y = 0; y < ftGlyph->bitmap.rows; y++ ) {
- for ( x = 0; x < ftGlyph->bitmap.width; x++ ) {
- output[(y * outStride) + x] = ftGlyph->bitmap.buffer[ftGlyphOffset++];
- }
- }
-}
-
-static int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2)
-{
- FT_Vector ftKerning;
- FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning);
- return (int)((ftKerning.x + 32) >> 6); // Round up and convert to integer
-}
-
-#else
-
-#define STB_TRUETYPE_IMPLEMENTATION
-#define STBTT_STATIC
-static void* fons__tmpalloc(size_t size, void* up);
-static void fons__tmpfree(void* ptr, void* up);
-#define STBTT_malloc(x,u) fons__tmpalloc(x,u)
-#define STBTT_free(x,u) fons__tmpfree(x,u)
-#include "stb_truetype.h"
-
-struct FONSttFontImpl {
- stbtt_fontinfo font;
-};
-typedef struct FONSttFontImpl FONSttFontImpl;
-
-static int fons__tt_init(FONScontext *context)
-{
- FONS_NOTUSED(context);
- return 1;
-}
-
-static int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize)
-{
- int stbError;
- FONS_NOTUSED(dataSize);
-
- font->font.userdata = context;
- stbError = stbtt_InitFont(&font->font, data, 0);
- return stbError;
-}
-
-static void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap)
-{
- stbtt_GetFontVMetrics(&font->font, ascent, descent, lineGap);
-}
-
-static float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size)
-{
- return stbtt_ScaleForPixelHeight(&font->font, size);
-}
-
-static int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint)
-{
- return stbtt_FindGlyphIndex(&font->font, codepoint);
-}
-
-static int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale,
- int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1)
-{
- FONS_NOTUSED(size);
- stbtt_GetGlyphHMetrics(&font->font, glyph, advance, lsb);
- stbtt_GetGlyphBitmapBox(&font->font, glyph, scale, scale, x0, y0, x1, y1);
- return 1;
-}
-
-static void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride,
- float scaleX, float scaleY, int glyph)
-{
- stbtt_MakeGlyphBitmap(&font->font, output, outWidth, outHeight, outStride, scaleX, scaleY, glyph);
-}
-
-static int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2)
-{
- return stbtt_GetGlyphKernAdvance(&font->font, glyph1, glyph2);
-}
-
-#endif
-
-#ifndef FONS_SCRATCH_BUF_SIZE
-# define FONS_SCRATCH_BUF_SIZE 64000
-#endif
-#ifndef FONS_HASH_LUT_SIZE
-# define FONS_HASH_LUT_SIZE 256
-#endif
-#ifndef FONS_INIT_FONTS
-# define FONS_INIT_FONTS 4
-#endif
-#ifndef FONS_INIT_GLYPHS
-# define FONS_INIT_GLYPHS 256
-#endif
-#ifndef FONS_INIT_ATLAS_NODES
-# define FONS_INIT_ATLAS_NODES 256
-#endif
-#ifndef FONS_VERTEX_COUNT
-# define FONS_VERTEX_COUNT 1024
-#endif
-#ifndef FONS_MAX_STATES
-# define FONS_MAX_STATES 20
-#endif
-#ifndef FONS_MAX_FALLBACKS
-# define FONS_MAX_FALLBACKS 20
-#endif
-
-static unsigned int fons__hashint(unsigned int a)
-{
- a += ~(a<<15);
- a ^= (a>>10);
- a += (a<<3);
- a ^= (a>>6);
- a += ~(a<<11);
- a ^= (a>>16);
- return a;
-}
-
-static int fons__mini(int a, int b)
-{
- return a < b ? a : b;
-}
-
-static int fons__maxi(int a, int b)
-{
- return a > b ? a : b;
-}
-
-struct FONSglyph
-{
- unsigned int codepoint;
- int index;
- int next;
- short size, blur;
- short x0,y0,x1,y1;
- short xadv,xoff,yoff;
-};
-typedef struct FONSglyph FONSglyph;
-
-struct FONSfont
-{
- FONSttFontImpl font;
- char name[64];
- unsigned char* data;
- int dataSize;
- unsigned char freeData;
- float ascender;
- float descender;
- float lineh;
- FONSglyph* glyphs;
- int cglyphs;
- int nglyphs;
- int lut[FONS_HASH_LUT_SIZE];
- int fallbacks[FONS_MAX_FALLBACKS];
- int nfallbacks;
-};
-typedef struct FONSfont FONSfont;
-
-struct FONSstate
-{
- int font;
- int align;
- float size;
- unsigned int color;
- float blur;
- float spacing;
-};
-typedef struct FONSstate FONSstate;
-
-struct FONSatlasNode {
- short x, y, width;
-};
-typedef struct FONSatlasNode FONSatlasNode;
-
-struct FONSatlas
-{
- int width, height;
- FONSatlasNode* nodes;
- int nnodes;
- int cnodes;
-};
-typedef struct FONSatlas FONSatlas;
-
-struct FONScontext
-{
- FONSparams params;
- float itw,ith;
- unsigned char* texData;
- int dirtyRect[4];
- FONSfont** fonts;
- FONSatlas* atlas;
- int cfonts;
- int nfonts;
- float verts[FONS_VERTEX_COUNT*2];
- float tcoords[FONS_VERTEX_COUNT*2];
- unsigned int colors[FONS_VERTEX_COUNT];
- int nverts;
- unsigned char* scratch;
- int nscratch;
- FONSstate states[FONS_MAX_STATES];
- int nstates;
- void (*handleError)(void* uptr, int error, int val);
- void* errorUptr;
-};
-
-#ifdef STB_TRUETYPE_IMPLEMENTATION
-
-static void* fons__tmpalloc(size_t size, void* up)
-{
- unsigned char* ptr;
- FONScontext* stash = (FONScontext*)up;
-
- // 16-byte align the returned pointer
- size = (size + 0xf) & ~0xf;
-
- if (stash->nscratch+(int)size > FONS_SCRATCH_BUF_SIZE) {
- if (stash->handleError)
- stash->handleError(stash->errorUptr, FONS_SCRATCH_FULL, stash->nscratch+(int)size);
- return NULL;
- }
- ptr = stash->scratch + stash->nscratch;
- stash->nscratch += (int)size;
- return ptr;
-}
-
-static void fons__tmpfree(void* ptr, void* up)
-{
- (void)ptr;
- (void)up;
- // empty
-}
-
-#endif // STB_TRUETYPE_IMPLEMENTATION
-
-// Copyright (c) 2008-2010 Bjoern Hoehrmann
-// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
-
-#define FONS_UTF8_ACCEPT 0
-#define FONS_UTF8_REJECT 12
-
-static unsigned int fons__decutf8(unsigned int* state, unsigned int* codep, unsigned int byte)
-{
- static const unsigned char utf8d[] = {
- // The first part of the table maps bytes to character classes that
- // to reduce the size of the transition table and create bitmasks.
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
- 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
- 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
- 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
-
- // The second part is a transition table that maps a combination
- // of a state of the automaton and a character class to a state.
- 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
- 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
- 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
- 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
- 12,36,12,12,12,12,12,12,12,12,12,12,
- };
-
- unsigned int type = utf8d[byte];
-
- *codep = (*state != FONS_UTF8_ACCEPT) ?
- (byte & 0x3fu) | (*codep << 6) :
- (0xff >> type) & (byte);
-
- *state = utf8d[256 + *state + type];
- return *state;
-}
-
-// Atlas based on Skyline Bin Packer by Jukka Jylänki
-
-static void fons__deleteAtlas(FONSatlas* atlas)
-{
- if (atlas == NULL) return;
- if (atlas->nodes != NULL) free(atlas->nodes);
- free(atlas);
-}
-
-static FONSatlas* fons__allocAtlas(int w, int h, int nnodes)
-{
- FONSatlas* atlas = NULL;
-
- // Allocate memory for the font stash.
- atlas = (FONSatlas*)malloc(sizeof(FONSatlas));
- if (atlas == NULL) goto error;
- memset(atlas, 0, sizeof(FONSatlas));
-
- atlas->width = w;
- atlas->height = h;
-
- // Allocate space for skyline nodes
- atlas->nodes = (FONSatlasNode*)malloc(sizeof(FONSatlasNode) * nnodes);
- if (atlas->nodes == NULL) goto error;
- memset(atlas->nodes, 0, sizeof(FONSatlasNode) * nnodes);
- atlas->nnodes = 0;
- atlas->cnodes = nnodes;
-
- // Init root node.
- atlas->nodes[0].x = 0;
- atlas->nodes[0].y = 0;
- atlas->nodes[0].width = (short)w;
- atlas->nnodes++;
-
- return atlas;
-
-error:
- if (atlas) fons__deleteAtlas(atlas);
- return NULL;
-}
-
-static int fons__atlasInsertNode(FONSatlas* atlas, int idx, int x, int y, int w)
-{
- int i;
- // Insert node
- if (atlas->nnodes+1 > atlas->cnodes) {
- atlas->cnodes = atlas->cnodes == 0 ? 8 : atlas->cnodes * 2;
- atlas->nodes = (FONSatlasNode*)realloc(atlas->nodes, sizeof(FONSatlasNode) * atlas->cnodes);
- if (atlas->nodes == NULL)
- return 0;
- }
- for (i = atlas->nnodes; i > idx; i--)
- atlas->nodes[i] = atlas->nodes[i-1];
- atlas->nodes[idx].x = (short)x;
- atlas->nodes[idx].y = (short)y;
- atlas->nodes[idx].width = (short)w;
- atlas->nnodes++;
-
- return 1;
-}
-
-static void fons__atlasRemoveNode(FONSatlas* atlas, int idx)
-{
- int i;
- if (atlas->nnodes == 0) return;
- for (i = idx; i < atlas->nnodes-1; i++)
- atlas->nodes[i] = atlas->nodes[i+1];
- atlas->nnodes--;
-}
-
-static void fons__atlasExpand(FONSatlas* atlas, int w, int h)
-{
- // Insert node for empty space
- if (w > atlas->width)
- fons__atlasInsertNode(atlas, atlas->nnodes, atlas->width, 0, w - atlas->width);
- atlas->width = w;
- atlas->height = h;
-}
-
-static void fons__atlasReset(FONSatlas* atlas, int w, int h)
-{
- atlas->width = w;
- atlas->height = h;
- atlas->nnodes = 0;
-
- // Init root node.
- atlas->nodes[0].x = 0;
- atlas->nodes[0].y = 0;
- atlas->nodes[0].width = (short)w;
- atlas->nnodes++;
-}
-
-static int fons__atlasAddSkylineLevel(FONSatlas* atlas, int idx, int x, int y, int w, int h)
-{
- int i;
-
- // Insert new node
- if (fons__atlasInsertNode(atlas, idx, x, y+h, w) == 0)
- return 0;
-
- // Delete skyline segments that fall under the shadow of the new segment.
- for (i = idx+1; i < atlas->nnodes; i++) {
- if (atlas->nodes[i].x < atlas->nodes[i-1].x + atlas->nodes[i-1].width) {
- int shrink = atlas->nodes[i-1].x + atlas->nodes[i-1].width - atlas->nodes[i].x;
- atlas->nodes[i].x += (short)shrink;
- atlas->nodes[i].width -= (short)shrink;
- if (atlas->nodes[i].width <= 0) {
- fons__atlasRemoveNode(atlas, i);
- i--;
- } else {
- break;
- }
- } else {
- break;
- }
- }
-
- // Merge same height skyline segments that are next to each other.
- for (i = 0; i < atlas->nnodes-1; i++) {
- if (atlas->nodes[i].y == atlas->nodes[i+1].y) {
- atlas->nodes[i].width += atlas->nodes[i+1].width;
- fons__atlasRemoveNode(atlas, i+1);
- i--;
- }
- }
-
- return 1;
-}
-
-static int fons__atlasRectFits(FONSatlas* atlas, int i, int w, int h)
-{
- // Checks if there is enough space at the location of skyline span 'i',
- // and return the max height of all skyline spans under that at that location,
- // (think tetris block being dropped at that position). Or -1 if no space found.
- int x = atlas->nodes[i].x;
- int y = atlas->nodes[i].y;
- int spaceLeft;
- if (x + w > atlas->width)
- return -1;
- spaceLeft = w;
- while (spaceLeft > 0) {
- if (i == atlas->nnodes) return -1;
- y = fons__maxi(y, atlas->nodes[i].y);
- if (y + h > atlas->height) return -1;
- spaceLeft -= atlas->nodes[i].width;
- ++i;
- }
- return y;
-}
-
-static int fons__atlasAddRect(FONSatlas* atlas, int rw, int rh, int* rx, int* ry)
-{
- int besth = atlas->height, bestw = atlas->width, besti = -1;
- int bestx = -1, besty = -1, i;
-
- // Bottom left fit heuristic.
- for (i = 0; i < atlas->nnodes; i++) {
- int y = fons__atlasRectFits(atlas, i, rw, rh);
- if (y != -1) {
- if (y + rh < besth || (y + rh == besth && atlas->nodes[i].width < bestw)) {
- besti = i;
- bestw = atlas->nodes[i].width;
- besth = y + rh;
- bestx = atlas->nodes[i].x;
- besty = y;
- }
- }
- }
-
- if (besti == -1)
- return 0;
-
- // Perform the actual packing.
- if (fons__atlasAddSkylineLevel(atlas, besti, bestx, besty, rw, rh) == 0)
- return 0;
-
- *rx = bestx;
- *ry = besty;
-
- return 1;
-}
-
-static void fons__addWhiteRect(FONScontext* stash, int w, int h)
-{
- int x, y, gx, gy;
- unsigned char* dst;
- if (fons__atlasAddRect(stash->atlas, w, h, &gx, &gy) == 0)
- return;
-
- // Rasterize
- dst = &stash->texData[gx + gy * stash->params.width];
- for (y = 0; y < h; y++) {
- for (x = 0; x < w; x++)
- dst[x] = 0xff;
- dst += stash->params.width;
- }
-
- stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], gx);
- stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], gy);
- stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], gx+w);
- stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], gy+h);
-}
-
-FONScontext* fonsCreateInternal(FONSparams* params)
-{
- FONScontext* stash = NULL;
-
- // Allocate memory for the font stash.
- stash = (FONScontext*)malloc(sizeof(FONScontext));
- if (stash == NULL) goto error;
- memset(stash, 0, sizeof(FONScontext));
-
- stash->params = *params;
-
- // Allocate scratch buffer.
- stash->scratch = (unsigned char*)malloc(FONS_SCRATCH_BUF_SIZE);
- if (stash->scratch == NULL) goto error;
-
- // Initialize implementation library
- if (!fons__tt_init(stash)) goto error;
-
- if (stash->params.renderCreate != NULL) {
- if (stash->params.renderCreate(stash->params.userPtr, stash->params.width, stash->params.height) == 0)
- goto error;
- }
-
- stash->atlas = fons__allocAtlas(stash->params.width, stash->params.height, FONS_INIT_ATLAS_NODES);
- if (stash->atlas == NULL) goto error;
-
- // Allocate space for fonts.
- stash->fonts = (FONSfont**)malloc(sizeof(FONSfont*) * FONS_INIT_FONTS);
- if (stash->fonts == NULL) goto error;
- memset(stash->fonts, 0, sizeof(FONSfont*) * FONS_INIT_FONTS);
- stash->cfonts = FONS_INIT_FONTS;
- stash->nfonts = 0;
-
- // Create texture for the cache.
- stash->itw = 1.0f/stash->params.width;
- stash->ith = 1.0f/stash->params.height;
- stash->texData = (unsigned char*)malloc(stash->params.width * stash->params.height);
- if (stash->texData == NULL) goto error;
- memset(stash->texData, 0, stash->params.width * stash->params.height);
-
- stash->dirtyRect[0] = stash->params.width;
- stash->dirtyRect[1] = stash->params.height;
- stash->dirtyRect[2] = 0;
- stash->dirtyRect[3] = 0;
-
- // Add white rect at 0,0 for debug drawing.
- fons__addWhiteRect(stash, 2,2);
-
- fonsPushState(stash);
- fonsClearState(stash);
-
- return stash;
-
-error:
- fonsDeleteInternal(stash);
- return NULL;
-}
-
-static FONSstate* fons__getState(FONScontext* stash)
-{
- return &stash->states[stash->nstates-1];
-}
-
-int fonsAddFallbackFont(FONScontext* stash, int base, int fallback)
-{
- FONSfont* baseFont = stash->fonts[base];
- if (baseFont->nfallbacks < FONS_MAX_FALLBACKS) {
- baseFont->fallbacks[baseFont->nfallbacks++] = fallback;
- return 1;
- }
- return 0;
-}
-
-void fonsSetSize(FONScontext* stash, float size)
-{
- fons__getState(stash)->size = size;
-}
-
-void fonsSetColor(FONScontext* stash, unsigned int color)
-{
- fons__getState(stash)->color = color;
-}
-
-void fonsSetSpacing(FONScontext* stash, float spacing)
-{
- fons__getState(stash)->spacing = spacing;
-}
-
-void fonsSetBlur(FONScontext* stash, float blur)
-{
- fons__getState(stash)->blur = blur;
-}
-
-void fonsSetAlign(FONScontext* stash, int align)
-{
- fons__getState(stash)->align = align;
-}
-
-void fonsSetFont(FONScontext* stash, int font)
-{
- fons__getState(stash)->font = font;
-}
-
-void fonsPushState(FONScontext* stash)
-{
- if (stash->nstates >= FONS_MAX_STATES) {
- if (stash->handleError)
- stash->handleError(stash->errorUptr, FONS_STATES_OVERFLOW, 0);
- return;
- }
- if (stash->nstates > 0)
- memcpy(&stash->states[stash->nstates], &stash->states[stash->nstates-1], sizeof(FONSstate));
- stash->nstates++;
-}
-
-void fonsPopState(FONScontext* stash)
-{
- if (stash->nstates <= 1) {
- if (stash->handleError)
- stash->handleError(stash->errorUptr, FONS_STATES_UNDERFLOW, 0);
- return;
- }
- stash->nstates--;
-}
-
-void fonsClearState(FONScontext* stash)
-{
- FONSstate* state = fons__getState(stash);
- state->size = 12.0f;
- state->color = 0xffffffff;
- state->font = 0;
- state->blur = 0;
- state->spacing = 0;
- state->align = FONS_ALIGN_LEFT | FONS_ALIGN_BASELINE;
-}
-
-static void fons__freeFont(FONSfont* font)
-{
- if (font == NULL) return;
- if (font->glyphs) free(font->glyphs);
- if (font->freeData && font->data) free(font->data);
- free(font);
-}
-
-static int fons__allocFont(FONScontext* stash)
-{
- FONSfont* font = NULL;
- if (stash->nfonts+1 > stash->cfonts) {
- stash->cfonts = stash->cfonts == 0 ? 8 : stash->cfonts * 2;
- stash->fonts = (FONSfont**)realloc(stash->fonts, sizeof(FONSfont*) * stash->cfonts);
- if (stash->fonts == NULL)
- return -1;
- }
- font = (FONSfont*)malloc(sizeof(FONSfont));
- if (font == NULL) goto error;
- memset(font, 0, sizeof(FONSfont));
-
- font->glyphs = (FONSglyph*)malloc(sizeof(FONSglyph) * FONS_INIT_GLYPHS);
- if (font->glyphs == NULL) goto error;
- font->cglyphs = FONS_INIT_GLYPHS;
- font->nglyphs = 0;
-
- stash->fonts[stash->nfonts++] = font;
- return stash->nfonts-1;
-
-error:
- fons__freeFont(font);
-
- return FONS_INVALID;
-}
-
-int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData)
-{
- int i, ascent, descent, fh, lineGap;
- FONSfont* font;
-
- int idx = fons__allocFont(stash);
- if (idx == FONS_INVALID)
- return FONS_INVALID;
-
- font = stash->fonts[idx];
-
- strncpy(font->name, name, sizeof(font->name));
- font->name[sizeof(font->name)-1] = '\0';
-
- // Init hash lookup.
- for (i = 0; i < FONS_HASH_LUT_SIZE; ++i)
- font->lut[i] = -1;
-
- // Read in the font data.
- font->dataSize = dataSize;
- font->data = data;
- font->freeData = (unsigned char)freeData;
-
- // Init font
- stash->nscratch = 0;
- if (!fons__tt_loadFont(stash, &font->font, data, dataSize)) goto error;
-
- // Store normalized line height. The real line height is got
- // by multiplying the lineh by font size.
- fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap);
- fh = ascent - descent;
- font->ascender = (float)ascent / (float)fh;
- font->descender = (float)descent / (float)fh;
- font->lineh = (float)(fh + lineGap) / (float)fh;
-
- return idx;
-
-error:
- fons__freeFont(font);
- stash->nfonts--;
- return FONS_INVALID;
-}
-
-int fonsGetFontByName(FONScontext* s, const char* name)
-{
- int i;
- for (i = 0; i < s->nfonts; i++) {
- if (strcmp(s->fonts[i]->name, name) == 0)
- return i;
- }
- return FONS_INVALID;
-}
-
-
-static FONSglyph* fons__allocGlyph(FONSfont* font)
-{
- if (font->nglyphs+1 > font->cglyphs) {
- font->cglyphs = font->cglyphs == 0 ? 8 : font->cglyphs * 2;
- font->glyphs = (FONSglyph*)realloc(font->glyphs, sizeof(FONSglyph) * font->cglyphs);
- if (font->glyphs == NULL) return NULL;
- }
- font->nglyphs++;
- return &font->glyphs[font->nglyphs-1];
-}
-
-
-// Based on Exponential blur, Jani Huhtanen, 2006
-
-#define APREC 16
-#define ZPREC 7
-
-static void fons__blurCols(unsigned char* dst, int w, int h, int dstStride, int alpha)
-{
- int x, y;
- for (y = 0; y < h; y++) {
- int z = 0; // force zero border
- for (x = 1; x < w; x++) {
- z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC;
- dst[x] = (unsigned char)(z >> ZPREC);
- }
- dst[w-1] = 0; // force zero border
- z = 0;
- for (x = w-2; x >= 0; x--) {
- z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC;
- dst[x] = (unsigned char)(z >> ZPREC);
- }
- dst[0] = 0; // force zero border
- dst += dstStride;
- }
-}
-
-static void fons__blurRows(unsigned char* dst, int w, int h, int dstStride, int alpha)
-{
- int x, y;
- for (x = 0; x < w; x++) {
- int z = 0; // force zero border
- for (y = dstStride; y < h*dstStride; y += dstStride) {
- z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC;
- dst[y] = (unsigned char)(z >> ZPREC);
- }
- dst[(h-1)*dstStride] = 0; // force zero border
- z = 0;
- for (y = (h-2)*dstStride; y >= 0; y -= dstStride) {
- z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC;
- dst[y] = (unsigned char)(z >> ZPREC);
- }
- dst[0] = 0; // force zero border
- dst++;
- }
-}
-
-
-static void fons__blur(FONScontext* stash, unsigned char* dst, int w, int h, int dstStride, int blur)
-{
- int alpha;
- float sigma;
- (void)stash;
-
- if (blur < 1)
- return;
- // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity)
- sigma = (float)blur * 0.57735f; // 1 / sqrt(3)
- alpha = (int)((1< 20) iblur = 20;
- pad = iblur+2;
-
- // Reset allocator.
- stash->nscratch = 0;
-
- // Find code point and size.
- h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1);
- i = font->lut[h];
- while (i != -1) {
- if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur)
- return &font->glyphs[i];
- i = font->glyphs[i].next;
- }
-
- // Could not find glyph, create it.
- g = fons__tt_getGlyphIndex(&font->font, codepoint);
- // Try to find the glyph in fallback fonts.
- if (g == 0) {
- for (i = 0; i < font->nfallbacks; ++i) {
- FONSfont* fallbackFont = stash->fonts[font->fallbacks[i]];
- int fallbackIndex = fons__tt_getGlyphIndex(&fallbackFont->font, codepoint);
- if (fallbackIndex != 0) {
- g = fallbackIndex;
- renderFont = fallbackFont;
- break;
- }
- }
- // It is possible that we did not find a fallback glyph.
- // In that case the glyph index 'g' is 0, and we'll proceed below and cache empty glyph.
- }
- scale = fons__tt_getPixelHeightScale(&renderFont->font, size);
- fons__tt_buildGlyphBitmap(&renderFont->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1);
- gw = x1-x0 + pad*2;
- gh = y1-y0 + pad*2;
-
- // Find free spot for the rect in the atlas
- added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy);
- if (added == 0 && stash->handleError != NULL) {
- // Atlas is full, let the user to resize the atlas (or not), and try again.
- stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0);
- added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy);
- }
- if (added == 0) return NULL;
-
- // Init glyph.
- glyph = fons__allocGlyph(font);
- glyph->codepoint = codepoint;
- glyph->size = isize;
- glyph->blur = iblur;
- glyph->index = g;
- glyph->x0 = (short)gx;
- glyph->y0 = (short)gy;
- glyph->x1 = (short)(glyph->x0+gw);
- glyph->y1 = (short)(glyph->y0+gh);
- glyph->xadv = (short)(scale * advance * 10.0f);
- glyph->xoff = (short)(x0 - pad);
- glyph->yoff = (short)(y0 - pad);
- glyph->next = 0;
-
- // Insert char to hash lookup.
- glyph->next = font->lut[h];
- font->lut[h] = font->nglyphs-1;
-
- // Rasterize
- dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width];
- fons__tt_renderGlyphBitmap(&renderFont->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale,scale, g);
-
- // Make sure there is one pixel empty border.
- dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width];
- for (y = 0; y < gh; y++) {
- dst[y*stash->params.width] = 0;
- dst[gw-1 + y*stash->params.width] = 0;
- }
- for (x = 0; x < gw; x++) {
- dst[x] = 0;
- dst[x + (gh-1)*stash->params.width] = 0;
- }
-
- // Debug code to color the glyph background
-/* unsigned char* fdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width];
- for (y = 0; y < gh; y++) {
- for (x = 0; x < gw; x++) {
- int a = (int)fdst[x+y*stash->params.width] + 20;
- if (a > 255) a = 255;
- fdst[x+y*stash->params.width] = a;
- }
- }*/
-
- // Blur
- if (iblur > 0) {
- stash->nscratch = 0;
- bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width];
- fons__blur(stash, bdst, gw,gh, stash->params.width, iblur);
- }
-
- stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0);
- stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], glyph->y0);
- stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], glyph->x1);
- stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], glyph->y1);
-
- return glyph;
-}
-
-static void fons__getQuad(FONScontext* stash, FONSfont* font,
- int prevGlyphIndex, FONSglyph* glyph,
- float scale, float spacing, float* x, float* y, FONSquad* q)
-{
- float rx,ry,xoff,yoff,x0,y0,x1,y1;
-
- if (prevGlyphIndex != -1) {
- float adv = fons__tt_getGlyphKernAdvance(&font->font, prevGlyphIndex, glyph->index) * scale;
- *x += (int)(adv + spacing + 0.5f);
- }
-
- // Each glyph has 2px border to allow good interpolation,
- // one pixel to prevent leaking, and one to allow good interpolation for rendering.
- // Inset the texture region by one pixel for correct interpolation.
- xoff = (short)(glyph->xoff+1);
- yoff = (short)(glyph->yoff+1);
- x0 = (float)(glyph->x0+1);
- y0 = (float)(glyph->y0+1);
- x1 = (float)(glyph->x1-1);
- y1 = (float)(glyph->y1-1);
-
- if (stash->params.flags & FONS_ZERO_TOPLEFT) {
- rx = (float)(int)(*x + xoff);
- ry = (float)(int)(*y + yoff);
-
- q->x0 = rx;
- q->y0 = ry;
- q->x1 = rx + x1 - x0;
- q->y1 = ry + y1 - y0;
-
- q->s0 = x0 * stash->itw;
- q->t0 = y0 * stash->ith;
- q->s1 = x1 * stash->itw;
- q->t1 = y1 * stash->ith;
- } else {
- rx = (float)(int)(*x + xoff);
- ry = (float)(int)(*y - yoff);
-
- q->x0 = rx;
- q->y0 = ry;
- q->x1 = rx + x1 - x0;
- q->y1 = ry - y1 + y0;
-
- q->s0 = x0 * stash->itw;
- q->t0 = y0 * stash->ith;
- q->s1 = x1 * stash->itw;
- q->t1 = y1 * stash->ith;
- }
-
- *x += (int)(glyph->xadv / 10.0f + 0.5f);
-}
-
-static void fons__flush(FONScontext* stash)
-{
- // Flush texture
- if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) {
- if (stash->params.renderUpdate != NULL)
- stash->params.renderUpdate(stash->params.userPtr, stash->dirtyRect, stash->texData);
- // Reset dirty rect
- stash->dirtyRect[0] = stash->params.width;
- stash->dirtyRect[1] = stash->params.height;
- stash->dirtyRect[2] = 0;
- stash->dirtyRect[3] = 0;
- }
-
- // Flush triangles
- if (stash->nverts > 0) {
- if (stash->params.renderDraw != NULL)
- stash->params.renderDraw(stash->params.userPtr, stash->verts, stash->tcoords, stash->colors, stash->nverts);
- stash->nverts = 0;
- }
-}
-
-static __inline void fons__vertex(FONScontext* stash, float x, float y, float s, float t, unsigned int c)
-{
- stash->verts[stash->nverts*2+0] = x;
- stash->verts[stash->nverts*2+1] = y;
- stash->tcoords[stash->nverts*2+0] = s;
- stash->tcoords[stash->nverts*2+1] = t;
- stash->colors[stash->nverts] = c;
- stash->nverts++;
-}
-
-static float fons__getVertAlign(FONScontext* stash, FONSfont* font, int align, short isize)
-{
- if (stash->params.flags & FONS_ZERO_TOPLEFT) {
- if (align & FONS_ALIGN_TOP) {
- return font->ascender * (float)isize/10.0f;
- } else if (align & FONS_ALIGN_MIDDLE) {
- return (font->ascender + font->descender) / 2.0f * (float)isize/10.0f;
- } else if (align & FONS_ALIGN_BASELINE) {
- return 0.0f;
- } else if (align & FONS_ALIGN_BOTTOM) {
- return font->descender * (float)isize/10.0f;
- }
- } else {
- if (align & FONS_ALIGN_TOP) {
- return -font->ascender * (float)isize/10.0f;
- } else if (align & FONS_ALIGN_MIDDLE) {
- return -(font->ascender + font->descender) / 2.0f * (float)isize/10.0f;
- } else if (align & FONS_ALIGN_BASELINE) {
- return 0.0f;
- } else if (align & FONS_ALIGN_BOTTOM) {
- return -font->descender * (float)isize/10.0f;
- }
- }
- return 0.0;
-}
-
-FONS_DEF float fonsDrawText(FONScontext* stash,
- float x, float y,
- const char* str, const char* end)
-{
- FONSstate* state = fons__getState(stash);
- unsigned int codepoint;
- unsigned int utf8state = 0;
- FONSglyph* glyph = NULL;
- FONSquad q;
- int prevGlyphIndex = -1;
- short isize = (short)(state->size*10.0f);
- short iblur = (short)state->blur;
- float scale;
- FONSfont* font;
- float width;
-
- if (stash == NULL) return x;
- if (state->font < 0 || state->font >= stash->nfonts) return x;
- font = stash->fonts[state->font];
- if (font->data == NULL) return x;
-
- scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f);
-
- if (end == NULL)
- end = str + strlen(str);
-
- // Align horizontally
- if (state->align & FONS_ALIGN_LEFT) {
- // empty
- } else if (state->align & FONS_ALIGN_RIGHT) {
- width = fonsTextBounds(stash, x,y, str, end, NULL);
- x -= width;
- } else if (state->align & FONS_ALIGN_CENTER) {
- width = fonsTextBounds(stash, x,y, str, end, NULL);
- x -= width * 0.5f;
- }
- // Align vertically.
- y += fons__getVertAlign(stash, font, state->align, isize);
-
- for (; str != end; ++str) {
- if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str))
- continue;
- glyph = fons__getGlyph(stash, font, codepoint, isize, iblur);
- if (glyph != NULL) {
- fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q);
-
- if (stash->nverts+6 > FONS_VERTEX_COUNT)
- fons__flush(stash);
-
- fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color);
- fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color);
- fons__vertex(stash, q.x1, q.y0, q.s1, q.t0, state->color);
-
- fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color);
- fons__vertex(stash, q.x0, q.y1, q.s0, q.t1, state->color);
- fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color);
- }
- prevGlyphIndex = glyph != NULL ? glyph->index : -1;
- }
- fons__flush(stash);
-
- return x;
-}
-
-FONS_DEF int fonsTextIterInit(FONScontext* stash, FONStextIter* iter,
- float x, float y, const char* str, const char* end)
-{
- FONSstate* state = fons__getState(stash);
- float width;
-
- memset(iter, 0, sizeof(*iter));
-
- if (stash == NULL) return 0;
- if (state->font < 0 || state->font >= stash->nfonts) return 0;
- iter->font = stash->fonts[state->font];
- if (iter->font->data == NULL) return 0;
-
- iter->isize = (short)(state->size*10.0f);
- iter->iblur = (short)state->blur;
- iter->scale = fons__tt_getPixelHeightScale(&iter->font->font, (float)iter->isize/10.0f);
-
- // Align horizontally
- if (state->align & FONS_ALIGN_LEFT) {
- // empty
- } else if (state->align & FONS_ALIGN_RIGHT) {
- width = fonsTextBounds(stash, x,y, str, end, NULL);
- x -= width;
- } else if (state->align & FONS_ALIGN_CENTER) {
- width = fonsTextBounds(stash, x,y, str, end, NULL);
- x -= width * 0.5f;
- }
- // Align vertically.
- y += fons__getVertAlign(stash, iter->font, state->align, iter->isize);
-
- if (end == NULL)
- end = str + strlen(str);
-
- iter->x = iter->nextx = x;
- iter->y = iter->nexty = y;
- iter->spacing = state->spacing;
- iter->str = str;
- iter->next = str;
- iter->end = end;
- iter->codepoint = 0;
- iter->prevGlyphIndex = -1;
-
- return 1;
-}
-
-FONS_DEF int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad)
-{
- FONSglyph* glyph = NULL;
- const char* str = iter->next;
- iter->str = iter->next;
-
- if (str == iter->end)
- return 0;
-
- for (; str != iter->end; str++) {
- if (fons__decutf8(&iter->utf8state, &iter->codepoint, *(const unsigned char*)str))
- continue;
- str++;
- // Get glyph and quad
- iter->x = iter->nextx;
- iter->y = iter->nexty;
- glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur);
- if (glyph != NULL)
- fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad);
- iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1;
- break;
- }
- iter->next = str;
-
- return 1;
-}
-
-FONS_DEF void fonsDrawDebug(FONScontext* stash, float x, float y)
-{
- int i;
- int w = stash->params.width;
- int h = stash->params.height;
- float u = w == 0 ? 0 : (1.0f / w);
- float v = h == 0 ? 0 : (1.0f / h);
-
- if (stash->nverts+6+6 > FONS_VERTEX_COUNT)
- fons__flush(stash);
-
- // Draw background
- fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff);
- fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff);
- fons__vertex(stash, x+w, y+0, u, v, 0x0fffffff);
-
- fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff);
- fons__vertex(stash, x+0, y+h, u, v, 0x0fffffff);
- fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff);
-
- // Draw texture
- fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff);
- fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff);
- fons__vertex(stash, x+w, y+0, 1, 0, 0xffffffff);
-
- fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff);
- fons__vertex(stash, x+0, y+h, 0, 1, 0xffffffff);
- fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff);
-
- // Drawbug draw atlas
- for (i = 0; i < stash->atlas->nnodes; i++) {
- FONSatlasNode* n = &stash->atlas->nodes[i];
-
- if (stash->nverts+6 > FONS_VERTEX_COUNT)
- fons__flush(stash);
-
- fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff);
- fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff);
- fons__vertex(stash, x+n->x+n->width, y+n->y+0, u, v, 0xc00000ff);
-
- fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff);
- fons__vertex(stash, x+n->x+0, y+n->y+1, u, v, 0xc00000ff);
- fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff);
- }
-
- fons__flush(stash);
-}
-
-FONS_DEF float fonsTextBounds(FONScontext* stash,
- float x, float y,
- const char* str, const char* end,
- float* bounds)
-{
- FONSstate* state = fons__getState(stash);
- unsigned int codepoint;
- unsigned int utf8state = 0;
- FONSquad q;
- FONSglyph* glyph = NULL;
- int prevGlyphIndex = -1;
- short isize = (short)(state->size*10.0f);
- short iblur = (short)state->blur;
- float scale;
- FONSfont* font;
- float startx, advance;
- float minx, miny, maxx, maxy;
-
- if (stash == NULL) return 0;
- if (state->font < 0 || state->font >= stash->nfonts) return 0;
- font = stash->fonts[state->font];
- if (font->data == NULL) return 0;
-
- scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f);
-
- // Align vertically.
- y += fons__getVertAlign(stash, font, state->align, isize);
-
- minx = maxx = x;
- miny = maxy = y;
- startx = x;
-
- if (end == NULL)
- end = str + strlen(str);
-
- for (; str != end; ++str) {
- if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str))
- continue;
- glyph = fons__getGlyph(stash, font, codepoint, isize, iblur);
- if (glyph != NULL) {
- fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q);
- if (q.x0 < minx) minx = q.x0;
- if (q.x1 > maxx) maxx = q.x1;
- if (stash->params.flags & FONS_ZERO_TOPLEFT) {
- if (q.y0 < miny) miny = q.y0;
- if (q.y1 > maxy) maxy = q.y1;
- } else {
- if (q.y1 < miny) miny = q.y1;
- if (q.y0 > maxy) maxy = q.y0;
- }
- }
- prevGlyphIndex = glyph != NULL ? glyph->index : -1;
- }
-
- advance = x - startx;
-
- // Align horizontally
- if (state->align & FONS_ALIGN_LEFT) {
- // empty
- } else if (state->align & FONS_ALIGN_RIGHT) {
- minx -= advance;
- maxx -= advance;
- } else if (state->align & FONS_ALIGN_CENTER) {
- minx -= advance * 0.5f;
- maxx -= advance * 0.5f;
- }
-
- if (bounds) {
- bounds[0] = minx;
- bounds[1] = miny;
- bounds[2] = maxx;
- bounds[3] = maxy;
- }
-
- return advance;
-}
-
-FONS_DEF void fonsVertMetrics(FONScontext* stash,
- float* ascender, float* descender, float* lineh)
-{
- FONSfont* font;
- FONSstate* state = fons__getState(stash);
- short isize;
-
- if (stash == NULL) return;
- if (state->font < 0 || state->font >= stash->nfonts) return;
- font = stash->fonts[state->font];
- isize = (short)(state->size*10.0f);
- if (font->data == NULL) return;
-
- if (ascender)
- *ascender = font->ascender*isize/10.0f;
- if (descender)
- *descender = font->descender*isize/10.0f;
- if (lineh)
- *lineh = font->lineh*isize/10.0f;
-}
-
-FONS_DEF void fonsLineBounds(FONScontext* stash, float y, float* miny, float* maxy)
-{
- FONSfont* font;
- FONSstate* state = fons__getState(stash);
- short isize;
-
- if (stash == NULL) return;
- if (state->font < 0 || state->font >= stash->nfonts) return;
- font = stash->fonts[state->font];
- isize = (short)(state->size*10.0f);
- if (font->data == NULL) return;
-
- y += fons__getVertAlign(stash, font, state->align, isize);
-
- if (stash->params.flags & FONS_ZERO_TOPLEFT) {
- *miny = y - font->ascender * (float)isize/10.0f;
- *maxy = *miny + font->lineh*isize/10.0f;
- } else {
- *maxy = y + font->descender * (float)isize/10.0f;
- *miny = *maxy - font->lineh*isize/10.0f;
- }
-}
-
-FONS_DEF const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height)
-{
- if (width != NULL)
- *width = stash->params.width;
- if (height != NULL)
- *height = stash->params.height;
- return stash->texData;
-}
-
-FONS_DEF int fonsValidateTexture(FONScontext* stash, int* dirty)
-{
- if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) {
- dirty[0] = stash->dirtyRect[0];
- dirty[1] = stash->dirtyRect[1];
- dirty[2] = stash->dirtyRect[2];
- dirty[3] = stash->dirtyRect[3];
- // Reset dirty rect
- stash->dirtyRect[0] = stash->params.width;
- stash->dirtyRect[1] = stash->params.height;
- stash->dirtyRect[2] = 0;
- stash->dirtyRect[3] = 0;
- return 1;
- }
- return 0;
-}
-
-FONS_DEF void fonsDeleteInternal(FONScontext* stash)
-{
- int i;
- if (stash == NULL) return;
-
- if (stash->params.renderDelete)
- stash->params.renderDelete(stash->params.userPtr);
-
- for (i = 0; i < stash->nfonts; ++i)
- fons__freeFont(stash->fonts[i]);
-
- if (stash->atlas) fons__deleteAtlas(stash->atlas);
- if (stash->fonts) free(stash->fonts);
- if (stash->texData) free(stash->texData);
- if (stash->scratch) free(stash->scratch);
- free(stash);
-}
-
-FONS_DEF void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr)
-{
- if (stash == NULL) return;
- stash->handleError = callback;
- stash->errorUptr = uptr;
-}
-
-FONS_DEF void fonsGetAtlasSize(FONScontext* stash, int* width, int* height)
-{
- if (stash == NULL) return;
- *width = stash->params.width;
- *height = stash->params.height;
-}
-
-FONS_DEF int fonsExpandAtlas(FONScontext* stash, int width, int height)
-{
- int i, maxy = 0;
- unsigned char* data = NULL;
- if (stash == NULL) return 0;
-
- width = fons__maxi(width, stash->params.width);
- height = fons__maxi(height, stash->params.height);
-
- if (width == stash->params.width && height == stash->params.height)
- return 1;
-
- // Flush pending glyphs.
- fons__flush(stash);
-
- // Create new texture
- if (stash->params.renderResize != NULL) {
- if (stash->params.renderResize(stash->params.userPtr, width, height) == 0)
- return 0;
- }
- // Copy old texture data over.
- data = (unsigned char*)malloc(width * height);
- if (data == NULL)
- return 0;
- for (i = 0; i < stash->params.height; i++) {
- unsigned char* dst = &data[i*width];
- unsigned char* src = &stash->texData[i*stash->params.width];
- memcpy(dst, src, stash->params.width);
- if (width > stash->params.width)
- memset(dst+stash->params.width, 0, width - stash->params.width);
- }
- if (height > stash->params.height)
- memset(&data[stash->params.height * width], 0, (height - stash->params.height) * width);
-
- free(stash->texData);
- stash->texData = data;
-
- // Increase atlas size
- fons__atlasExpand(stash->atlas, width, height);
-
- // Add existing data as dirty.
- for (i = 0; i < stash->atlas->nnodes; i++)
- maxy = fons__maxi(maxy, stash->atlas->nodes[i].y);
- stash->dirtyRect[0] = 0;
- stash->dirtyRect[1] = 0;
- stash->dirtyRect[2] = stash->params.width;
- stash->dirtyRect[3] = maxy;
-
- stash->params.width = width;
- stash->params.height = height;
- stash->itw = 1.0f/stash->params.width;
- stash->ith = 1.0f/stash->params.height;
-
- return 1;
-}
-
-FONS_DEF int fonsResetAtlas(FONScontext* stash, int width, int height)
-{
- int i, j;
- if (stash == NULL) return 0;
-
- // Flush pending glyphs.
- fons__flush(stash);
-
- // Create new texture
- if (stash->params.renderResize != NULL) {
- if (stash->params.renderResize(stash->params.userPtr, width, height) == 0)
- return 0;
- }
-
- // Reset atlas
- fons__atlasReset(stash->atlas, width, height);
-
- // Clear texture data.
- stash->texData = (unsigned char*)realloc(stash->texData, width * height);
- if (stash->texData == NULL) return 0;
- memset(stash->texData, 0, width * height);
-
- // Reset dirty rect
- stash->dirtyRect[0] = width;
- stash->dirtyRect[1] = height;
- stash->dirtyRect[2] = 0;
- stash->dirtyRect[3] = 0;
-
- // Reset cached glyphs
- for (i = 0; i < stash->nfonts; i++) {
- FONSfont* font = stash->fonts[i];
- font->nglyphs = 0;
- for (j = 0; j < FONS_HASH_LUT_SIZE; j++)
- font->lut[j] = -1;
- }
-
- stash->params.width = width;
- stash->params.height = height;
- stash->itw = 1.0f/stash->params.width;
- stash->ith = 1.0f/stash->params.height;
-
- // Add white rect at 0,0 for debug drawing.
- fons__addWhiteRect(stash, 2,2);
-
- return 1;
-}
-
-#endif // FONTSTASH_IMPLEMENTATION
diff --git a/source/engine/thirdparty/sokol/tests/ext/nuklear.c b/source/engine/thirdparty/sokol/tests/ext/nuklear.c
deleted file mode 100644
index 52712f3..0000000
--- a/source/engine/thirdparty/sokol/tests/ext/nuklear.c
+++ /dev/null
@@ -1,33 +0,0 @@
-// include nuklear.h before the sokol_nuklear.h implementation
-#define NK_INCLUDE_FIXED_TYPES
-#define NK_INCLUDE_STANDARD_IO
-#define NK_INCLUDE_DEFAULT_ALLOCATOR
-#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
-#define NK_INCLUDE_FONT_BAKING
-#define NK_INCLUDE_DEFAULT_FONT
-#define NK_INCLUDE_STANDARD_VARARGS
-#define NK_IMPLEMENTATION
-
-#if defined(__clang__)
-#pragma GCC diagnostic ignored "-Wunknown-warning-option"
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-#pragma GCC diagnostic ignored "-Wsign-conversion"
-#pragma GCC diagnostic ignored "-Wnull-pointer-subtraction"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
-#endif
-#if defined(__GNUC__) && !defined(__clang__)
-#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
-#pragma GCC diagnostic ignored "-Wsign-conversion"
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-#endif
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable:4996) // sprintf,fopen,localtime: This function or variable may be unsafe
-#pragma warning(disable:4127) // conditional expression is constant
-#pragma warning(disable:4100) // unreferenced formal parameter
-#pragma warning(disable:4701) // potentially uninitialized local variable used
-#endif
-#include "nuklear.h"
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
diff --git a/source/engine/thirdparty/sokol/tests/ext/nuklear.h b/source/engine/thirdparty/sokol/tests/ext/nuklear.h
deleted file mode 100644
index 6855f6f..0000000
--- a/source/engine/thirdparty/sokol/tests/ext/nuklear.h
+++ /dev/null
@@ -1,29499 +0,0 @@
-/*
-/// # Nuklear
-/// ![](https://cloud.githubusercontent.com/assets/8057201/11761525/ae06f0ca-a0c6-11e5-819d-5610b25f6ef4.gif)
-///
-/// ## Contents
-/// 1. About section
-/// 2. Highlights section
-/// 3. Features section
-/// 4. Usage section
-/// 1. Flags section
-/// 2. Constants section
-/// 3. Dependencies section
-/// 5. Example section
-/// 6. API section
-/// 1. Context section
-/// 2. Input section
-/// 3. Drawing section
-/// 4. Window section
-/// 5. Layouting section
-/// 6. Groups section
-/// 7. Tree section
-/// 8. Properties section
-/// 7. License section
-/// 8. Changelog section
-/// 9. Gallery section
-/// 10. Credits section
-///
-/// ## About
-/// This is a minimal state immediate mode graphical user interface toolkit
-/// written in ANSI C and licensed under public domain. It was designed as a simple
-/// embeddable user interface for application and does not have any dependencies,
-/// a default renderbackend or OS window and input handling but instead provides a very modular
-/// library approach by using simple input state for input and draw
-/// commands describing primitive shapes as output. So instead of providing a
-/// layered library that tries to abstract over a number of platform and
-/// render backends it only focuses on the actual UI.
-///
-/// ## Highlights
-/// - Graphical user interface toolkit
-/// - Single header library
-/// - Written in C89 (a.k.a. ANSI C or ISO C90)
-/// - Small codebase (~18kLOC)
-/// - Focus on portability, efficiency and simplicity
-/// - No dependencies (not even the standard library if not wanted)
-/// - Fully skinnable and customizable
-/// - Low memory footprint with total memory control if needed or wanted
-/// - UTF-8 support
-/// - No global or hidden state
-/// - Customizable library modules (you can compile and use only what you need)
-/// - Optional font baker and vertex buffer output
-///
-/// ## Features
-/// - Absolutely no platform dependent code
-/// - Memory management control ranging from/to
-/// - Ease of use by allocating everything from standard library
-/// - Control every byte of memory inside the library
-/// - Font handling control ranging from/to
-/// - Use your own font implementation for everything
-/// - Use this libraries internal font baking and handling API
-/// - Drawing output control ranging from/to
-/// - Simple shapes for more high level APIs which already have drawing capabilities
-/// - Hardware accessible anti-aliased vertex buffer output
-/// - Customizable colors and properties ranging from/to
-/// - Simple changes to color by filling a simple color table
-/// - Complete control with ability to use skinning to decorate widgets
-/// - Bendable UI library with widget ranging from/to
-/// - Basic widgets like buttons, checkboxes, slider, ...
-/// - Advanced widget like abstract comboboxes, contextual menus,...
-/// - Compile time configuration to only compile what you need
-/// - Subset which can be used if you do not want to link or use the standard library
-/// - Can be easily modified to only update on user input instead of frame updates
-///
-/// ## Usage
-/// This library is self contained in one single header file and can be used either
-/// in header only mode or in implementation mode. The header only mode is used
-/// by default when included and allows including this header in other headers
-/// and does not contain the actual implementation.
-///
-/// The implementation mode requires to define the preprocessor macro
-/// NK_IMPLEMENTATION in *one* .c/.cpp file before #includeing this file, e.g.:
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~C
-/// #define NK_IMPLEMENTATION
-/// #include "nuklear.h"
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Also optionally define the symbols listed in the section "OPTIONAL DEFINES"
-/// below in header and implementation mode if you want to use additional functionality
-/// or need more control over the library.
-///
-/// !!! WARNING
-/// Every time nuklear is included define the same compiler flags. This very important not doing so could lead to compiler errors or even worse stack corruptions.
-///
-/// ### Flags
-/// Flag | Description
-/// --------------------------------|------------------------------------------
-/// NK_PRIVATE | If defined declares all functions as static, so they can only be accessed inside the file that contains the implementation
-/// NK_INCLUDE_FIXED_TYPES | If defined it will include header `` for fixed sized types otherwise nuklear tries to select the correct type. If that fails it will throw a compiler error and you have to select the correct types yourself.
-/// NK_INCLUDE_DEFAULT_ALLOCATOR | If defined it will include header `` and provide additional functions to use this library without caring for memory allocation control and therefore ease memory management.
-/// NK_INCLUDE_STANDARD_IO | If defined it will include header `` and provide additional functions depending on file loading.
-/// NK_INCLUDE_STANDARD_VARARGS | If defined it will include header and provide additional functions depending on file loading.
-/// NK_INCLUDE_STANDARD_BOOL | If defined it will include header `` for nk_bool otherwise nuklear defines nk_bool as int.
-/// NK_INCLUDE_VERTEX_BUFFER_OUTPUT | Defining this adds a vertex draw command list backend to this library, which allows you to convert queue commands into vertex draw commands. This is mainly if you need a hardware accessible format for OpenGL, DirectX, Vulkan, Metal,...
-/// NK_INCLUDE_FONT_BAKING | Defining this adds `stb_truetype` and `stb_rect_pack` implementation to this library and provides font baking and rendering. If you already have font handling or do not want to use this font handler you don't have to define it.
-/// NK_INCLUDE_DEFAULT_FONT | Defining this adds the default font: ProggyClean.ttf into this library which can be loaded into a font atlas and allows using this library without having a truetype font
-/// NK_INCLUDE_COMMAND_USERDATA | Defining this adds a userdata pointer into each command. Can be useful for example if you want to provide custom shaders depending on the used widget. Can be combined with the style structures.
-/// NK_BUTTON_TRIGGER_ON_RELEASE | Different platforms require button clicks occurring either on buttons being pressed (up to down) or released (down to up). By default this library will react on buttons being pressed, but if you define this it will only trigger if a button is released.
-/// NK_ZERO_COMMAND_MEMORY | Defining this will zero out memory for each drawing command added to a drawing queue (inside nk_command_buffer_push). Zeroing command memory is very useful for fast checking (using memcmp) if command buffers are equal and avoid drawing frames when nothing on screen has changed since previous frame.
-/// NK_UINT_DRAW_INDEX | Defining this will set the size of vertex index elements when using NK_VERTEX_BUFFER_OUTPUT to 32bit instead of the default of 16bit
-/// NK_KEYSTATE_BASED_INPUT | Define this if your backend uses key state for each frame rather than key press/release events
-///
-/// !!! WARNING
-/// The following flags will pull in the standard C library:
-/// - NK_INCLUDE_DEFAULT_ALLOCATOR
-/// - NK_INCLUDE_STANDARD_IO
-/// - NK_INCLUDE_STANDARD_VARARGS
-///
-/// !!! WARNING
-/// The following flags if defined need to be defined for both header and implementation:
-/// - NK_INCLUDE_FIXED_TYPES
-/// - NK_INCLUDE_DEFAULT_ALLOCATOR
-/// - NK_INCLUDE_STANDARD_VARARGS
-/// - NK_INCLUDE_STANDARD_BOOL
-/// - NK_INCLUDE_VERTEX_BUFFER_OUTPUT
-/// - NK_INCLUDE_FONT_BAKING
-/// - NK_INCLUDE_DEFAULT_FONT
-/// - NK_INCLUDE_STANDARD_VARARGS
-/// - NK_INCLUDE_COMMAND_USERDATA
-/// - NK_UINT_DRAW_INDEX
-///
-/// ### Constants
-/// Define | Description
-/// --------------------------------|---------------------------------------
-/// NK_BUFFER_DEFAULT_INITIAL_SIZE | Initial buffer size allocated by all buffers while using the default allocator functions included by defining NK_INCLUDE_DEFAULT_ALLOCATOR. If you don't want to allocate the default 4k memory then redefine it.
-/// NK_MAX_NUMBER_BUFFER | Maximum buffer size for the conversion buffer between float and string Under normal circumstances this should be more than sufficient.
-/// NK_INPUT_MAX | Defines the max number of bytes which can be added as text input in one frame. Under normal circumstances this should be more than sufficient.
-///
-/// !!! WARNING
-/// The following constants if defined need to be defined for both header and implementation:
-/// - NK_MAX_NUMBER_BUFFER
-/// - NK_BUFFER_DEFAULT_INITIAL_SIZE
-/// - NK_INPUT_MAX
-///
-/// ### Dependencies
-/// Function | Description
-/// ------------|---------------------------------------------------------------
-/// NK_ASSERT | If you don't define this, nuklear will use with assert().
-/// NK_MEMSET | You can define this to 'memset' or your own memset implementation replacement. If not nuklear will use its own version.
-/// NK_MEMCPY | You can define this to 'memcpy' or your own memcpy implementation replacement. If not nuklear will use its own version.
-/// NK_SQRT | You can define this to 'sqrt' or your own sqrt implementation replacement. If not nuklear will use its own slow and not highly accurate version.
-/// NK_SIN | You can define this to 'sinf' or your own sine implementation replacement. If not nuklear will use its own approximation implementation.
-/// NK_COS | You can define this to 'cosf' or your own cosine implementation replacement. If not nuklear will use its own approximation implementation.
-/// NK_STRTOD | You can define this to `strtod` or your own string to double conversion implementation replacement. If not defined nuklear will use its own imprecise and possibly unsafe version (does not handle nan or infinity!).
-/// NK_DTOA | You can define this to `dtoa` or your own double to string conversion implementation replacement. If not defined nuklear will use its own imprecise and possibly unsafe version (does not handle nan or infinity!).
-/// NK_VSNPRINTF| If you define `NK_INCLUDE_STANDARD_VARARGS` as well as `NK_INCLUDE_STANDARD_IO` and want to be safe define this to `vsnprintf` on compilers supporting later versions of C or C++. By default nuklear will check for your stdlib version in C as well as compiler version in C++. if `vsnprintf` is available it will define it to `vsnprintf` directly. If not defined and if you have older versions of C or C++ it will be defined to `vsprintf` which is unsafe.
-///
-/// !!! WARNING
-/// The following dependencies will pull in the standard C library if not redefined:
-/// - NK_ASSERT
-///
-/// !!! WARNING
-/// The following dependencies if defined need to be defined for both header and implementation:
-/// - NK_ASSERT
-///
-/// !!! WARNING
-/// The following dependencies if defined need to be defined only for the implementation part:
-/// - NK_MEMSET
-/// - NK_MEMCPY
-/// - NK_SQRT
-/// - NK_SIN
-/// - NK_COS
-/// - NK_STRTOD
-/// - NK_DTOA
-/// - NK_VSNPRINTF
-///
-/// ## Example
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// // init gui state
-/// enum {EASY, HARD};
-/// static int op = EASY;
-/// static float value = 0.6f;
-/// static int i = 20;
-/// struct nk_context ctx;
-///
-/// nk_init_fixed(&ctx, calloc(1, MAX_MEMORY), MAX_MEMORY, &font);
-/// if (nk_begin(&ctx, "Show", nk_rect(50, 50, 220, 220),
-/// NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_CLOSABLE)) {
-/// // fixed widget pixel width
-/// nk_layout_row_static(&ctx, 30, 80, 1);
-/// if (nk_button_label(&ctx, "button")) {
-/// // event handling
-/// }
-///
-/// // fixed widget window ratio width
-/// nk_layout_row_dynamic(&ctx, 30, 2);
-/// if (nk_option_label(&ctx, "easy", op == EASY)) op = EASY;
-/// if (nk_option_label(&ctx, "hard", op == HARD)) op = HARD;
-///
-/// // custom widget pixel width
-/// nk_layout_row_begin(&ctx, NK_STATIC, 30, 2);
-/// {
-/// nk_layout_row_push(&ctx, 50);
-/// nk_label(&ctx, "Volume:", NK_TEXT_LEFT);
-/// nk_layout_row_push(&ctx, 110);
-/// nk_slider_float(&ctx, 0, &value, 1.0f, 0.1f);
-/// }
-/// nk_layout_row_end(&ctx);
-/// }
-/// nk_end(&ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// ![](https://cloud.githubusercontent.com/assets/8057201/10187981/584ecd68-675c-11e5-897c-822ef534a876.png)
-///
-/// ## API
-///
-*/
-#ifndef NK_SINGLE_FILE
- #define NK_SINGLE_FILE
-#endif
-
-#ifndef NK_NUKLEAR_H_
-#define NK_NUKLEAR_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-/*
- * ==============================================================
- *
- * CONSTANTS
- *
- * ===============================================================
- */
-#define NK_UNDEFINED (-1.0f)
-#define NK_UTF_INVALID 0xFFFD /* internal invalid utf8 rune */
-#define NK_UTF_SIZE 4 /* describes the number of bytes a glyph consists of*/
-#ifndef NK_INPUT_MAX
- #define NK_INPUT_MAX 16
-#endif
-#ifndef NK_MAX_NUMBER_BUFFER
- #define NK_MAX_NUMBER_BUFFER 64
-#endif
-#ifndef NK_SCROLLBAR_HIDING_TIMEOUT
- #define NK_SCROLLBAR_HIDING_TIMEOUT 4.0f
-#endif
-/*
- * ==============================================================
- *
- * HELPER
- *
- * ===============================================================
- */
-#ifndef NK_API
- #ifdef NK_PRIVATE
- #if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199409L))
- #define NK_API static inline
- #elif defined(__cplusplus)
- #define NK_API static inline
- #else
- #define NK_API static
- #endif
- #else
- #define NK_API extern
- #endif
-#endif
-#ifndef NK_LIB
- #ifdef NK_SINGLE_FILE
- #define NK_LIB static
- #else
- #define NK_LIB extern
- #endif
-#endif
-
-#define NK_INTERN static
-#define NK_STORAGE static
-#define NK_GLOBAL static
-
-#define NK_FLAG(x) (1 << (x))
-#define NK_STRINGIFY(x) #x
-#define NK_MACRO_STRINGIFY(x) NK_STRINGIFY(x)
-#define NK_STRING_JOIN_IMMEDIATE(arg1, arg2) arg1 ## arg2
-#define NK_STRING_JOIN_DELAY(arg1, arg2) NK_STRING_JOIN_IMMEDIATE(arg1, arg2)
-#define NK_STRING_JOIN(arg1, arg2) NK_STRING_JOIN_DELAY(arg1, arg2)
-
-#ifdef _MSC_VER
- #define NK_UNIQUE_NAME(name) NK_STRING_JOIN(name,__COUNTER__)
-#else
- #define NK_UNIQUE_NAME(name) NK_STRING_JOIN(name,__LINE__)
-#endif
-
-#ifndef NK_STATIC_ASSERT
- #define NK_STATIC_ASSERT(exp) typedef char NK_UNIQUE_NAME(_dummy_array)[(exp)?1:-1]
-#endif
-
-#ifndef NK_FILE_LINE
-#ifdef _MSC_VER
- #define NK_FILE_LINE __FILE__ ":" NK_MACRO_STRINGIFY(__COUNTER__)
-#else
- #define NK_FILE_LINE __FILE__ ":" NK_MACRO_STRINGIFY(__LINE__)
-#endif
-#endif
-
-#define NK_MIN(a,b) ((a) < (b) ? (a) : (b))
-#define NK_MAX(a,b) ((a) < (b) ? (b) : (a))
-#define NK_CLAMP(i,v,x) (NK_MAX(NK_MIN(v,x), i))
-
-#ifdef NK_INCLUDE_STANDARD_VARARGS
- #include
- #if defined(_MSC_VER) && (_MSC_VER >= 1600) /* VS 2010 and above */
- #include
- #define NK_PRINTF_FORMAT_STRING _Printf_format_string_
- #else
- #define NK_PRINTF_FORMAT_STRING
- #endif
- #if defined(__GNUC__)
- #define NK_PRINTF_VARARG_FUNC(fmtargnumber) __attribute__((format(__printf__, fmtargnumber, fmtargnumber+1)))
- #define NK_PRINTF_VALIST_FUNC(fmtargnumber) __attribute__((format(__printf__, fmtargnumber, 0)))
- #else
- #define NK_PRINTF_VARARG_FUNC(fmtargnumber)
- #define NK_PRINTF_VALIST_FUNC(fmtargnumber)
- #endif
-#endif
-
-/*
- * ===============================================================
- *
- * BASIC
- *
- * ===============================================================
- */
-#ifdef NK_INCLUDE_FIXED_TYPES
- #include
- #define NK_INT8 int8_t
- #define NK_UINT8 uint8_t
- #define NK_INT16 int16_t
- #define NK_UINT16 uint16_t
- #define NK_INT32 int32_t
- #define NK_UINT32 uint32_t
- #define NK_SIZE_TYPE uintptr_t
- #define NK_POINTER_TYPE uintptr_t
-#else
- #ifndef NK_INT8
- #define NK_INT8 signed char
- #endif
- #ifndef NK_UINT8
- #define NK_UINT8 unsigned char
- #endif
- #ifndef NK_INT16
- #define NK_INT16 signed short
- #endif
- #ifndef NK_UINT16
- #define NK_UINT16 unsigned short
- #endif
- #ifndef NK_INT32
- #if defined(_MSC_VER)
- #define NK_INT32 __int32
- #else
- #define NK_INT32 signed int
- #endif
- #endif
- #ifndef NK_UINT32
- #if defined(_MSC_VER)
- #define NK_UINT32 unsigned __int32
- #else
- #define NK_UINT32 unsigned int
- #endif
- #endif
- #ifndef NK_SIZE_TYPE
- #if defined(_WIN64) && defined(_MSC_VER)
- #define NK_SIZE_TYPE unsigned __int64
- #elif (defined(_WIN32) || defined(WIN32)) && defined(_MSC_VER)
- #define NK_SIZE_TYPE unsigned __int32
- #elif defined(__GNUC__) || defined(__clang__)
- #if defined(__x86_64__) || defined(__ppc64__)
- #define NK_SIZE_TYPE unsigned long
- #else
- #define NK_SIZE_TYPE unsigned int
- #endif
- #else
- #define NK_SIZE_TYPE unsigned long
- #endif
- #endif
- #ifndef NK_POINTER_TYPE
- #if defined(_WIN64) && defined(_MSC_VER)
- #define NK_POINTER_TYPE unsigned __int64
- #elif (defined(_WIN32) || defined(WIN32)) && defined(_MSC_VER)
- #define NK_POINTER_TYPE unsigned __int32
- #elif defined(__GNUC__) || defined(__clang__)
- #if defined(__x86_64__) || defined(__ppc64__)
- #define NK_POINTER_TYPE unsigned long
- #else
- #define NK_POINTER_TYPE unsigned int
- #endif
- #else
- #define NK_POINTER_TYPE unsigned long
- #endif
- #endif
-#endif
-
-#ifndef NK_BOOL
- #ifdef NK_INCLUDE_STANDARD_BOOL
- #include
- #define NK_BOOL bool
- #else
- #define NK_BOOL int /* could be char, use int for drop-in replacement backwards compatibility */
- #endif
-#endif
-
-typedef NK_INT8 nk_char;
-typedef NK_UINT8 nk_uchar;
-typedef NK_UINT8 nk_byte;
-typedef NK_INT16 nk_short;
-typedef NK_UINT16 nk_ushort;
-typedef NK_INT32 nk_int;
-typedef NK_UINT32 nk_uint;
-typedef NK_SIZE_TYPE nk_size;
-typedef NK_POINTER_TYPE nk_ptr;
-typedef NK_BOOL nk_bool;
-
-typedef nk_uint nk_hash;
-typedef nk_uint nk_flags;
-typedef nk_uint nk_rune;
-
-/* Make sure correct type size:
- * This will fire with a negative subscript error if the type sizes
- * are set incorrectly by the compiler, and compile out if not */
-NK_STATIC_ASSERT(sizeof(nk_short) == 2);
-NK_STATIC_ASSERT(sizeof(nk_ushort) == 2);
-NK_STATIC_ASSERT(sizeof(nk_uint) == 4);
-NK_STATIC_ASSERT(sizeof(nk_int) == 4);
-NK_STATIC_ASSERT(sizeof(nk_byte) == 1);
-NK_STATIC_ASSERT(sizeof(nk_flags) >= 4);
-NK_STATIC_ASSERT(sizeof(nk_rune) >= 4);
-NK_STATIC_ASSERT(sizeof(nk_size) >= sizeof(void*));
-NK_STATIC_ASSERT(sizeof(nk_ptr) >= sizeof(void*));
-#ifdef NK_INCLUDE_STANDARD_BOOL
-NK_STATIC_ASSERT(sizeof(nk_bool) == sizeof(bool));
-#else
-NK_STATIC_ASSERT(sizeof(nk_bool) >= 2);
-#endif
-
-/* ============================================================================
- *
- * API
- *
- * =========================================================================== */
-struct nk_buffer;
-struct nk_allocator;
-struct nk_command_buffer;
-struct nk_draw_command;
-struct nk_convert_config;
-struct nk_style_item;
-struct nk_text_edit;
-struct nk_draw_list;
-struct nk_user_font;
-struct nk_panel;
-struct nk_context;
-struct nk_draw_vertex_layout_element;
-struct nk_style_button;
-struct nk_style_toggle;
-struct nk_style_selectable;
-struct nk_style_slide;
-struct nk_style_progress;
-struct nk_style_scrollbar;
-struct nk_style_edit;
-struct nk_style_property;
-struct nk_style_chart;
-struct nk_style_combo;
-struct nk_style_tab;
-struct nk_style_window_header;
-struct nk_style_window;
-
-enum {nk_false, nk_true};
-struct nk_color {nk_byte r,g,b,a;};
-struct nk_colorf {float r,g,b,a;};
-struct nk_vec2 {float x,y;};
-struct nk_vec2i {short x, y;};
-struct nk_rect {float x,y,w,h;};
-struct nk_recti {short x,y,w,h;};
-typedef char nk_glyph[NK_UTF_SIZE];
-typedef union {void *ptr; int id;} nk_handle;
-struct nk_image {nk_handle handle;unsigned short w,h;unsigned short region[4];};
-struct nk_cursor {struct nk_image img; struct nk_vec2 size, offset;};
-struct nk_scroll {nk_uint x, y;};
-
-enum nk_heading {NK_UP, NK_RIGHT, NK_DOWN, NK_LEFT};
-enum nk_button_behavior {NK_BUTTON_DEFAULT, NK_BUTTON_REPEATER};
-enum nk_modify {NK_FIXED = nk_false, NK_MODIFIABLE = nk_true};
-enum nk_orientation {NK_VERTICAL, NK_HORIZONTAL};
-enum nk_collapse_states {NK_MINIMIZED = nk_false, NK_MAXIMIZED = nk_true};
-enum nk_show_states {NK_HIDDEN = nk_false, NK_SHOWN = nk_true};
-enum nk_chart_type {NK_CHART_LINES, NK_CHART_COLUMN, NK_CHART_MAX};
-enum nk_chart_event {NK_CHART_HOVERING = 0x01, NK_CHART_CLICKED = 0x02};
-enum nk_color_format {NK_RGB, NK_RGBA};
-enum nk_popup_type {NK_POPUP_STATIC, NK_POPUP_DYNAMIC};
-enum nk_layout_format {NK_DYNAMIC, NK_STATIC};
-enum nk_tree_type {NK_TREE_NODE, NK_TREE_TAB};
-
-typedef void*(*nk_plugin_alloc)(nk_handle, void *old, nk_size);
-typedef void (*nk_plugin_free)(nk_handle, void *old);
-typedef nk_bool(*nk_plugin_filter)(const struct nk_text_edit*, nk_rune unicode);
-typedef void(*nk_plugin_paste)(nk_handle, struct nk_text_edit*);
-typedef void(*nk_plugin_copy)(nk_handle, const char*, int len);
-
-struct nk_allocator {
- nk_handle userdata;
- nk_plugin_alloc alloc;
- nk_plugin_free free;
-};
-enum nk_symbol_type {
- NK_SYMBOL_NONE,
- NK_SYMBOL_X,
- NK_SYMBOL_UNDERSCORE,
- NK_SYMBOL_CIRCLE_SOLID,
- NK_SYMBOL_CIRCLE_OUTLINE,
- NK_SYMBOL_RECT_SOLID,
- NK_SYMBOL_RECT_OUTLINE,
- NK_SYMBOL_TRIANGLE_UP,
- NK_SYMBOL_TRIANGLE_DOWN,
- NK_SYMBOL_TRIANGLE_LEFT,
- NK_SYMBOL_TRIANGLE_RIGHT,
- NK_SYMBOL_PLUS,
- NK_SYMBOL_MINUS,
- NK_SYMBOL_MAX
-};
-/* =============================================================================
- *
- * CONTEXT
- *
- * =============================================================================*/
-/*/// ### Context
-/// Contexts are the main entry point and the majestro of nuklear and contain all required state.
-/// They are used for window, memory, input, style, stack, commands and time management and need
-/// to be passed into all nuklear GUI specific functions.
-///
-/// #### Usage
-/// To use a context it first has to be initialized which can be achieved by calling
-/// one of either `nk_init_default`, `nk_init_fixed`, `nk_init`, `nk_init_custom`.
-/// Each takes in a font handle and a specific way of handling memory. Memory control
-/// hereby ranges from standard library to just specifying a fixed sized block of memory
-/// which nuklear has to manage itself from.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_context ctx;
-/// nk_init_xxx(&ctx, ...);
-/// while (1) {
-/// // [...]
-/// nk_clear(&ctx);
-/// }
-/// nk_free(&ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// #### Reference
-/// Function | Description
-/// --------------------|-------------------------------------------------------
-/// __nk_init_default__ | Initializes context with standard library memory allocation (malloc,free)
-/// __nk_init_fixed__ | Initializes context from single fixed size memory block
-/// __nk_init__ | Initializes context with memory allocator callbacks for alloc and free
-/// __nk_init_custom__ | Initializes context from two buffers. One for draw commands the other for window/panel/table allocations
-/// __nk_clear__ | Called at the end of the frame to reset and prepare the context for the next frame
-/// __nk_free__ | Shutdown and free all memory allocated inside the context
-/// __nk_set_user_data__| Utility function to pass user data to draw command
- */
-#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR
-/*/// #### nk_init_default
-/// Initializes a `nk_context` struct with a default standard library allocator.
-/// Should be used if you don't want to be bothered with memory management in nuklear.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_init_default(struct nk_context *ctx, const struct nk_user_font *font);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|---------------------------------------------------------------
-/// __ctx__ | Must point to an either stack or heap allocated `nk_context` struct
-/// __font__ | Must point to a previously initialized font handle for more info look at font documentation
-///
-/// Returns either `false(0)` on failure or `true(1)` on success.
-///
-*/
-NK_API nk_bool nk_init_default(struct nk_context*, const struct nk_user_font*);
-#endif
-/*/// #### nk_init_fixed
-/// Initializes a `nk_context` struct from single fixed size memory block
-/// Should be used if you want complete control over nuklear's memory management.
-/// Especially recommended for system with little memory or systems with virtual memory.
-/// For the later case you can just allocate for example 16MB of virtual memory
-/// and only the required amount of memory will actually be committed.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_init_fixed(struct nk_context *ctx, void *memory, nk_size size, const struct nk_user_font *font);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// !!! Warning
-/// make sure the passed memory block is aligned correctly for `nk_draw_commands`.
-///
-/// Parameter | Description
-/// ------------|--------------------------------------------------------------
-/// __ctx__ | Must point to an either stack or heap allocated `nk_context` struct
-/// __memory__ | Must point to a previously allocated memory block
-/// __size__ | Must contain the total size of __memory__
-/// __font__ | Must point to a previously initialized font handle for more info look at font documentation
-///
-/// Returns either `false(0)` on failure or `true(1)` on success.
-*/
-NK_API nk_bool nk_init_fixed(struct nk_context*, void *memory, nk_size size, const struct nk_user_font*);
-/*/// #### nk_init
-/// Initializes a `nk_context` struct with memory allocation callbacks for nuklear to allocate
-/// memory from. Used internally for `nk_init_default` and provides a kitchen sink allocation
-/// interface to nuklear. Can be useful for cases like monitoring memory consumption.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_init(struct nk_context *ctx, struct nk_allocator *alloc, const struct nk_user_font *font);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|---------------------------------------------------------------
-/// __ctx__ | Must point to an either stack or heap allocated `nk_context` struct
-/// __alloc__ | Must point to a previously allocated memory allocator
-/// __font__ | Must point to a previously initialized font handle for more info look at font documentation
-///
-/// Returns either `false(0)` on failure or `true(1)` on success.
-*/
-NK_API nk_bool nk_init(struct nk_context*, struct nk_allocator*, const struct nk_user_font*);
-/*/// #### nk_init_custom
-/// Initializes a `nk_context` struct from two different either fixed or growing
-/// buffers. The first buffer is for allocating draw commands while the second buffer is
-/// used for allocating windows, panels and state tables.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_init_custom(struct nk_context *ctx, struct nk_buffer *cmds, struct nk_buffer *pool, const struct nk_user_font *font);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|---------------------------------------------------------------
-/// __ctx__ | Must point to an either stack or heap allocated `nk_context` struct
-/// __cmds__ | Must point to a previously initialized memory buffer either fixed or dynamic to store draw commands into
-/// __pool__ | Must point to a previously initialized memory buffer either fixed or dynamic to store windows, panels and tables
-/// __font__ | Must point to a previously initialized font handle for more info look at font documentation
-///
-/// Returns either `false(0)` on failure or `true(1)` on success.
-*/
-NK_API nk_bool nk_init_custom(struct nk_context*, struct nk_buffer *cmds, struct nk_buffer *pool, const struct nk_user_font*);
-/*/// #### nk_clear
-/// Resets the context state at the end of the frame. This includes mostly
-/// garbage collector tasks like removing windows or table not called and therefore
-/// used anymore.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_clear(struct nk_context *ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to a previously initialized `nk_context` struct
-*/
-NK_API void nk_clear(struct nk_context*);
-/*/// #### nk_free
-/// Frees all memory allocated by nuklear. Not needed if context was
-/// initialized with `nk_init_fixed`.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_free(struct nk_context *ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to a previously initialized `nk_context` struct
-*/
-NK_API void nk_free(struct nk_context*);
-#ifdef NK_INCLUDE_COMMAND_USERDATA
-/*/// #### nk_set_user_data
-/// Sets the currently passed userdata passed down into each draw command.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_set_user_data(struct nk_context *ctx, nk_handle data);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|--------------------------------------------------------------
-/// __ctx__ | Must point to a previously initialized `nk_context` struct
-/// __data__ | Handle with either pointer or index to be passed into every draw commands
-*/
-NK_API void nk_set_user_data(struct nk_context*, nk_handle handle);
-#endif
-/* =============================================================================
- *
- * INPUT
- *
- * =============================================================================*/
-/*/// ### Input
-/// The input API is responsible for holding the current input state composed of
-/// mouse, key and text input states.
-/// It is worth noting that no direct OS or window handling is done in nuklear.
-/// Instead all input state has to be provided by platform specific code. This on one hand
-/// expects more work from the user and complicates usage but on the other hand
-/// provides simple abstraction over a big number of platforms, libraries and other
-/// already provided functionality.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// nk_input_begin(&ctx);
-/// while (GetEvent(&evt)) {
-/// if (evt.type == MOUSE_MOVE)
-/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y);
-/// else if (evt.type == [...]) {
-/// // [...]
-/// }
-/// } nk_input_end(&ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// #### Usage
-/// Input state needs to be provided to nuklear by first calling `nk_input_begin`
-/// which resets internal state like delta mouse position and button transistions.
-/// After `nk_input_begin` all current input state needs to be provided. This includes
-/// mouse motion, button and key pressed and released, text input and scrolling.
-/// Both event- or state-based input handling are supported by this API
-/// and should work without problems. Finally after all input state has been
-/// mirrored `nk_input_end` needs to be called to finish input process.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_context ctx;
-/// nk_init_xxx(&ctx, ...);
-/// while (1) {
-/// Event evt;
-/// nk_input_begin(&ctx);
-/// while (GetEvent(&evt)) {
-/// if (evt.type == MOUSE_MOVE)
-/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y);
-/// else if (evt.type == [...]) {
-/// // [...]
-/// }
-/// }
-/// nk_input_end(&ctx);
-/// // [...]
-/// nk_clear(&ctx);
-/// } nk_free(&ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// #### Reference
-/// Function | Description
-/// --------------------|-------------------------------------------------------
-/// __nk_input_begin__ | Begins the input mirroring process. Needs to be called before all other `nk_input_xxx` calls
-/// __nk_input_motion__ | Mirrors mouse cursor position
-/// __nk_input_key__ | Mirrors key state with either pressed or released
-/// __nk_input_button__ | Mirrors mouse button state with either pressed or released
-/// __nk_input_scroll__ | Mirrors mouse scroll values
-/// __nk_input_char__ | Adds a single ASCII text character into an internal text buffer
-/// __nk_input_glyph__ | Adds a single multi-byte UTF-8 character into an internal text buffer
-/// __nk_input_unicode__| Adds a single unicode rune into an internal text buffer
-/// __nk_input_end__ | Ends the input mirroring process by calculating state changes. Don't call any `nk_input_xxx` function referenced above after this call
-*/
-enum nk_keys {
- NK_KEY_NONE,
- NK_KEY_SHIFT,
- NK_KEY_CTRL,
- NK_KEY_DEL,
- NK_KEY_ENTER,
- NK_KEY_TAB,
- NK_KEY_BACKSPACE,
- NK_KEY_COPY,
- NK_KEY_CUT,
- NK_KEY_PASTE,
- NK_KEY_UP,
- NK_KEY_DOWN,
- NK_KEY_LEFT,
- NK_KEY_RIGHT,
- /* Shortcuts: text field */
- NK_KEY_TEXT_INSERT_MODE,
- NK_KEY_TEXT_REPLACE_MODE,
- NK_KEY_TEXT_RESET_MODE,
- NK_KEY_TEXT_LINE_START,
- NK_KEY_TEXT_LINE_END,
- NK_KEY_TEXT_START,
- NK_KEY_TEXT_END,
- NK_KEY_TEXT_UNDO,
- NK_KEY_TEXT_REDO,
- NK_KEY_TEXT_SELECT_ALL,
- NK_KEY_TEXT_WORD_LEFT,
- NK_KEY_TEXT_WORD_RIGHT,
- /* Shortcuts: scrollbar */
- NK_KEY_SCROLL_START,
- NK_KEY_SCROLL_END,
- NK_KEY_SCROLL_DOWN,
- NK_KEY_SCROLL_UP,
- NK_KEY_MAX
-};
-enum nk_buttons {
- NK_BUTTON_LEFT,
- NK_BUTTON_MIDDLE,
- NK_BUTTON_RIGHT,
- NK_BUTTON_DOUBLE,
- NK_BUTTON_MAX
-};
-/*/// #### nk_input_begin
-/// Begins the input mirroring process by resetting text, scroll
-/// mouse, previous mouse position and movement as well as key state transitions,
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_input_begin(struct nk_context*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to a previously initialized `nk_context` struct
-*/
-NK_API void nk_input_begin(struct nk_context*);
-/*/// #### nk_input_motion
-/// Mirrors current mouse position to nuklear
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_input_motion(struct nk_context *ctx, int x, int y);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to a previously initialized `nk_context` struct
-/// __x__ | Must hold an integer describing the current mouse cursor x-position
-/// __y__ | Must hold an integer describing the current mouse cursor y-position
-*/
-NK_API void nk_input_motion(struct nk_context*, int x, int y);
-/*/// #### nk_input_key
-/// Mirrors the state of a specific key to nuklear
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_input_key(struct nk_context*, enum nk_keys key, nk_bool down);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to a previously initialized `nk_context` struct
-/// __key__ | Must be any value specified in enum `nk_keys` that needs to be mirrored
-/// __down__ | Must be 0 for key is up and 1 for key is down
-*/
-NK_API void nk_input_key(struct nk_context*, enum nk_keys, nk_bool down);
-/*/// #### nk_input_button
-/// Mirrors the state of a specific mouse button to nuklear
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_input_button(struct nk_context *ctx, enum nk_buttons btn, int x, int y, nk_bool down);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to a previously initialized `nk_context` struct
-/// __btn__ | Must be any value specified in enum `nk_buttons` that needs to be mirrored
-/// __x__ | Must contain an integer describing mouse cursor x-position on click up/down
-/// __y__ | Must contain an integer describing mouse cursor y-position on click up/down
-/// __down__ | Must be 0 for key is up and 1 for key is down
-*/
-NK_API void nk_input_button(struct nk_context*, enum nk_buttons, int x, int y, nk_bool down);
-/*/// #### nk_input_scroll
-/// Copies the last mouse scroll value to nuklear. Is generally
-/// a scroll value. So does not have to come from mouse and could also originate
-/// TODO finish this sentence
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_input_scroll(struct nk_context *ctx, struct nk_vec2 val);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to a previously initialized `nk_context` struct
-/// __val__ | vector with both X- as well as Y-scroll value
-*/
-NK_API void nk_input_scroll(struct nk_context*, struct nk_vec2 val);
-/*/// #### nk_input_char
-/// Copies a single ASCII character into an internal text buffer
-/// This is basically a helper function to quickly push ASCII characters into
-/// nuklear.
-///
-/// !!! Note
-/// Stores up to NK_INPUT_MAX bytes between `nk_input_begin` and `nk_input_end`.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_input_char(struct nk_context *ctx, char c);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to a previously initialized `nk_context` struct
-/// __c__ | Must be a single ASCII character preferable one that can be printed
-*/
-NK_API void nk_input_char(struct nk_context*, char);
-/*/// #### nk_input_glyph
-/// Converts an encoded unicode rune into UTF-8 and copies the result into an
-/// internal text buffer.
-///
-/// !!! Note
-/// Stores up to NK_INPUT_MAX bytes between `nk_input_begin` and `nk_input_end`.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_input_glyph(struct nk_context *ctx, const nk_glyph g);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to a previously initialized `nk_context` struct
-/// __g__ | UTF-32 unicode codepoint
-*/
-NK_API void nk_input_glyph(struct nk_context*, const nk_glyph);
-/*/// #### nk_input_unicode
-/// Converts a unicode rune into UTF-8 and copies the result
-/// into an internal text buffer.
-/// !!! Note
-/// Stores up to NK_INPUT_MAX bytes between `nk_input_begin` and `nk_input_end`.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_input_unicode(struct nk_context*, nk_rune rune);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to a previously initialized `nk_context` struct
-/// __rune__ | UTF-32 unicode codepoint
-*/
-NK_API void nk_input_unicode(struct nk_context*, nk_rune);
-/*/// #### nk_input_end
-/// End the input mirroring process by resetting mouse grabbing
-/// state to ensure the mouse cursor is not grabbed indefinitely.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_input_end(struct nk_context *ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to a previously initialized `nk_context` struct
-*/
-NK_API void nk_input_end(struct nk_context*);
-/* =============================================================================
- *
- * DRAWING
- *
- * =============================================================================*/
-/*/// ### Drawing
-/// This library was designed to be render backend agnostic so it does
-/// not draw anything to screen directly. Instead all drawn shapes, widgets
-/// are made of, are buffered into memory and make up a command queue.
-/// Each frame therefore fills the command buffer with draw commands
-/// that then need to be executed by the user and his own render backend.
-/// After that the command buffer needs to be cleared and a new frame can be
-/// started. It is probably important to note that the command buffer is the main
-/// drawing API and the optional vertex buffer API only takes this format and
-/// converts it into a hardware accessible format.
-///
-/// #### Usage
-/// To draw all draw commands accumulated over a frame you need your own render
-/// backend able to draw a number of 2D primitives. This includes at least
-/// filled and stroked rectangles, circles, text, lines, triangles and scissors.
-/// As soon as this criterion is met you can iterate over each draw command
-/// and execute each draw command in a interpreter like fashion:
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// const struct nk_command *cmd = 0;
-/// nk_foreach(cmd, &ctx) {
-/// switch (cmd->type) {
-/// case NK_COMMAND_LINE:
-/// your_draw_line_function(...)
-/// break;
-/// case NK_COMMAND_RECT
-/// your_draw_rect_function(...)
-/// break;
-/// case //...:
-/// //[...]
-/// }
-/// }
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// In program flow context draw commands need to be executed after input has been
-/// gathered and the complete UI with windows and their contained widgets have
-/// been executed and before calling `nk_clear` which frees all previously
-/// allocated draw commands.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_context ctx;
-/// nk_init_xxx(&ctx, ...);
-/// while (1) {
-/// Event evt;
-/// nk_input_begin(&ctx);
-/// while (GetEvent(&evt)) {
-/// if (evt.type == MOUSE_MOVE)
-/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y);
-/// else if (evt.type == [...]) {
-/// [...]
-/// }
-/// }
-/// nk_input_end(&ctx);
-/// //
-/// // [...]
-/// //
-/// const struct nk_command *cmd = 0;
-/// nk_foreach(cmd, &ctx) {
-/// switch (cmd->type) {
-/// case NK_COMMAND_LINE:
-/// your_draw_line_function(...)
-/// break;
-/// case NK_COMMAND_RECT
-/// your_draw_rect_function(...)
-/// break;
-/// case ...:
-/// // [...]
-/// }
-/// nk_clear(&ctx);
-/// }
-/// nk_free(&ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// You probably noticed that you have to draw all of the UI each frame which is
-/// quite wasteful. While the actual UI updating loop is quite fast rendering
-/// without actually needing it is not. So there are multiple things you could do.
-///
-/// First is only update on input. This of course is only an option if your
-/// application only depends on the UI and does not require any outside calculations.
-/// If you actually only update on input make sure to update the UI two times each
-/// frame and call `nk_clear` directly after the first pass and only draw in
-/// the second pass. In addition it is recommended to also add additional timers
-/// to make sure the UI is not drawn more than a fixed number of frames per second.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_context ctx;
-/// nk_init_xxx(&ctx, ...);
-/// while (1) {
-/// // [...wait for input ]
-/// // [...do two UI passes ...]
-/// do_ui(...)
-/// nk_clear(&ctx);
-/// do_ui(...)
-/// //
-/// // draw
-/// const struct nk_command *cmd = 0;
-/// nk_foreach(cmd, &ctx) {
-/// switch (cmd->type) {
-/// case NK_COMMAND_LINE:
-/// your_draw_line_function(...)
-/// break;
-/// case NK_COMMAND_RECT
-/// your_draw_rect_function(...)
-/// break;
-/// case ...:
-/// //[...]
-/// }
-/// nk_clear(&ctx);
-/// }
-/// nk_free(&ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// The second probably more applicable trick is to only draw if anything changed.
-/// It is not really useful for applications with continuous draw loop but
-/// quite useful for desktop applications. To actually get nuklear to only
-/// draw on changes you first have to define `NK_ZERO_COMMAND_MEMORY` and
-/// allocate a memory buffer that will store each unique drawing output.
-/// After each frame you compare the draw command memory inside the library
-/// with your allocated buffer by memcmp. If memcmp detects differences
-/// you have to copy the command buffer into the allocated buffer
-/// and then draw like usual (this example uses fixed memory but you could
-/// use dynamically allocated memory).
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// //[... other defines ...]
-/// #define NK_ZERO_COMMAND_MEMORY
-/// #include "nuklear.h"
-/// //
-/// // setup context
-/// struct nk_context ctx;
-/// void *last = calloc(1,64*1024);
-/// void *buf = calloc(1,64*1024);
-/// nk_init_fixed(&ctx, buf, 64*1024);
-/// //
-/// // loop
-/// while (1) {
-/// // [...input...]
-/// // [...ui...]
-/// void *cmds = nk_buffer_memory(&ctx.memory);
-/// if (memcmp(cmds, last, ctx.memory.allocated)) {
-/// memcpy(last,cmds,ctx.memory.allocated);
-/// const struct nk_command *cmd = 0;
-/// nk_foreach(cmd, &ctx) {
-/// switch (cmd->type) {
-/// case NK_COMMAND_LINE:
-/// your_draw_line_function(...)
-/// break;
-/// case NK_COMMAND_RECT
-/// your_draw_rect_function(...)
-/// break;
-/// case ...:
-/// // [...]
-/// }
-/// }
-/// }
-/// nk_clear(&ctx);
-/// }
-/// nk_free(&ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Finally while using draw commands makes sense for higher abstracted platforms like
-/// X11 and Win32 or drawing libraries it is often desirable to use graphics
-/// hardware directly. Therefore it is possible to just define
-/// `NK_INCLUDE_VERTEX_BUFFER_OUTPUT` which includes optional vertex output.
-/// To access the vertex output you first have to convert all draw commands into
-/// vertexes by calling `nk_convert` which takes in your preferred vertex format.
-/// After successfully converting all draw commands just iterate over and execute all
-/// vertex draw commands:
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// // fill configuration
-/// struct your_vertex
-/// {
-/// float pos[2]; // important to keep it to 2 floats
-/// float uv[2];
-/// unsigned char col[4];
-/// };
-/// struct nk_convert_config cfg = {};
-/// static const struct nk_draw_vertex_layout_element vertex_layout[] = {
-/// {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct your_vertex, pos)},
-/// {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct your_vertex, uv)},
-/// {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct your_vertex, col)},
-/// {NK_VERTEX_LAYOUT_END}
-/// };
-/// cfg.shape_AA = NK_ANTI_ALIASING_ON;
-/// cfg.line_AA = NK_ANTI_ALIASING_ON;
-/// cfg.vertex_layout = vertex_layout;
-/// cfg.vertex_size = sizeof(struct your_vertex);
-/// cfg.vertex_alignment = NK_ALIGNOF(struct your_vertex);
-/// cfg.circle_segment_count = 22;
-/// cfg.curve_segment_count = 22;
-/// cfg.arc_segment_count = 22;
-/// cfg.global_alpha = 1.0f;
-/// cfg.null = dev->null;
-/// //
-/// // setup buffers and convert
-/// struct nk_buffer cmds, verts, idx;
-/// nk_buffer_init_default(&cmds);
-/// nk_buffer_init_default(&verts);
-/// nk_buffer_init_default(&idx);
-/// nk_convert(&ctx, &cmds, &verts, &idx, &cfg);
-/// //
-/// // draw
-/// nk_draw_foreach(cmd, &ctx, &cmds) {
-/// if (!cmd->elem_count) continue;
-/// //[...]
-/// }
-/// nk_buffer_free(&cms);
-/// nk_buffer_free(&verts);
-/// nk_buffer_free(&idx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// #### Reference
-/// Function | Description
-/// --------------------|-------------------------------------------------------
-/// __nk__begin__ | Returns the first draw command in the context draw command list to be drawn
-/// __nk__next__ | Increments the draw command iterator to the next command inside the context draw command list
-/// __nk_foreach__ | Iterates over each draw command inside the context draw command list
-/// __nk_convert__ | Converts from the abstract draw commands list into a hardware accessible vertex format
-/// __nk_draw_begin__ | Returns the first vertex command in the context vertex draw list to be executed
-/// __nk__draw_next__ | Increments the vertex command iterator to the next command inside the context vertex command list
-/// __nk__draw_end__ | Returns the end of the vertex draw list
-/// __nk_draw_foreach__ | Iterates over each vertex draw command inside the vertex draw list
-*/
-enum nk_anti_aliasing {NK_ANTI_ALIASING_OFF, NK_ANTI_ALIASING_ON};
-enum nk_convert_result {
- NK_CONVERT_SUCCESS = 0,
- NK_CONVERT_INVALID_PARAM = 1,
- NK_CONVERT_COMMAND_BUFFER_FULL = NK_FLAG(1),
- NK_CONVERT_VERTEX_BUFFER_FULL = NK_FLAG(2),
- NK_CONVERT_ELEMENT_BUFFER_FULL = NK_FLAG(3)
-};
-struct nk_draw_null_texture {
- nk_handle texture; /* texture handle to a texture with a white pixel */
- struct nk_vec2 uv; /* coordinates to a white pixel in the texture */
-};
-struct nk_convert_config {
- float global_alpha; /* global alpha value */
- enum nk_anti_aliasing line_AA; /* line anti-aliasing flag can be turned off if you are tight on memory */
- enum nk_anti_aliasing shape_AA; /* shape anti-aliasing flag can be turned off if you are tight on memory */
- unsigned circle_segment_count; /* number of segments used for circles: default to 22 */
- unsigned arc_segment_count; /* number of segments used for arcs: default to 22 */
- unsigned curve_segment_count; /* number of segments used for curves: default to 22 */
- struct nk_draw_null_texture null; /* handle to texture with a white pixel for shape drawing */
- const struct nk_draw_vertex_layout_element *vertex_layout; /* describes the vertex output format and packing */
- nk_size vertex_size; /* sizeof one vertex for vertex packing */
- nk_size vertex_alignment; /* vertex alignment: Can be obtained by NK_ALIGNOF */
-};
-/*/// #### nk__begin
-/// Returns a draw command list iterator to iterate all draw
-/// commands accumulated over one frame.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// const struct nk_command* nk__begin(struct nk_context*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | must point to an previously initialized `nk_context` struct at the end of a frame
-///
-/// Returns draw command pointer pointing to the first command inside the draw command list
-*/
-NK_API const struct nk_command* nk__begin(struct nk_context*);
-/*/// #### nk__next
-/// Returns draw command pointer pointing to the next command inside the draw command list
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// const struct nk_command* nk__next(struct nk_context*, const struct nk_command*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame
-/// __cmd__ | Must point to an previously a draw command either returned by `nk__begin` or `nk__next`
-///
-/// Returns draw command pointer pointing to the next command inside the draw command list
-*/
-NK_API const struct nk_command* nk__next(struct nk_context*, const struct nk_command*);
-/*/// #### nk_foreach
-/// Iterates over each draw command inside the context draw command list
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// #define nk_foreach(c, ctx)
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame
-/// __cmd__ | Command pointer initialized to NULL
-///
-/// Iterates over each draw command inside the context draw command list
-*/
-#define nk_foreach(c, ctx) for((c) = nk__begin(ctx); (c) != 0; (c) = nk__next(ctx,c))
-#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT
-/*/// #### nk_convert
-/// Converts all internal draw commands into vertex draw commands and fills
-/// three buffers with vertexes, vertex draw commands and vertex indices. The vertex format
-/// as well as some other configuration values have to be configured by filling out a
-/// `nk_convert_config` struct.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// nk_flags nk_convert(struct nk_context *ctx, struct nk_buffer *cmds,
-/// struct nk_buffer *vertices, struct nk_buffer *elements, const struct nk_convert_config*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame
-/// __cmds__ | Must point to a previously initialized buffer to hold converted vertex draw commands
-/// __vertices__| Must point to a previously initialized buffer to hold all produced vertices
-/// __elements__| Must point to a previously initialized buffer to hold all produced vertex indices
-/// __config__ | Must point to a filled out `nk_config` struct to configure the conversion process
-///
-/// Returns one of enum nk_convert_result error codes
-///
-/// Parameter | Description
-/// --------------------------------|-----------------------------------------------------------
-/// NK_CONVERT_SUCCESS | Signals a successful draw command to vertex buffer conversion
-/// NK_CONVERT_INVALID_PARAM | An invalid argument was passed in the function call
-/// NK_CONVERT_COMMAND_BUFFER_FULL | The provided buffer for storing draw commands is full or failed to allocate more memory
-/// NK_CONVERT_VERTEX_BUFFER_FULL | The provided buffer for storing vertices is full or failed to allocate more memory
-/// NK_CONVERT_ELEMENT_BUFFER_FULL | The provided buffer for storing indicies is full or failed to allocate more memory
-*/
-NK_API nk_flags nk_convert(struct nk_context*, struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, const struct nk_convert_config*);
-/*/// #### nk__draw_begin
-/// Returns a draw vertex command buffer iterator to iterate over the vertex draw command buffer
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// const struct nk_draw_command* nk__draw_begin(const struct nk_context*, const struct nk_buffer*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame
-/// __buf__ | Must point to an previously by `nk_convert` filled out vertex draw command buffer
-///
-/// Returns vertex draw command pointer pointing to the first command inside the vertex draw command buffer
-*/
-NK_API const struct nk_draw_command* nk__draw_begin(const struct nk_context*, const struct nk_buffer*);
-/*/// #### nk__draw_end
-/// Returns the vertex draw command at the end of the vertex draw command buffer
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// const struct nk_draw_command* nk__draw_end(const struct nk_context *ctx, const struct nk_buffer *buf);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame
-/// __buf__ | Must point to an previously by `nk_convert` filled out vertex draw command buffer
-///
-/// Returns vertex draw command pointer pointing to the end of the last vertex draw command inside the vertex draw command buffer
-*/
-NK_API const struct nk_draw_command* nk__draw_end(const struct nk_context*, const struct nk_buffer*);
-/*/// #### nk__draw_next
-/// Increments the vertex draw command buffer iterator
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// const struct nk_draw_command* nk__draw_next(const struct nk_draw_command*, const struct nk_buffer*, const struct nk_context*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __cmd__ | Must point to an previously either by `nk__draw_begin` or `nk__draw_next` returned vertex draw command
-/// __buf__ | Must point to an previously by `nk_convert` filled out vertex draw command buffer
-/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame
-///
-/// Returns vertex draw command pointer pointing to the end of the last vertex draw command inside the vertex draw command buffer
-*/
-NK_API const struct nk_draw_command* nk__draw_next(const struct nk_draw_command*, const struct nk_buffer*, const struct nk_context*);
-/*/// #### nk_draw_foreach
-/// Iterates over each vertex draw command inside a vertex draw command buffer
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// #define nk_draw_foreach(cmd,ctx, b)
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __cmd__ | `nk_draw_command`iterator set to NULL
-/// __buf__ | Must point to an previously by `nk_convert` filled out vertex draw command buffer
-/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame
-*/
-#define nk_draw_foreach(cmd,ctx, b) for((cmd)=nk__draw_begin(ctx, b); (cmd)!=0; (cmd)=nk__draw_next(cmd, b, ctx))
-#endif
-/* =============================================================================
- *
- * WINDOW
- *
- * =============================================================================
-/// ### Window
-/// Windows are the main persistent state used inside nuklear and are life time
-/// controlled by simply "retouching" (i.e. calling) each window each frame.
-/// All widgets inside nuklear can only be added inside the function pair `nk_begin_xxx`
-/// and `nk_end`. Calling any widgets outside these two functions will result in an
-/// assert in debug or no state change in release mode.
-///
-/// Each window holds frame persistent state like position, size, flags, state tables,
-/// and some garbage collected internal persistent widget state. Each window
-/// is linked into a window stack list which determines the drawing and overlapping
-/// order. The topmost window thereby is the currently active window.
-///
-/// To change window position inside the stack occurs either automatically by
-/// user input by being clicked on or programmatically by calling `nk_window_focus`.
-/// Windows by default are visible unless explicitly being defined with flag
-/// `NK_WINDOW_HIDDEN`, the user clicked the close button on windows with flag
-/// `NK_WINDOW_CLOSABLE` or if a window was explicitly hidden by calling
-/// `nk_window_show`. To explicitly close and destroy a window call `nk_window_close`.
-///
-/// #### Usage
-/// To create and keep a window you have to call one of the two `nk_begin_xxx`
-/// functions to start window declarations and `nk_end` at the end. Furthermore it
-/// is recommended to check the return value of `nk_begin_xxx` and only process
-/// widgets inside the window if the value is not 0. Either way you have to call
-/// `nk_end` at the end of window declarations. Furthermore, do not attempt to
-/// nest `nk_begin_xxx` calls which will hopefully result in an assert or if not
-/// in a segmentation fault.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// if (nk_begin_xxx(...) {
-/// // [... widgets ...]
-/// }
-/// nk_end(ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// In the grand concept window and widget declarations need to occur after input
-/// handling and before drawing to screen. Not doing so can result in higher
-/// latency or at worst invalid behavior. Furthermore make sure that `nk_clear`
-/// is called at the end of the frame. While nuklear's default platform backends
-/// already call `nk_clear` for you if you write your own backend not calling
-/// `nk_clear` can cause asserts or even worse undefined behavior.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_context ctx;
-/// nk_init_xxx(&ctx, ...);
-/// while (1) {
-/// Event evt;
-/// nk_input_begin(&ctx);
-/// while (GetEvent(&evt)) {
-/// if (evt.type == MOUSE_MOVE)
-/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y);
-/// else if (evt.type == [...]) {
-/// nk_input_xxx(...);
-/// }
-/// }
-/// nk_input_end(&ctx);
-///
-/// if (nk_begin_xxx(...) {
-/// //[...]
-/// }
-/// nk_end(ctx);
-///
-/// const struct nk_command *cmd = 0;
-/// nk_foreach(cmd, &ctx) {
-/// case NK_COMMAND_LINE:
-/// your_draw_line_function(...)
-/// break;
-/// case NK_COMMAND_RECT
-/// your_draw_rect_function(...)
-/// break;
-/// case //...:
-/// //[...]
-/// }
-/// nk_clear(&ctx);
-/// }
-/// nk_free(&ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// #### Reference
-/// Function | Description
-/// ------------------------------------|----------------------------------------
-/// nk_begin | Starts a new window; needs to be called every frame for every window (unless hidden) or otherwise the window gets removed
-/// nk_begin_titled | Extended window start with separated title and identifier to allow multiple windows with same name but not title
-/// nk_end | Needs to be called at the end of the window building process to process scaling, scrollbars and general cleanup
-//
-/// nk_window_find | Finds and returns the window with give name
-/// nk_window_get_bounds | Returns a rectangle with screen position and size of the currently processed window.
-/// nk_window_get_position | Returns the position of the currently processed window
-/// nk_window_get_size | Returns the size with width and height of the currently processed window
-/// nk_window_get_width | Returns the width of the currently processed window
-/// nk_window_get_height | Returns the height of the currently processed window
-/// nk_window_get_panel | Returns the underlying panel which contains all processing state of the current window
-/// nk_window_get_content_region | Returns the position and size of the currently visible and non-clipped space inside the currently processed window
-/// nk_window_get_content_region_min | Returns the upper rectangle position of the currently visible and non-clipped space inside the currently processed window
-/// nk_window_get_content_region_max | Returns the upper rectangle position of the currently visible and non-clipped space inside the currently processed window
-/// nk_window_get_content_region_size | Returns the size of the currently visible and non-clipped space inside the currently processed window
-/// nk_window_get_canvas | Returns the draw command buffer. Can be used to draw custom widgets
-/// nk_window_get_scroll | Gets the scroll offset of the current window
-/// nk_window_has_focus | Returns if the currently processed window is currently active
-/// nk_window_is_collapsed | Returns if the window with given name is currently minimized/collapsed
-/// nk_window_is_closed | Returns if the currently processed window was closed
-/// nk_window_is_hidden | Returns if the currently processed window was hidden
-/// nk_window_is_active | Same as nk_window_has_focus for some reason
-/// nk_window_is_hovered | Returns if the currently processed window is currently being hovered by mouse
-/// nk_window_is_any_hovered | Return if any window currently hovered
-/// nk_item_is_any_active | Returns if any window or widgets is currently hovered or active
-//
-/// nk_window_set_bounds | Updates position and size of the currently processed window
-/// nk_window_set_position | Updates position of the currently process window
-/// nk_window_set_size | Updates the size of the currently processed window
-/// nk_window_set_focus | Set the currently processed window as active window
-/// nk_window_set_scroll | Sets the scroll offset of the current window
-//
-/// nk_window_close | Closes the window with given window name which deletes the window at the end of the frame
-/// nk_window_collapse | Collapses the window with given window name
-/// nk_window_collapse_if | Collapses the window with given window name if the given condition was met
-/// nk_window_show | Hides a visible or reshows a hidden window
-/// nk_window_show_if | Hides/shows a window depending on condition
-*/
-/*
-/// #### nk_panel_flags
-/// Flag | Description
-/// ----------------------------|----------------------------------------
-/// NK_WINDOW_BORDER | Draws a border around the window to visually separate window from the background
-/// NK_WINDOW_MOVABLE | The movable flag indicates that a window can be moved by user input or by dragging the window header
-/// NK_WINDOW_SCALABLE | The scalable flag indicates that a window can be scaled by user input by dragging a scaler icon at the button of the window
-/// NK_WINDOW_CLOSABLE | Adds a closable icon into the header
-/// NK_WINDOW_MINIMIZABLE | Adds a minimize icon into the header
-/// NK_WINDOW_NO_SCROLLBAR | Removes the scrollbar from the window
-/// NK_WINDOW_TITLE | Forces a header at the top at the window showing the title
-/// NK_WINDOW_SCROLL_AUTO_HIDE | Automatically hides the window scrollbar if no user interaction: also requires delta time in `nk_context` to be set each frame
-/// NK_WINDOW_BACKGROUND | Always keep window in the background
-/// NK_WINDOW_SCALE_LEFT | Puts window scaler in the left-bottom corner instead right-bottom
-/// NK_WINDOW_NO_INPUT | Prevents window of scaling, moving or getting focus
-///
-/// #### nk_collapse_states
-/// State | Description
-/// ----------------|-----------------------------------------------------------
-/// __NK_MINIMIZED__| UI section is collased and not visibile until maximized
-/// __NK_MAXIMIZED__| UI section is extended and visibile until minimized
-///
-*/
-enum nk_panel_flags {
- NK_WINDOW_BORDER = NK_FLAG(0),
- NK_WINDOW_MOVABLE = NK_FLAG(1),
- NK_WINDOW_SCALABLE = NK_FLAG(2),
- NK_WINDOW_CLOSABLE = NK_FLAG(3),
- NK_WINDOW_MINIMIZABLE = NK_FLAG(4),
- NK_WINDOW_NO_SCROLLBAR = NK_FLAG(5),
- NK_WINDOW_TITLE = NK_FLAG(6),
- NK_WINDOW_SCROLL_AUTO_HIDE = NK_FLAG(7),
- NK_WINDOW_BACKGROUND = NK_FLAG(8),
- NK_WINDOW_SCALE_LEFT = NK_FLAG(9),
- NK_WINDOW_NO_INPUT = NK_FLAG(10)
-};
-/*/// #### nk_begin
-/// Starts a new window; needs to be called every frame for every
-/// window (unless hidden) or otherwise the window gets removed
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_begin(struct nk_context *ctx, const char *title, struct nk_rect bounds, nk_flags flags);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __title__ | Window title and identifier. Needs to be persistent over frames to identify the window
-/// __bounds__ | Initial position and window size. However if you do not define `NK_WINDOW_SCALABLE` or `NK_WINDOW_MOVABLE` you can set window position and size every frame
-/// __flags__ | Window flags defined in the nk_panel_flags section with a number of different window behaviors
-///
-/// Returns `true(1)` if the window can be filled up with widgets from this point
-/// until `nk_end` or `false(0)` otherwise for example if minimized
-*/
-NK_API nk_bool nk_begin(struct nk_context *ctx, const char *title, struct nk_rect bounds, nk_flags flags);
-/*/// #### nk_begin_titled
-/// Extended window start with separated title and identifier to allow multiple
-/// windows with same title but not name
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_begin_titled(struct nk_context *ctx, const char *name, const char *title, struct nk_rect bounds, nk_flags flags);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __name__ | Window identifier. Needs to be persistent over frames to identify the window
-/// __title__ | Window title displayed inside header if flag `NK_WINDOW_TITLE` or either `NK_WINDOW_CLOSABLE` or `NK_WINDOW_MINIMIZED` was set
-/// __bounds__ | Initial position and window size. However if you do not define `NK_WINDOW_SCALABLE` or `NK_WINDOW_MOVABLE` you can set window position and size every frame
-/// __flags__ | Window flags defined in the nk_panel_flags section with a number of different window behaviors
-///
-/// Returns `true(1)` if the window can be filled up with widgets from this point
-/// until `nk_end` or `false(0)` otherwise for example if minimized
-*/
-NK_API nk_bool nk_begin_titled(struct nk_context *ctx, const char *name, const char *title, struct nk_rect bounds, nk_flags flags);
-/*/// #### nk_end
-/// Needs to be called at the end of the window building process to process scaling, scrollbars and general cleanup.
-/// All widget calls after this functions will result in asserts or no state changes
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_end(struct nk_context *ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-*/
-NK_API void nk_end(struct nk_context *ctx);
-/*/// #### nk_window_find
-/// Finds and returns a window from passed name
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_window *nk_window_find(struct nk_context *ctx, const char *name);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __name__ | Window identifier
-///
-/// Returns a `nk_window` struct pointing to the identified window or NULL if
-/// no window with the given name was found
-*/
-NK_API struct nk_window *nk_window_find(struct nk_context *ctx, const char *name);
-/*/// #### nk_window_get_bounds
-/// Returns a rectangle with screen position and size of the currently processed window
-///
-/// !!! WARNING
-/// Only call this function between calls `nk_begin_xxx` and `nk_end`
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_rect nk_window_get_bounds(const struct nk_context *ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-///
-/// Returns a `nk_rect` struct with window upper left window position and size
-*/
-NK_API struct nk_rect nk_window_get_bounds(const struct nk_context *ctx);
-/*/// #### nk_window_get_position
-/// Returns the position of the currently processed window.
-///
-/// !!! WARNING
-/// Only call this function between calls `nk_begin_xxx` and `nk_end`
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_vec2 nk_window_get_position(const struct nk_context *ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-///
-/// Returns a `nk_vec2` struct with window upper left position
-*/
-NK_API struct nk_vec2 nk_window_get_position(const struct nk_context *ctx);
-/*/// #### nk_window_get_size
-/// Returns the size with width and height of the currently processed window.
-///
-/// !!! WARNING
-/// Only call this function between calls `nk_begin_xxx` and `nk_end`
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_vec2 nk_window_get_size(const struct nk_context *ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-///
-/// Returns a `nk_vec2` struct with window width and height
-*/
-NK_API struct nk_vec2 nk_window_get_size(const struct nk_context*);
-/*/// #### nk_window_get_width
-/// Returns the width of the currently processed window.
-///
-/// !!! WARNING
-/// Only call this function between calls `nk_begin_xxx` and `nk_end`
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// float nk_window_get_width(const struct nk_context *ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-///
-/// Returns the current window width
-*/
-NK_API float nk_window_get_width(const struct nk_context*);
-/*/// #### nk_window_get_height
-/// Returns the height of the currently processed window.
-///
-/// !!! WARNING
-/// Only call this function between calls `nk_begin_xxx` and `nk_end`
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// float nk_window_get_height(const struct nk_context *ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-///
-/// Returns the current window height
-*/
-NK_API float nk_window_get_height(const struct nk_context*);
-/*/// #### nk_window_get_panel
-/// Returns the underlying panel which contains all processing state of the current window.
-///
-/// !!! WARNING
-/// Only call this function between calls `nk_begin_xxx` and `nk_end`
-/// !!! WARNING
-/// Do not keep the returned panel pointer around, it is only valid until `nk_end`
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_panel* nk_window_get_panel(struct nk_context *ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-///
-/// Returns a pointer to window internal `nk_panel` state.
-*/
-NK_API struct nk_panel* nk_window_get_panel(struct nk_context*);
-/*/// #### nk_window_get_content_region
-/// Returns the position and size of the currently visible and non-clipped space
-/// inside the currently processed window.
-///
-/// !!! WARNING
-/// Only call this function between calls `nk_begin_xxx` and `nk_end`
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_rect nk_window_get_content_region(struct nk_context *ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-///
-/// Returns `nk_rect` struct with screen position and size (no scrollbar offset)
-/// of the visible space inside the current window
-*/
-NK_API struct nk_rect nk_window_get_content_region(struct nk_context*);
-/*/// #### nk_window_get_content_region_min
-/// Returns the upper left position of the currently visible and non-clipped
-/// space inside the currently processed window.
-///
-/// !!! WARNING
-/// Only call this function between calls `nk_begin_xxx` and `nk_end`
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_vec2 nk_window_get_content_region_min(struct nk_context *ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-///
-/// returns `nk_vec2` struct with upper left screen position (no scrollbar offset)
-/// of the visible space inside the current window
-*/
-NK_API struct nk_vec2 nk_window_get_content_region_min(struct nk_context*);
-/*/// #### nk_window_get_content_region_max
-/// Returns the lower right screen position of the currently visible and
-/// non-clipped space inside the currently processed window.
-///
-/// !!! WARNING
-/// Only call this function between calls `nk_begin_xxx` and `nk_end`
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_vec2 nk_window_get_content_region_max(struct nk_context *ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-///
-/// Returns `nk_vec2` struct with lower right screen position (no scrollbar offset)
-/// of the visible space inside the current window
-*/
-NK_API struct nk_vec2 nk_window_get_content_region_max(struct nk_context*);
-/*/// #### nk_window_get_content_region_size
-/// Returns the size of the currently visible and non-clipped space inside the
-/// currently processed window
-///
-/// !!! WARNING
-/// Only call this function between calls `nk_begin_xxx` and `nk_end`
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_vec2 nk_window_get_content_region_size(struct nk_context *ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-///
-/// Returns `nk_vec2` struct with size the visible space inside the current window
-*/
-NK_API struct nk_vec2 nk_window_get_content_region_size(struct nk_context*);
-/*/// #### nk_window_get_canvas
-/// Returns the draw command buffer. Can be used to draw custom widgets
-/// !!! WARNING
-/// Only call this function between calls `nk_begin_xxx` and `nk_end`
-/// !!! WARNING
-/// Do not keep the returned command buffer pointer around it is only valid until `nk_end`
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_command_buffer* nk_window_get_canvas(struct nk_context *ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-///
-/// Returns a pointer to window internal `nk_command_buffer` struct used as
-/// drawing canvas. Can be used to do custom drawing.
-*/
-NK_API struct nk_command_buffer* nk_window_get_canvas(struct nk_context*);
-/*/// #### nk_window_get_scroll
-/// Gets the scroll offset for the current window
-/// !!! WARNING
-/// Only call this function between calls `nk_begin_xxx` and `nk_end`
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_window_get_scroll(struct nk_context *ctx, nk_uint *offset_x, nk_uint *offset_y);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// -------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __offset_x__ | A pointer to the x offset output (or NULL to ignore)
-/// __offset_y__ | A pointer to the y offset output (or NULL to ignore)
-*/
-NK_API void nk_window_get_scroll(struct nk_context*, nk_uint *offset_x, nk_uint *offset_y);
-/*/// #### nk_window_has_focus
-/// Returns if the currently processed window is currently active
-/// !!! WARNING
-/// Only call this function between calls `nk_begin_xxx` and `nk_end`
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_window_has_focus(const struct nk_context *ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-///
-/// Returns `false(0)` if current window is not active or `true(1)` if it is
-*/
-NK_API nk_bool nk_window_has_focus(const struct nk_context*);
-/*/// #### nk_window_is_hovered
-/// Return if the current window is being hovered
-/// !!! WARNING
-/// Only call this function between calls `nk_begin_xxx` and `nk_end`
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_window_is_hovered(struct nk_context *ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-///
-/// Returns `true(1)` if current window is hovered or `false(0)` otherwise
-*/
-NK_API nk_bool nk_window_is_hovered(struct nk_context*);
-/*/// #### nk_window_is_collapsed
-/// Returns if the window with given name is currently minimized/collapsed
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_window_is_collapsed(struct nk_context *ctx, const char *name);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __name__ | Identifier of window you want to check if it is collapsed
-///
-/// Returns `true(1)` if current window is minimized and `false(0)` if window not
-/// found or is not minimized
-*/
-NK_API nk_bool nk_window_is_collapsed(struct nk_context *ctx, const char *name);
-/*/// #### nk_window_is_closed
-/// Returns if the window with given name was closed by calling `nk_close`
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_window_is_closed(struct nk_context *ctx, const char *name);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __name__ | Identifier of window you want to check if it is closed
-///
-/// Returns `true(1)` if current window was closed or `false(0)` window not found or not closed
-*/
-NK_API nk_bool nk_window_is_closed(struct nk_context*, const char*);
-/*/// #### nk_window_is_hidden
-/// Returns if the window with given name is hidden
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_window_is_hidden(struct nk_context *ctx, const char *name);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __name__ | Identifier of window you want to check if it is hidden
-///
-/// Returns `true(1)` if current window is hidden or `false(0)` window not found or visible
-*/
-NK_API nk_bool nk_window_is_hidden(struct nk_context*, const char*);
-/*/// #### nk_window_is_active
-/// Same as nk_window_has_focus for some reason
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_window_is_active(struct nk_context *ctx, const char *name);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __name__ | Identifier of window you want to check if it is active
-///
-/// Returns `true(1)` if current window is active or `false(0)` window not found or not active
-*/
-NK_API nk_bool nk_window_is_active(struct nk_context*, const char*);
-/*/// #### nk_window_is_any_hovered
-/// Returns if the any window is being hovered
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_window_is_any_hovered(struct nk_context*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-///
-/// Returns `true(1)` if any window is hovered or `false(0)` otherwise
-*/
-NK_API nk_bool nk_window_is_any_hovered(struct nk_context*);
-/*/// #### nk_item_is_any_active
-/// Returns if the any window is being hovered or any widget is currently active.
-/// Can be used to decide if input should be processed by UI or your specific input handling.
-/// Example could be UI and 3D camera to move inside a 3D space.
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_item_is_any_active(struct nk_context*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-///
-/// Returns `true(1)` if any window is hovered or any item is active or `false(0)` otherwise
-*/
-NK_API nk_bool nk_item_is_any_active(struct nk_context*);
-/*/// #### nk_window_set_bounds
-/// Updates position and size of window with passed in name
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_window_set_bounds(struct nk_context*, const char *name, struct nk_rect bounds);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __name__ | Identifier of the window to modify both position and size
-/// __bounds__ | Must point to a `nk_rect` struct with the new position and size
-*/
-NK_API void nk_window_set_bounds(struct nk_context*, const char *name, struct nk_rect bounds);
-/*/// #### nk_window_set_position
-/// Updates position of window with passed name
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_window_set_position(struct nk_context*, const char *name, struct nk_vec2 pos);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __name__ | Identifier of the window to modify both position
-/// __pos__ | Must point to a `nk_vec2` struct with the new position
-*/
-NK_API void nk_window_set_position(struct nk_context*, const char *name, struct nk_vec2 pos);
-/*/// #### nk_window_set_size
-/// Updates size of window with passed in name
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_window_set_size(struct nk_context*, const char *name, struct nk_vec2);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __name__ | Identifier of the window to modify both window size
-/// __size__ | Must point to a `nk_vec2` struct with new window size
-*/
-NK_API void nk_window_set_size(struct nk_context*, const char *name, struct nk_vec2);
-/*/// #### nk_window_set_focus
-/// Sets the window with given name as active
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_window_set_focus(struct nk_context*, const char *name);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __name__ | Identifier of the window to set focus on
-*/
-NK_API void nk_window_set_focus(struct nk_context*, const char *name);
-/*/// #### nk_window_set_scroll
-/// Sets the scroll offset for the current window
-/// !!! WARNING
-/// Only call this function between calls `nk_begin_xxx` and `nk_end`
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_window_set_scroll(struct nk_context *ctx, nk_uint offset_x, nk_uint offset_y);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// -------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __offset_x__ | The x offset to scroll to
-/// __offset_y__ | The y offset to scroll to
-*/
-NK_API void nk_window_set_scroll(struct nk_context*, nk_uint offset_x, nk_uint offset_y);
-/*/// #### nk_window_close
-/// Closes a window and marks it for being freed at the end of the frame
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_window_close(struct nk_context *ctx, const char *name);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __name__ | Identifier of the window to close
-*/
-NK_API void nk_window_close(struct nk_context *ctx, const char *name);
-/*/// #### nk_window_collapse
-/// Updates collapse state of a window with given name
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_window_collapse(struct nk_context*, const char *name, enum nk_collapse_states state);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __name__ | Identifier of the window to close
-/// __state__ | value out of nk_collapse_states section
-*/
-NK_API void nk_window_collapse(struct nk_context*, const char *name, enum nk_collapse_states state);
-/*/// #### nk_window_collapse_if
-/// Updates collapse state of a window with given name if given condition is met
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_window_collapse_if(struct nk_context*, const char *name, enum nk_collapse_states, int cond);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __name__ | Identifier of the window to either collapse or maximize
-/// __state__ | value out of nk_collapse_states section the window should be put into
-/// __cond__ | condition that has to be met to actually commit the collapse state change
-*/
-NK_API void nk_window_collapse_if(struct nk_context*, const char *name, enum nk_collapse_states, int cond);
-/*/// #### nk_window_show
-/// updates visibility state of a window with given name
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_window_show(struct nk_context*, const char *name, enum nk_show_states);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __name__ | Identifier of the window to either collapse or maximize
-/// __state__ | state with either visible or hidden to modify the window with
-*/
-NK_API void nk_window_show(struct nk_context*, const char *name, enum nk_show_states);
-/*/// #### nk_window_show_if
-/// Updates visibility state of a window with given name if a given condition is met
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_window_show_if(struct nk_context*, const char *name, enum nk_show_states, int cond);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __name__ | Identifier of the window to either hide or show
-/// __state__ | state with either visible or hidden to modify the window with
-/// __cond__ | condition that has to be met to actually commit the visbility state change
-*/
-NK_API void nk_window_show_if(struct nk_context*, const char *name, enum nk_show_states, int cond);
-/* =============================================================================
- *
- * LAYOUT
- *
- * =============================================================================
-/// ### Layouting
-/// Layouting in general describes placing widget inside a window with position and size.
-/// While in this particular implementation there are five different APIs for layouting
-/// each with different trade offs between control and ease of use.
-///
-/// All layouting methods in this library are based around the concept of a row.
-/// A row has a height the window content grows by and a number of columns and each
-/// layouting method specifies how each widget is placed inside the row.
-/// After a row has been allocated by calling a layouting functions and then
-/// filled with widgets will advance an internal pointer over the allocated row.
-///
-/// To actually define a layout you just call the appropriate layouting function
-/// and each subsequent widget call will place the widget as specified. Important
-/// here is that if you define more widgets then columns defined inside the layout
-/// functions it will allocate the next row without you having to make another layouting
-/// call.
-///
-/// Biggest limitation with using all these APIs outside the `nk_layout_space_xxx` API
-/// is that you have to define the row height for each. However the row height
-/// often depends on the height of the font.
-///
-/// To fix that internally nuklear uses a minimum row height that is set to the
-/// height plus padding of currently active font and overwrites the row height
-/// value if zero.
-///
-/// If you manually want to change the minimum row height then
-/// use nk_layout_set_min_row_height, and use nk_layout_reset_min_row_height to
-/// reset it back to be derived from font height.
-///
-/// Also if you change the font in nuklear it will automatically change the minimum
-/// row height for you and. This means if you change the font but still want
-/// a minimum row height smaller than the font you have to repush your value.
-///
-/// For actually more advanced UI I would even recommend using the `nk_layout_space_xxx`
-/// layouting method in combination with a cassowary constraint solver (there are
-/// some versions on github with permissive license model) to take over all control over widget
-/// layouting yourself. However for quick and dirty layouting using all the other layouting
-/// functions should be fine.
-///
-/// #### Usage
-/// 1. __nk_layout_row_dynamic__
-/// The easiest layouting function is `nk_layout_row_dynamic`. It provides each
-/// widgets with same horizontal space inside the row and dynamically grows
-/// if the owning window grows in width. So the number of columns dictates
-/// the size of each widget dynamically by formula:
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// widget_width = (window_width - padding - spacing) * (1/colum_count)
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Just like all other layouting APIs if you define more widget than columns this
-/// library will allocate a new row and keep all layouting parameters previously
-/// defined.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// if (nk_begin_xxx(...) {
-/// // first row with height: 30 composed of two widgets
-/// nk_layout_row_dynamic(&ctx, 30, 2);
-/// nk_widget(...);
-/// nk_widget(...);
-/// //
-/// // second row with same parameter as defined above
-/// nk_widget(...);
-/// nk_widget(...);
-/// //
-/// // third row uses 0 for height which will use auto layouting
-/// nk_layout_row_dynamic(&ctx, 0, 2);
-/// nk_widget(...);
-/// nk_widget(...);
-/// }
-/// nk_end(...);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// 2. __nk_layout_row_static__
-/// Another easy layouting function is `nk_layout_row_static`. It provides each
-/// widget with same horizontal pixel width inside the row and does not grow
-/// if the owning window scales smaller or bigger.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// if (nk_begin_xxx(...) {
-/// // first row with height: 30 composed of two widgets with width: 80
-/// nk_layout_row_static(&ctx, 30, 80, 2);
-/// nk_widget(...);
-/// nk_widget(...);
-/// //
-/// // second row with same parameter as defined above
-/// nk_widget(...);
-/// nk_widget(...);
-/// //
-/// // third row uses 0 for height which will use auto layouting
-/// nk_layout_row_static(&ctx, 0, 80, 2);
-/// nk_widget(...);
-/// nk_widget(...);
-/// }
-/// nk_end(...);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// 3. __nk_layout_row_xxx__
-/// A little bit more advanced layouting API are functions `nk_layout_row_begin`,
-/// `nk_layout_row_push` and `nk_layout_row_end`. They allow to directly
-/// specify each column pixel or window ratio in a row. It supports either
-/// directly setting per column pixel width or widget window ratio but not
-/// both. Furthermore it is a immediate mode API so each value is directly
-/// pushed before calling a widget. Therefore the layout is not automatically
-/// repeating like the last two layouting functions.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// if (nk_begin_xxx(...) {
-/// // first row with height: 25 composed of two widgets with width 60 and 40
-/// nk_layout_row_begin(ctx, NK_STATIC, 25, 2);
-/// nk_layout_row_push(ctx, 60);
-/// nk_widget(...);
-/// nk_layout_row_push(ctx, 40);
-/// nk_widget(...);
-/// nk_layout_row_end(ctx);
-/// //
-/// // second row with height: 25 composed of two widgets with window ratio 0.25 and 0.75
-/// nk_layout_row_begin(ctx, NK_DYNAMIC, 25, 2);
-/// nk_layout_row_push(ctx, 0.25f);
-/// nk_widget(...);
-/// nk_layout_row_push(ctx, 0.75f);
-/// nk_widget(...);
-/// nk_layout_row_end(ctx);
-/// //
-/// // third row with auto generated height: composed of two widgets with window ratio 0.25 and 0.75
-/// nk_layout_row_begin(ctx, NK_DYNAMIC, 0, 2);
-/// nk_layout_row_push(ctx, 0.25f);
-/// nk_widget(...);
-/// nk_layout_row_push(ctx, 0.75f);
-/// nk_widget(...);
-/// nk_layout_row_end(ctx);
-/// }
-/// nk_end(...);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// 4. __nk_layout_row__
-/// The array counterpart to API nk_layout_row_xxx is the single nk_layout_row
-/// functions. Instead of pushing either pixel or window ratio for every widget
-/// it allows to define it by array. The trade of for less control is that
-/// `nk_layout_row` is automatically repeating. Otherwise the behavior is the
-/// same.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// if (nk_begin_xxx(...) {
-/// // two rows with height: 30 composed of two widgets with width 60 and 40
-/// const float size[] = {60,40};
-/// nk_layout_row(ctx, NK_STATIC, 30, 2, ratio);
-/// nk_widget(...);
-/// nk_widget(...);
-/// nk_widget(...);
-/// nk_widget(...);
-/// //
-/// // two rows with height: 30 composed of two widgets with window ratio 0.25 and 0.75
-/// const float ratio[] = {0.25, 0.75};
-/// nk_layout_row(ctx, NK_DYNAMIC, 30, 2, ratio);
-/// nk_widget(...);
-/// nk_widget(...);
-/// nk_widget(...);
-/// nk_widget(...);
-/// //
-/// // two rows with auto generated height composed of two widgets with window ratio 0.25 and 0.75
-/// const float ratio[] = {0.25, 0.75};
-/// nk_layout_row(ctx, NK_DYNAMIC, 30, 2, ratio);
-/// nk_widget(...);
-/// nk_widget(...);
-/// nk_widget(...);
-/// nk_widget(...);
-/// }
-/// nk_end(...);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// 5. __nk_layout_row_template_xxx__
-/// The most complex and second most flexible API is a simplified flexbox version without
-/// line wrapping and weights for dynamic widgets. It is an immediate mode API but
-/// unlike `nk_layout_row_xxx` it has auto repeat behavior and needs to be called
-/// before calling the templated widgets.
-/// The row template layout has three different per widget size specifier. The first
-/// one is the `nk_layout_row_template_push_static` with fixed widget pixel width.
-/// They do not grow if the row grows and will always stay the same.
-/// The second size specifier is `nk_layout_row_template_push_variable`
-/// which defines a minimum widget size but it also can grow if more space is available
-/// not taken by other widgets.
-/// Finally there are dynamic widgets with `nk_layout_row_template_push_dynamic`
-/// which are completely flexible and unlike variable widgets can even shrink
-/// to zero if not enough space is provided.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// if (nk_begin_xxx(...) {
-/// // two rows with height: 30 composed of three widgets
-/// nk_layout_row_template_begin(ctx, 30);
-/// nk_layout_row_template_push_dynamic(ctx);
-/// nk_layout_row_template_push_variable(ctx, 80);
-/// nk_layout_row_template_push_static(ctx, 80);
-/// nk_layout_row_template_end(ctx);
-/// //
-/// // first row
-/// nk_widget(...); // dynamic widget can go to zero if not enough space
-/// nk_widget(...); // variable widget with min 80 pixel but can grow bigger if enough space
-/// nk_widget(...); // static widget with fixed 80 pixel width
-/// //
-/// // second row same layout
-/// nk_widget(...);
-/// nk_widget(...);
-/// nk_widget(...);
-/// }
-/// nk_end(...);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// 6. __nk_layout_space_xxx__
-/// Finally the most flexible API directly allows you to place widgets inside the
-/// window. The space layout API is an immediate mode API which does not support
-/// row auto repeat and directly sets position and size of a widget. Position
-/// and size hereby can be either specified as ratio of allocated space or
-/// allocated space local position and pixel size. Since this API is quite
-/// powerful there are a number of utility functions to get the available space
-/// and convert between local allocated space and screen space.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// if (nk_begin_xxx(...) {
-/// // static row with height: 500 (you can set column count to INT_MAX if you don't want to be bothered)
-/// nk_layout_space_begin(ctx, NK_STATIC, 500, INT_MAX);
-/// nk_layout_space_push(ctx, nk_rect(0,0,150,200));
-/// nk_widget(...);
-/// nk_layout_space_push(ctx, nk_rect(200,200,100,200));
-/// nk_widget(...);
-/// nk_layout_space_end(ctx);
-/// //
-/// // dynamic row with height: 500 (you can set column count to INT_MAX if you don't want to be bothered)
-/// nk_layout_space_begin(ctx, NK_DYNAMIC, 500, INT_MAX);
-/// nk_layout_space_push(ctx, nk_rect(0.5,0.5,0.1,0.1));
-/// nk_widget(...);
-/// nk_layout_space_push(ctx, nk_rect(0.7,0.6,0.1,0.1));
-/// nk_widget(...);
-/// }
-/// nk_end(...);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// #### Reference
-/// Function | Description
-/// ----------------------------------------|------------------------------------
-/// nk_layout_set_min_row_height | Set the currently used minimum row height to a specified value
-/// nk_layout_reset_min_row_height | Resets the currently used minimum row height to font height
-/// nk_layout_widget_bounds | Calculates current width a static layout row can fit inside a window
-/// nk_layout_ratio_from_pixel | Utility functions to calculate window ratio from pixel size
-//
-/// nk_layout_row_dynamic | Current layout is divided into n same sized growing columns
-/// nk_layout_row_static | Current layout is divided into n same fixed sized columns
-/// nk_layout_row_begin | Starts a new row with given height and number of columns
-/// nk_layout_row_push | Pushes another column with given size or window ratio
-/// nk_layout_row_end | Finished previously started row
-/// nk_layout_row | Specifies row columns in array as either window ratio or size
-//
-/// nk_layout_row_template_begin | Begins the row template declaration
-/// nk_layout_row_template_push_dynamic | Adds a dynamic column that dynamically grows and can go to zero if not enough space
-/// nk_layout_row_template_push_variable | Adds a variable column that dynamically grows but does not shrink below specified pixel width
-/// nk_layout_row_template_push_static | Adds a static column that does not grow and will always have the same size
-/// nk_layout_row_template_end | Marks the end of the row template
-//
-/// nk_layout_space_begin | Begins a new layouting space that allows to specify each widgets position and size
-/// nk_layout_space_push | Pushes position and size of the next widget in own coordinate space either as pixel or ratio
-/// nk_layout_space_end | Marks the end of the layouting space
-//
-/// nk_layout_space_bounds | Callable after nk_layout_space_begin and returns total space allocated
-/// nk_layout_space_to_screen | Converts vector from nk_layout_space coordinate space into screen space
-/// nk_layout_space_to_local | Converts vector from screen space into nk_layout_space coordinates
-/// nk_layout_space_rect_to_screen | Converts rectangle from nk_layout_space coordinate space into screen space
-/// nk_layout_space_rect_to_local | Converts rectangle from screen space into nk_layout_space coordinates
-*/
-/*/// #### nk_layout_set_min_row_height
-/// Sets the currently used minimum row height.
-/// !!! WARNING
-/// The passed height needs to include both your preferred row height
-/// as well as padding. No internal padding is added.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_layout_set_min_row_height(struct nk_context*, float height);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
-/// __height__ | New minimum row height to be used for auto generating the row height
-*/
-NK_API void nk_layout_set_min_row_height(struct nk_context*, float height);
-/*/// #### nk_layout_reset_min_row_height
-/// Reset the currently used minimum row height back to `font_height + text_padding + padding`
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_layout_reset_min_row_height(struct nk_context*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
-*/
-NK_API void nk_layout_reset_min_row_height(struct nk_context*);
-/*/// #### nk_layout_widget_bounds
-/// Returns the width of the next row allocate by one of the layouting functions
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_rect nk_layout_widget_bounds(struct nk_context*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
-///
-/// Return `nk_rect` with both position and size of the next row
-*/
-NK_API struct nk_rect nk_layout_widget_bounds(struct nk_context*);
-/*/// #### nk_layout_ratio_from_pixel
-/// Utility functions to calculate window ratio from pixel size
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// float nk_layout_ratio_from_pixel(struct nk_context*, float pixel_width);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
-/// __pixel__ | Pixel_width to convert to window ratio
-///
-/// Returns `nk_rect` with both position and size of the next row
-*/
-NK_API float nk_layout_ratio_from_pixel(struct nk_context*, float pixel_width);
-/*/// #### nk_layout_row_dynamic
-/// Sets current row layout to share horizontal space
-/// between @cols number of widgets evenly. Once called all subsequent widget
-/// calls greater than @cols will allocate a new row with same layout.
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_layout_row_dynamic(struct nk_context *ctx, float height, int cols);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
-/// __height__ | Holds height of each widget in row or zero for auto layouting
-/// __columns__ | Number of widget inside row
-*/
-NK_API void nk_layout_row_dynamic(struct nk_context *ctx, float height, int cols);
-/*/// #### nk_layout_row_static
-/// Sets current row layout to fill @cols number of widgets
-/// in row with same @item_width horizontal size. Once called all subsequent widget
-/// calls greater than @cols will allocate a new row with same layout.
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_layout_row_static(struct nk_context *ctx, float height, int item_width, int cols);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
-/// __height__ | Holds height of each widget in row or zero for auto layouting
-/// __width__ | Holds pixel width of each widget in the row
-/// __columns__ | Number of widget inside row
-*/
-NK_API void nk_layout_row_static(struct nk_context *ctx, float height, int item_width, int cols);
-/*/// #### nk_layout_row_begin
-/// Starts a new dynamic or fixed row with given height and columns.
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_layout_row_begin(struct nk_context *ctx, enum nk_layout_format fmt, float row_height, int cols);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
-/// __fmt__ | either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns
-/// __height__ | holds height of each widget in row or zero for auto layouting
-/// __columns__ | Number of widget inside row
-*/
-NK_API void nk_layout_row_begin(struct nk_context *ctx, enum nk_layout_format fmt, float row_height, int cols);
-/*/// #### nk_layout_row_push
-/// Specifies either window ratio or width of a single column
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_layout_row_push(struct nk_context*, float value);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
-/// __value__ | either a window ratio or fixed width depending on @fmt in previous `nk_layout_row_begin` call
-*/
-NK_API void nk_layout_row_push(struct nk_context*, float value);
-/*/// #### nk_layout_row_end
-/// Finished previously started row
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_layout_row_end(struct nk_context*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
-*/
-NK_API void nk_layout_row_end(struct nk_context*);
-/*/// #### nk_layout_row
-/// Specifies row columns in array as either window ratio or size
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_layout_row(struct nk_context*, enum nk_layout_format, float height, int cols, const float *ratio);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
-/// __fmt__ | Either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns
-/// __height__ | Holds height of each widget in row or zero for auto layouting
-/// __columns__ | Number of widget inside row
-*/
-NK_API void nk_layout_row(struct nk_context*, enum nk_layout_format, float height, int cols, const float *ratio);
-/*/// #### nk_layout_row_template_begin
-/// Begins the row template declaration
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_layout_row_template_begin(struct nk_context*, float row_height);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
-/// __height__ | Holds height of each widget in row or zero for auto layouting
-*/
-NK_API void nk_layout_row_template_begin(struct nk_context*, float row_height);
-/*/// #### nk_layout_row_template_push_dynamic
-/// Adds a dynamic column that dynamically grows and can go to zero if not enough space
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_layout_row_template_push_dynamic(struct nk_context*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
-/// __height__ | Holds height of each widget in row or zero for auto layouting
-*/
-NK_API void nk_layout_row_template_push_dynamic(struct nk_context*);
-/*/// #### nk_layout_row_template_push_variable
-/// Adds a variable column that dynamically grows but does not shrink below specified pixel width
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_layout_row_template_push_variable(struct nk_context*, float min_width);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
-/// __width__ | Holds the minimum pixel width the next column must always be
-*/
-NK_API void nk_layout_row_template_push_variable(struct nk_context*, float min_width);
-/*/// #### nk_layout_row_template_push_static
-/// Adds a static column that does not grow and will always have the same size
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_layout_row_template_push_static(struct nk_context*, float width);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
-/// __width__ | Holds the absolute pixel width value the next column must be
-*/
-NK_API void nk_layout_row_template_push_static(struct nk_context*, float width);
-/*/// #### nk_layout_row_template_end
-/// Marks the end of the row template
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_layout_row_template_end(struct nk_context*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
-*/
-NK_API void nk_layout_row_template_end(struct nk_context*);
-/*/// #### nk_layout_space_begin
-/// Begins a new layouting space that allows to specify each widgets position and size.
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_layout_space_begin(struct nk_context*, enum nk_layout_format, float height, int widget_count);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
-/// __fmt__ | Either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns
-/// __height__ | Holds height of each widget in row or zero for auto layouting
-/// __columns__ | Number of widgets inside row
-*/
-NK_API void nk_layout_space_begin(struct nk_context*, enum nk_layout_format, float height, int widget_count);
-/*/// #### nk_layout_space_push
-/// Pushes position and size of the next widget in own coordinate space either as pixel or ratio
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_layout_space_push(struct nk_context *ctx, struct nk_rect bounds);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin`
-/// __bounds__ | Position and size in laoyut space local coordinates
-*/
-NK_API void nk_layout_space_push(struct nk_context*, struct nk_rect bounds);
-/*/// #### nk_layout_space_end
-/// Marks the end of the layout space
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_layout_space_end(struct nk_context*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin`
-*/
-NK_API void nk_layout_space_end(struct nk_context*);
-/*/// #### nk_layout_space_bounds
-/// Utility function to calculate total space allocated for `nk_layout_space`
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_rect nk_layout_space_bounds(struct nk_context*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin`
-///
-/// Returns `nk_rect` holding the total space allocated
-*/
-NK_API struct nk_rect nk_layout_space_bounds(struct nk_context*);
-/*/// #### nk_layout_space_to_screen
-/// Converts vector from nk_layout_space coordinate space into screen space
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_vec2 nk_layout_space_to_screen(struct nk_context*, struct nk_vec2);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin`
-/// __vec__ | Position to convert from layout space into screen coordinate space
-///
-/// Returns transformed `nk_vec2` in screen space coordinates
-*/
-NK_API struct nk_vec2 nk_layout_space_to_screen(struct nk_context*, struct nk_vec2);
-/*/// #### nk_layout_space_to_local
-/// Converts vector from layout space into screen space
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_vec2 nk_layout_space_to_local(struct nk_context*, struct nk_vec2);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin`
-/// __vec__ | Position to convert from screen space into layout coordinate space
-///
-/// Returns transformed `nk_vec2` in layout space coordinates
-*/
-NK_API struct nk_vec2 nk_layout_space_to_local(struct nk_context*, struct nk_vec2);
-/*/// #### nk_layout_space_rect_to_screen
-/// Converts rectangle from screen space into layout space
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_rect nk_layout_space_rect_to_screen(struct nk_context*, struct nk_rect);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin`
-/// __bounds__ | Rectangle to convert from layout space into screen space
-///
-/// Returns transformed `nk_rect` in screen space coordinates
-*/
-NK_API struct nk_rect nk_layout_space_rect_to_screen(struct nk_context*, struct nk_rect);
-/*/// #### nk_layout_space_rect_to_local
-/// Converts rectangle from layout space into screen space
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_rect nk_layout_space_rect_to_local(struct nk_context*, struct nk_rect);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin`
-/// __bounds__ | Rectangle to convert from layout space into screen space
-///
-/// Returns transformed `nk_rect` in layout space coordinates
-*/
-NK_API struct nk_rect nk_layout_space_rect_to_local(struct nk_context*, struct nk_rect);
-/* =============================================================================
- *
- * GROUP
- *
- * =============================================================================
-/// ### Groups
-/// Groups are basically windows inside windows. They allow to subdivide space
-/// in a window to layout widgets as a group. Almost all more complex widget
-/// layouting requirements can be solved using groups and basic layouting
-/// fuctionality. Groups just like windows are identified by an unique name and
-/// internally keep track of scrollbar offsets by default. However additional
-/// versions are provided to directly manage the scrollbar.
-///
-/// #### Usage
-/// To create a group you have to call one of the three `nk_group_begin_xxx`
-/// functions to start group declarations and `nk_group_end` at the end. Furthermore it
-/// is required to check the return value of `nk_group_begin_xxx` and only process
-/// widgets inside the window if the value is not 0.
-/// Nesting groups is possible and even encouraged since many layouting schemes
-/// can only be achieved by nesting. Groups, unlike windows, need `nk_group_end`
-/// to be only called if the corosponding `nk_group_begin_xxx` call does not return 0:
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// if (nk_group_begin_xxx(ctx, ...) {
-/// // [... widgets ...]
-/// nk_group_end(ctx);
-/// }
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// In the grand concept groups can be called after starting a window
-/// with `nk_begin_xxx` and before calling `nk_end`:
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// struct nk_context ctx;
-/// nk_init_xxx(&ctx, ...);
-/// while (1) {
-/// // Input
-/// Event evt;
-/// nk_input_begin(&ctx);
-/// while (GetEvent(&evt)) {
-/// if (evt.type == MOUSE_MOVE)
-/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y);
-/// else if (evt.type == [...]) {
-/// nk_input_xxx(...);
-/// }
-/// }
-/// nk_input_end(&ctx);
-/// //
-/// // Window
-/// if (nk_begin_xxx(...) {
-/// // [...widgets...]
-/// nk_layout_row_dynamic(...);
-/// if (nk_group_begin_xxx(ctx, ...) {
-/// //[... widgets ...]
-/// nk_group_end(ctx);
-/// }
-/// }
-/// nk_end(ctx);
-/// //
-/// // Draw
-/// const struct nk_command *cmd = 0;
-/// nk_foreach(cmd, &ctx) {
-/// switch (cmd->type) {
-/// case NK_COMMAND_LINE:
-/// your_draw_line_function(...)
-/// break;
-/// case NK_COMMAND_RECT
-/// your_draw_rect_function(...)
-/// break;
-/// case ...:
-/// // [...]
-/// }
-/// nk_clear(&ctx);
-/// }
-/// nk_free(&ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-/// #### Reference
-/// Function | Description
-/// --------------------------------|-------------------------------------------
-/// nk_group_begin | Start a new group with internal scrollbar handling
-/// nk_group_begin_titled | Start a new group with separeted name and title and internal scrollbar handling
-/// nk_group_end | Ends a group. Should only be called if nk_group_begin returned non-zero
-/// nk_group_scrolled_offset_begin | Start a new group with manual separated handling of scrollbar x- and y-offset
-/// nk_group_scrolled_begin | Start a new group with manual scrollbar handling
-/// nk_group_scrolled_end | Ends a group with manual scrollbar handling. Should only be called if nk_group_begin returned non-zero
-/// nk_group_get_scroll | Gets the scroll offset for the given group
-/// nk_group_set_scroll | Sets the scroll offset for the given group
-*/
-/*/// #### nk_group_begin
-/// Starts a new widget group. Requires a previous layouting function to specify a pos/size.
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_group_begin(struct nk_context*, const char *title, nk_flags);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __title__ | Must be an unique identifier for this group that is also used for the group header
-/// __flags__ | Window flags defined in the nk_panel_flags section with a number of different group behaviors
-///
-/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise
-*/
-NK_API nk_bool nk_group_begin(struct nk_context*, const char *title, nk_flags);
-/*/// #### nk_group_begin_titled
-/// Starts a new widget group. Requires a previous layouting function to specify a pos/size.
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_group_begin_titled(struct nk_context*, const char *name, const char *title, nk_flags);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __id__ | Must be an unique identifier for this group
-/// __title__ | Group header title
-/// __flags__ | Window flags defined in the nk_panel_flags section with a number of different group behaviors
-///
-/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise
-*/
-NK_API nk_bool nk_group_begin_titled(struct nk_context*, const char *name, const char *title, nk_flags);
-/*/// #### nk_group_end
-/// Ends a widget group
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_group_end(struct nk_context*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-*/
-NK_API void nk_group_end(struct nk_context*);
-/*/// #### nk_group_scrolled_offset_begin
-/// starts a new widget group. requires a previous layouting function to specify
-/// a size. Does not keep track of scrollbar.
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_group_scrolled_offset_begin(struct nk_context*, nk_uint *x_offset, nk_uint *y_offset, const char *title, nk_flags flags);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __x_offset__| Scrollbar x-offset to offset all widgets inside the group horizontally.
-/// __y_offset__| Scrollbar y-offset to offset all widgets inside the group vertically
-/// __title__ | Window unique group title used to both identify and display in the group header
-/// __flags__ | Window flags from the nk_panel_flags section
-///
-/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise
-*/
-NK_API nk_bool nk_group_scrolled_offset_begin(struct nk_context*, nk_uint *x_offset, nk_uint *y_offset, const char *title, nk_flags flags);
-/*/// #### nk_group_scrolled_begin
-/// Starts a new widget group. requires a previous
-/// layouting function to specify a size. Does not keep track of scrollbar.
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_group_scrolled_begin(struct nk_context*, struct nk_scroll *off, const char *title, nk_flags);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __off__ | Both x- and y- scroll offset. Allows for manual scrollbar control
-/// __title__ | Window unique group title used to both identify and display in the group header
-/// __flags__ | Window flags from nk_panel_flags section
-///
-/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise
-*/
-NK_API nk_bool nk_group_scrolled_begin(struct nk_context*, struct nk_scroll *off, const char *title, nk_flags);
-/*/// #### nk_group_scrolled_end
-/// Ends a widget group after calling nk_group_scrolled_offset_begin or nk_group_scrolled_begin.
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_group_scrolled_end(struct nk_context*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-*/
-NK_API void nk_group_scrolled_end(struct nk_context*);
-/*/// #### nk_group_get_scroll
-/// Gets the scroll position of the given group.
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_group_get_scroll(struct nk_context*, const char *id, nk_uint *x_offset, nk_uint *y_offset);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// -------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __id__ | The id of the group to get the scroll position of
-/// __x_offset__ | A pointer to the x offset output (or NULL to ignore)
-/// __y_offset__ | A pointer to the y offset output (or NULL to ignore)
-*/
-NK_API void nk_group_get_scroll(struct nk_context*, const char *id, nk_uint *x_offset, nk_uint *y_offset);
-/*/// #### nk_group_set_scroll
-/// Sets the scroll position of the given group.
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_group_set_scroll(struct nk_context*, const char *id, nk_uint x_offset, nk_uint y_offset);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// -------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __id__ | The id of the group to scroll
-/// __x_offset__ | The x offset to scroll to
-/// __y_offset__ | The y offset to scroll to
-*/
-NK_API void nk_group_set_scroll(struct nk_context*, const char *id, nk_uint x_offset, nk_uint y_offset);
-/* =============================================================================
- *
- * TREE
- *
- * =============================================================================
-/// ### Tree
-/// Trees represent two different concept. First the concept of a collapsable
-/// UI section that can be either in a hidden or visibile state. They allow the UI
-/// user to selectively minimize the current set of visible UI to comprehend.
-/// The second concept are tree widgets for visual UI representation of trees.
-///
-/// Trees thereby can be nested for tree representations and multiple nested
-/// collapsable UI sections. All trees are started by calling of the
-/// `nk_tree_xxx_push_tree` functions and ended by calling one of the
-/// `nk_tree_xxx_pop_xxx()` functions. Each starting functions takes a title label
-/// and optionally an image to be displayed and the initial collapse state from
-/// the nk_collapse_states section.
-///
-/// The runtime state of the tree is either stored outside the library by the caller
-/// or inside which requires a unique ID. The unique ID can either be generated
-/// automatically from `__FILE__` and `__LINE__` with function `nk_tree_push`,
-/// by `__FILE__` and a user provided ID generated for example by loop index with
-/// function `nk_tree_push_id` or completely provided from outside by user with
-/// function `nk_tree_push_hashed`.
-///
-/// #### Usage
-/// To create a tree you have to call one of the seven `nk_tree_xxx_push_xxx`
-/// functions to start a collapsable UI section and `nk_tree_xxx_pop` to mark the
-/// end.
-/// Each starting function will either return `false(0)` if the tree is collapsed
-/// or hidden and therefore does not need to be filled with content or `true(1)`
-/// if visible and required to be filled.
-///
-/// !!! Note
-/// The tree header does not require and layouting function and instead
-/// calculates a auto height based on the currently used font size
-///
-/// The tree ending functions only need to be called if the tree content is
-/// actually visible. So make sure the tree push function is guarded by `if`
-/// and the pop call is only taken if the tree is visible.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// if (nk_tree_push(ctx, NK_TREE_TAB, "Tree", NK_MINIMIZED)) {
-/// nk_layout_row_dynamic(...);
-/// nk_widget(...);
-/// nk_tree_pop(ctx);
-/// }
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// #### Reference
-/// Function | Description
-/// ----------------------------|-------------------------------------------
-/// nk_tree_push | Start a collapsable UI section with internal state management
-/// nk_tree_push_id | Start a collapsable UI section with internal state management callable in a look
-/// nk_tree_push_hashed | Start a collapsable UI section with internal state management with full control over internal unique ID use to store state
-/// nk_tree_image_push | Start a collapsable UI section with image and label header
-/// nk_tree_image_push_id | Start a collapsable UI section with image and label header and internal state management callable in a look
-/// nk_tree_image_push_hashed | Start a collapsable UI section with image and label header and internal state management with full control over internal unique ID use to store state
-/// nk_tree_pop | Ends a collapsable UI section
-//
-/// nk_tree_state_push | Start a collapsable UI section with external state management
-/// nk_tree_state_image_push | Start a collapsable UI section with image and label header and external state management
-/// nk_tree_state_pop | Ends a collapsabale UI section
-///
-/// #### nk_tree_type
-/// Flag | Description
-/// ----------------|----------------------------------------
-/// NK_TREE_NODE | Highlighted tree header to mark a collapsable UI section
-/// NK_TREE_TAB | Non-highighted tree header closer to tree representations
-*/
-/*/// #### nk_tree_push
-/// Starts a collapsable UI section with internal state management
-/// !!! WARNING
-/// To keep track of the runtime tree collapsable state this function uses
-/// defines `__FILE__` and `__LINE__` to generate a unique ID. If you want
-/// to call this function in a loop please use `nk_tree_push_id` or
-/// `nk_tree_push_hashed` instead.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// #define nk_tree_push(ctx, type, title, state)
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node
-/// __title__ | Label printed in the tree header
-/// __state__ | Initial tree state value out of nk_collapse_states
-///
-/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise
-*/
-#define nk_tree_push(ctx, type, title, state) nk_tree_push_hashed(ctx, type, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__)
-/*/// #### nk_tree_push_id
-/// Starts a collapsable UI section with internal state management callable in a look
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// #define nk_tree_push_id(ctx, type, title, state, id)
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node
-/// __title__ | Label printed in the tree header
-/// __state__ | Initial tree state value out of nk_collapse_states
-/// __id__ | Loop counter index if this function is called in a loop
-///
-/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise
-*/
-#define nk_tree_push_id(ctx, type, title, state, id) nk_tree_push_hashed(ctx, type, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id)
-/*/// #### nk_tree_push_hashed
-/// Start a collapsable UI section with internal state management with full
-/// control over internal unique ID used to store state
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_tree_push_hashed(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node
-/// __title__ | Label printed in the tree header
-/// __state__ | Initial tree state value out of nk_collapse_states
-/// __hash__ | Memory block or string to generate the ID from
-/// __len__ | Size of passed memory block or string in __hash__
-/// __seed__ | Seeding value if this function is called in a loop or default to `0`
-///
-/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise
-*/
-NK_API nk_bool nk_tree_push_hashed(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed);
-/*/// #### nk_tree_image_push
-/// Start a collapsable UI section with image and label header
-/// !!! WARNING
-/// To keep track of the runtime tree collapsable state this function uses
-/// defines `__FILE__` and `__LINE__` to generate a unique ID. If you want
-/// to call this function in a loop please use `nk_tree_image_push_id` or
-/// `nk_tree_image_push_hashed` instead.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// #define nk_tree_image_push(ctx, type, img, title, state)
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node
-/// __img__ | Image to display inside the header on the left of the label
-/// __title__ | Label printed in the tree header
-/// __state__ | Initial tree state value out of nk_collapse_states
-///
-/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise
-*/
-#define nk_tree_image_push(ctx, type, img, title, state) nk_tree_image_push_hashed(ctx, type, img, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__)
-/*/// #### nk_tree_image_push_id
-/// Start a collapsable UI section with image and label header and internal state
-/// management callable in a look
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// #define nk_tree_image_push_id(ctx, type, img, title, state, id)
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node
-/// __img__ | Image to display inside the header on the left of the label
-/// __title__ | Label printed in the tree header
-/// __state__ | Initial tree state value out of nk_collapse_states
-/// __id__ | Loop counter index if this function is called in a loop
-///
-/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise
-*/
-#define nk_tree_image_push_id(ctx, type, img, title, state, id) nk_tree_image_push_hashed(ctx, type, img, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id)
-/*/// #### nk_tree_image_push_hashed
-/// Start a collapsable UI section with internal state management with full
-/// control over internal unique ID used to store state
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_tree_image_push_hashed(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct
-/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node
-/// __img__ | Image to display inside the header on the left of the label
-/// __title__ | Label printed in the tree header
-/// __state__ | Initial tree state value out of nk_collapse_states
-/// __hash__ | Memory block or string to generate the ID from
-/// __len__ | Size of passed memory block or string in __hash__
-/// __seed__ | Seeding value if this function is called in a loop or default to `0`
-///
-/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise
-*/
-NK_API nk_bool nk_tree_image_push_hashed(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed);
-/*/// #### nk_tree_pop
-/// Ends a collapsabale UI section
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_tree_pop(struct nk_context*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling `nk_tree_xxx_push_xxx`
-*/
-NK_API void nk_tree_pop(struct nk_context*);
-/*/// #### nk_tree_state_push
-/// Start a collapsable UI section with external state management
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_tree_state_push(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states *state);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling `nk_tree_xxx_push_xxx`
-/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node
-/// __title__ | Label printed in the tree header
-/// __state__ | Persistent state to update
-///
-/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise
-*/
-NK_API nk_bool nk_tree_state_push(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states *state);
-/*/// #### nk_tree_state_image_push
-/// Start a collapsable UI section with image and label header and external state management
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_tree_state_image_push(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states *state);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling `nk_tree_xxx_push_xxx`
-/// __img__ | Image to display inside the header on the left of the label
-/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node
-/// __title__ | Label printed in the tree header
-/// __state__ | Persistent state to update
-///
-/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise
-*/
-NK_API nk_bool nk_tree_state_image_push(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states *state);
-/*/// #### nk_tree_state_pop
-/// Ends a collapsabale UI section
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_tree_state_pop(struct nk_context*);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// ------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling `nk_tree_xxx_push_xxx`
-*/
-NK_API void nk_tree_state_pop(struct nk_context*);
-
-#define nk_tree_element_push(ctx, type, title, state, sel) nk_tree_element_push_hashed(ctx, type, title, state, sel, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__)
-#define nk_tree_element_push_id(ctx, type, title, state, sel, id) nk_tree_element_push_hashed(ctx, type, title, state, sel, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id)
-NK_API nk_bool nk_tree_element_push_hashed(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states initial_state, nk_bool *selected, const char *hash, int len, int seed);
-NK_API nk_bool nk_tree_element_image_push_hashed(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states initial_state, nk_bool *selected, const char *hash, int len,int seed);
-NK_API void nk_tree_element_pop(struct nk_context*);
-
-/* =============================================================================
- *
- * LIST VIEW
- *
- * ============================================================================= */
-struct nk_list_view {
-/* public: */
- int begin, end, count;
-/* private: */
- int total_height;
- struct nk_context *ctx;
- nk_uint *scroll_pointer;
- nk_uint scroll_value;
-};
-NK_API nk_bool nk_list_view_begin(struct nk_context*, struct nk_list_view *out, const char *id, nk_flags, int row_height, int row_count);
-NK_API void nk_list_view_end(struct nk_list_view*);
-/* =============================================================================
- *
- * WIDGET
- *
- * ============================================================================= */
-enum nk_widget_layout_states {
- NK_WIDGET_INVALID, /* The widget cannot be seen and is completely out of view */
- NK_WIDGET_VALID, /* The widget is completely inside the window and can be updated and drawn */
- NK_WIDGET_ROM /* The widget is partially visible and cannot be updated */
-};
-enum nk_widget_states {
- NK_WIDGET_STATE_MODIFIED = NK_FLAG(1),
- NK_WIDGET_STATE_INACTIVE = NK_FLAG(2), /* widget is neither active nor hovered */
- NK_WIDGET_STATE_ENTERED = NK_FLAG(3), /* widget has been hovered on the current frame */
- NK_WIDGET_STATE_HOVER = NK_FLAG(4), /* widget is being hovered */
- NK_WIDGET_STATE_ACTIVED = NK_FLAG(5),/* widget is currently activated */
- NK_WIDGET_STATE_LEFT = NK_FLAG(6), /* widget is from this frame on not hovered anymore */
- NK_WIDGET_STATE_HOVERED = NK_WIDGET_STATE_HOVER|NK_WIDGET_STATE_MODIFIED, /* widget is being hovered */
- NK_WIDGET_STATE_ACTIVE = NK_WIDGET_STATE_ACTIVED|NK_WIDGET_STATE_MODIFIED /* widget is currently activated */
-};
-NK_API enum nk_widget_layout_states nk_widget(struct nk_rect*, const struct nk_context*);
-NK_API enum nk_widget_layout_states nk_widget_fitting(struct nk_rect*, struct nk_context*, struct nk_vec2);
-NK_API struct nk_rect nk_widget_bounds(struct nk_context*);
-NK_API struct nk_vec2 nk_widget_position(struct nk_context*);
-NK_API struct nk_vec2 nk_widget_size(struct nk_context*);
-NK_API float nk_widget_width(struct nk_context*);
-NK_API float nk_widget_height(struct nk_context*);
-NK_API nk_bool nk_widget_is_hovered(struct nk_context*);
-NK_API nk_bool nk_widget_is_mouse_clicked(struct nk_context*, enum nk_buttons);
-NK_API nk_bool nk_widget_has_mouse_click_down(struct nk_context*, enum nk_buttons, nk_bool down);
-NK_API void nk_spacing(struct nk_context*, int cols);
-/* =============================================================================
- *
- * TEXT
- *
- * ============================================================================= */
-enum nk_text_align {
- NK_TEXT_ALIGN_LEFT = 0x01,
- NK_TEXT_ALIGN_CENTERED = 0x02,
- NK_TEXT_ALIGN_RIGHT = 0x04,
- NK_TEXT_ALIGN_TOP = 0x08,
- NK_TEXT_ALIGN_MIDDLE = 0x10,
- NK_TEXT_ALIGN_BOTTOM = 0x20
-};
-enum nk_text_alignment {
- NK_TEXT_LEFT = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_LEFT,
- NK_TEXT_CENTERED = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_CENTERED,
- NK_TEXT_RIGHT = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_RIGHT
-};
-NK_API void nk_text(struct nk_context*, const char*, int, nk_flags);
-NK_API void nk_text_colored(struct nk_context*, const char*, int, nk_flags, struct nk_color);
-NK_API void nk_text_wrap(struct nk_context*, const char*, int);
-NK_API void nk_text_wrap_colored(struct nk_context*, const char*, int, struct nk_color);
-NK_API void nk_label(struct nk_context*, const char*, nk_flags align);
-NK_API void nk_label_colored(struct nk_context*, const char*, nk_flags align, struct nk_color);
-NK_API void nk_label_wrap(struct nk_context*, const char*);
-NK_API void nk_label_colored_wrap(struct nk_context*, const char*, struct nk_color);
-NK_API void nk_image(struct nk_context*, struct nk_image);
-NK_API void nk_image_color(struct nk_context*, struct nk_image, struct nk_color);
-#ifdef NK_INCLUDE_STANDARD_VARARGS
-NK_API void nk_labelf(struct nk_context*, nk_flags, NK_PRINTF_FORMAT_STRING const char*, ...) NK_PRINTF_VARARG_FUNC(3);
-NK_API void nk_labelf_colored(struct nk_context*, nk_flags, struct nk_color, NK_PRINTF_FORMAT_STRING const char*,...) NK_PRINTF_VARARG_FUNC(4);
-NK_API void nk_labelf_wrap(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*,...) NK_PRINTF_VARARG_FUNC(2);
-NK_API void nk_labelf_colored_wrap(struct nk_context*, struct nk_color, NK_PRINTF_FORMAT_STRING const char*,...) NK_PRINTF_VARARG_FUNC(3);
-NK_API void nk_labelfv(struct nk_context*, nk_flags, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(3);
-NK_API void nk_labelfv_colored(struct nk_context*, nk_flags, struct nk_color, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(4);
-NK_API void nk_labelfv_wrap(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(2);
-NK_API void nk_labelfv_colored_wrap(struct nk_context*, struct nk_color, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(3);
-NK_API void nk_value_bool(struct nk_context*, const char *prefix, int);
-NK_API void nk_value_int(struct nk_context*, const char *prefix, int);
-NK_API void nk_value_uint(struct nk_context*, const char *prefix, unsigned int);
-NK_API void nk_value_float(struct nk_context*, const char *prefix, float);
-NK_API void nk_value_color_byte(struct nk_context*, const char *prefix, struct nk_color);
-NK_API void nk_value_color_float(struct nk_context*, const char *prefix, struct nk_color);
-NK_API void nk_value_color_hex(struct nk_context*, const char *prefix, struct nk_color);
-#endif
-/* =============================================================================
- *
- * BUTTON
- *
- * ============================================================================= */
-NK_API nk_bool nk_button_text(struct nk_context*, const char *title, int len);
-NK_API nk_bool nk_button_label(struct nk_context*, const char *title);
-NK_API nk_bool nk_button_color(struct nk_context*, struct nk_color);
-NK_API nk_bool nk_button_symbol(struct nk_context*, enum nk_symbol_type);
-NK_API nk_bool nk_button_image(struct nk_context*, struct nk_image img);
-NK_API nk_bool nk_button_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags text_alignment);
-NK_API nk_bool nk_button_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment);
-NK_API nk_bool nk_button_image_label(struct nk_context*, struct nk_image img, const char*, nk_flags text_alignment);
-NK_API nk_bool nk_button_image_text(struct nk_context*, struct nk_image img, const char*, int, nk_flags alignment);
-NK_API nk_bool nk_button_text_styled(struct nk_context*, const struct nk_style_button*, const char *title, int len);
-NK_API nk_bool nk_button_label_styled(struct nk_context*, const struct nk_style_button*, const char *title);
-NK_API nk_bool nk_button_symbol_styled(struct nk_context*, const struct nk_style_button*, enum nk_symbol_type);
-NK_API nk_bool nk_button_image_styled(struct nk_context*, const struct nk_style_button*, struct nk_image img);
-NK_API nk_bool nk_button_symbol_text_styled(struct nk_context*,const struct nk_style_button*, enum nk_symbol_type, const char*, int, nk_flags alignment);
-NK_API nk_bool nk_button_symbol_label_styled(struct nk_context *ctx, const struct nk_style_button *style, enum nk_symbol_type symbol, const char *title, nk_flags align);
-NK_API nk_bool nk_button_image_label_styled(struct nk_context*,const struct nk_style_button*, struct nk_image img, const char*, nk_flags text_alignment);
-NK_API nk_bool nk_button_image_text_styled(struct nk_context*,const struct nk_style_button*, struct nk_image img, const char*, int, nk_flags alignment);
-NK_API void nk_button_set_behavior(struct nk_context*, enum nk_button_behavior);
-NK_API nk_bool nk_button_push_behavior(struct nk_context*, enum nk_button_behavior);
-NK_API nk_bool nk_button_pop_behavior(struct nk_context*);
-/* =============================================================================
- *
- * CHECKBOX
- *
- * ============================================================================= */
-NK_API nk_bool nk_check_label(struct nk_context*, const char*, nk_bool active);
-NK_API nk_bool nk_check_text(struct nk_context*, const char*, int, nk_bool active);
-NK_API unsigned nk_check_flags_label(struct nk_context*, const char*, unsigned int flags, unsigned int value);
-NK_API unsigned nk_check_flags_text(struct nk_context*, const char*, int, unsigned int flags, unsigned int value);
-NK_API nk_bool nk_checkbox_label(struct nk_context*, const char*, nk_bool *active);
-NK_API nk_bool nk_checkbox_text(struct nk_context*, const char*, int, nk_bool *active);
-NK_API nk_bool nk_checkbox_flags_label(struct nk_context*, const char*, unsigned int *flags, unsigned int value);
-NK_API nk_bool nk_checkbox_flags_text(struct nk_context*, const char*, int, unsigned int *flags, unsigned int value);
-/* =============================================================================
- *
- * RADIO BUTTON
- *
- * ============================================================================= */
-NK_API nk_bool nk_radio_label(struct nk_context*, const char*, nk_bool *active);
-NK_API nk_bool nk_radio_text(struct nk_context*, const char*, int, nk_bool *active);
-NK_API nk_bool nk_option_label(struct nk_context*, const char*, nk_bool active);
-NK_API nk_bool nk_option_text(struct nk_context*, const char*, int, nk_bool active);
-/* =============================================================================
- *
- * SELECTABLE
- *
- * ============================================================================= */
-NK_API nk_bool nk_selectable_label(struct nk_context*, const char*, nk_flags align, nk_bool *value);
-NK_API nk_bool nk_selectable_text(struct nk_context*, const char*, int, nk_flags align, nk_bool *value);
-NK_API nk_bool nk_selectable_image_label(struct nk_context*,struct nk_image, const char*, nk_flags align, nk_bool *value);
-NK_API nk_bool nk_selectable_image_text(struct nk_context*,struct nk_image, const char*, int, nk_flags align, nk_bool *value);
-NK_API nk_bool nk_selectable_symbol_label(struct nk_context*,enum nk_symbol_type, const char*, nk_flags align, nk_bool *value);
-NK_API nk_bool nk_selectable_symbol_text(struct nk_context*,enum nk_symbol_type, const char*, int, nk_flags align, nk_bool *value);
-
-NK_API nk_bool nk_select_label(struct nk_context*, const char*, nk_flags align, nk_bool value);
-NK_API nk_bool nk_select_text(struct nk_context*, const char*, int, nk_flags align, nk_bool value);
-NK_API nk_bool nk_select_image_label(struct nk_context*, struct nk_image,const char*, nk_flags align, nk_bool value);
-NK_API nk_bool nk_select_image_text(struct nk_context*, struct nk_image,const char*, int, nk_flags align, nk_bool value);
-NK_API nk_bool nk_select_symbol_label(struct nk_context*,enum nk_symbol_type, const char*, nk_flags align, nk_bool value);
-NK_API nk_bool nk_select_symbol_text(struct nk_context*,enum nk_symbol_type, const char*, int, nk_flags align, nk_bool value);
-
-/* =============================================================================
- *
- * SLIDER
- *
- * ============================================================================= */
-NK_API float nk_slide_float(struct nk_context*, float min, float val, float max, float step);
-NK_API int nk_slide_int(struct nk_context*, int min, int val, int max, int step);
-NK_API nk_bool nk_slider_float(struct nk_context*, float min, float *val, float max, float step);
-NK_API nk_bool nk_slider_int(struct nk_context*, int min, int *val, int max, int step);
-/* =============================================================================
- *
- * PROGRESSBAR
- *
- * ============================================================================= */
-NK_API nk_bool nk_progress(struct nk_context*, nk_size *cur, nk_size max, nk_bool modifyable);
-NK_API nk_size nk_prog(struct nk_context*, nk_size cur, nk_size max, nk_bool modifyable);
-
-/* =============================================================================
- *
- * COLOR PICKER
- *
- * ============================================================================= */
-NK_API struct nk_colorf nk_color_picker(struct nk_context*, struct nk_colorf, enum nk_color_format);
-NK_API nk_bool nk_color_pick(struct nk_context*, struct nk_colorf*, enum nk_color_format);
-/* =============================================================================
- *
- * PROPERTIES
- *
- * =============================================================================
-/// ### Properties
-/// Properties are the main value modification widgets in Nuklear. Changing a value
-/// can be achieved by dragging, adding/removing incremental steps on button click
-/// or by directly typing a number.
-///
-/// #### Usage
-/// Each property requires a unique name for identifaction that is also used for
-/// displaying a label. If you want to use the same name multiple times make sure
-/// add a '#' before your name. The '#' will not be shown but will generate a
-/// unique ID. Each propery also takes in a minimum and maximum value. If you want
-/// to make use of the complete number range of a type just use the provided
-/// type limits from `limits.h`. For example `INT_MIN` and `INT_MAX` for
-/// `nk_property_int` and `nk_propertyi`. In additional each property takes in
-/// a increment value that will be added or subtracted if either the increment
-/// decrement button is clicked. Finally there is a value for increment per pixel
-/// dragged that is added or subtracted from the value.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int value = 0;
-/// struct nk_context ctx;
-/// nk_init_xxx(&ctx, ...);
-/// while (1) {
-/// // Input
-/// Event evt;
-/// nk_input_begin(&ctx);
-/// while (GetEvent(&evt)) {
-/// if (evt.type == MOUSE_MOVE)
-/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y);
-/// else if (evt.type == [...]) {
-/// nk_input_xxx(...);
-/// }
-/// }
-/// nk_input_end(&ctx);
-/// //
-/// // Window
-/// if (nk_begin_xxx(...) {
-/// // Property
-/// nk_layout_row_dynamic(...);
-/// nk_property_int(ctx, "ID", INT_MIN, &value, INT_MAX, 1, 1);
-/// }
-/// nk_end(ctx);
-/// //
-/// // Draw
-/// const struct nk_command *cmd = 0;
-/// nk_foreach(cmd, &ctx) {
-/// switch (cmd->type) {
-/// case NK_COMMAND_LINE:
-/// your_draw_line_function(...)
-/// break;
-/// case NK_COMMAND_RECT
-/// your_draw_rect_function(...)
-/// break;
-/// case ...:
-/// // [...]
-/// }
-/// nk_clear(&ctx);
-/// }
-/// nk_free(&ctx);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// #### Reference
-/// Function | Description
-/// --------------------|-------------------------------------------
-/// nk_property_int | Integer property directly modifing a passed in value
-/// nk_property_float | Float property directly modifing a passed in value
-/// nk_property_double | Double property directly modifing a passed in value
-/// nk_propertyi | Integer property returning the modified int value
-/// nk_propertyf | Float property returning the modified float value
-/// nk_propertyd | Double property returning the modified double value
-///
-*/
-/*/// #### nk_property_int
-/// Integer property directly modifing a passed in value
-/// !!! WARNING
-/// To generate a unique property ID using the same label make sure to insert
-/// a `#` at the beginning. It will not be shown but guarantees correct behavior.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_property_int(struct nk_context *ctx, const char *name, int min, int *val, int max, int step, float inc_per_pixel);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// --------------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function
-/// __name__ | String used both as a label as well as a unique identifier
-/// __min__ | Minimum value not allowed to be underflown
-/// __val__ | Integer pointer to be modified
-/// __max__ | Maximum value not allowed to be overflown
-/// __step__ | Increment added and subtracted on increment and decrement button
-/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging
-*/
-NK_API void nk_property_int(struct nk_context*, const char *name, int min, int *val, int max, int step, float inc_per_pixel);
-/*/// #### nk_property_float
-/// Float property directly modifing a passed in value
-/// !!! WARNING
-/// To generate a unique property ID using the same label make sure to insert
-/// a `#` at the beginning. It will not be shown but guarantees correct behavior.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_property_float(struct nk_context *ctx, const char *name, float min, float *val, float max, float step, float inc_per_pixel);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// --------------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function
-/// __name__ | String used both as a label as well as a unique identifier
-/// __min__ | Minimum value not allowed to be underflown
-/// __val__ | Float pointer to be modified
-/// __max__ | Maximum value not allowed to be overflown
-/// __step__ | Increment added and subtracted on increment and decrement button
-/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging
-*/
-NK_API void nk_property_float(struct nk_context*, const char *name, float min, float *val, float max, float step, float inc_per_pixel);
-/*/// #### nk_property_double
-/// Double property directly modifing a passed in value
-/// !!! WARNING
-/// To generate a unique property ID using the same label make sure to insert
-/// a `#` at the beginning. It will not be shown but guarantees correct behavior.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// void nk_property_double(struct nk_context *ctx, const char *name, double min, double *val, double max, double step, double inc_per_pixel);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// --------------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function
-/// __name__ | String used both as a label as well as a unique identifier
-/// __min__ | Minimum value not allowed to be underflown
-/// __val__ | Double pointer to be modified
-/// __max__ | Maximum value not allowed to be overflown
-/// __step__ | Increment added and subtracted on increment and decrement button
-/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging
-*/
-NK_API void nk_property_double(struct nk_context*, const char *name, double min, double *val, double max, double step, float inc_per_pixel);
-/*/// #### nk_propertyi
-/// Integer property modifing a passed in value and returning the new value
-/// !!! WARNING
-/// To generate a unique property ID using the same label make sure to insert
-/// a `#` at the beginning. It will not be shown but guarantees correct behavior.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// int nk_propertyi(struct nk_context *ctx, const char *name, int min, int val, int max, int step, float inc_per_pixel);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// --------------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function
-/// __name__ | String used both as a label as well as a unique identifier
-/// __min__ | Minimum value not allowed to be underflown
-/// __val__ | Current integer value to be modified and returned
-/// __max__ | Maximum value not allowed to be overflown
-/// __step__ | Increment added and subtracted on increment and decrement button
-/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging
-///
-/// Returns the new modified integer value
-*/
-NK_API nk_bool nk_propertyi(struct nk_context*, const char *name, int min, int val, int max, int step, float inc_per_pixel);
-/*/// #### nk_propertyf
-/// Float property modifing a passed in value and returning the new value
-/// !!! WARNING
-/// To generate a unique property ID using the same label make sure to insert
-/// a `#` at the beginning. It will not be shown but guarantees correct behavior.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// float nk_propertyf(struct nk_context *ctx, const char *name, float min, float val, float max, float step, float inc_per_pixel);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// --------------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function
-/// __name__ | String used both as a label as well as a unique identifier
-/// __min__ | Minimum value not allowed to be underflown
-/// __val__ | Current float value to be modified and returned
-/// __max__ | Maximum value not allowed to be overflown
-/// __step__ | Increment added and subtracted on increment and decrement button
-/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging
-///
-/// Returns the new modified float value
-*/
-NK_API float nk_propertyf(struct nk_context*, const char *name, float min, float val, float max, float step, float inc_per_pixel);
-/*/// #### nk_propertyd
-/// Float property modifing a passed in value and returning the new value
-/// !!! WARNING
-/// To generate a unique property ID using the same label make sure to insert
-/// a `#` at the beginning. It will not be shown but guarantees correct behavior.
-///
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c
-/// float nk_propertyd(struct nk_context *ctx, const char *name, double min, double val, double max, double step, double inc_per_pixel);
-/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-///
-/// Parameter | Description
-/// --------------------|-----------------------------------------------------------
-/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function
-/// __name__ | String used both as a label as well as a unique identifier
-/// __min__ | Minimum value not allowed to be underflown
-/// __val__ | Current double value to be modified and returned
-/// __max__ | Maximum value not allowed to be overflown
-/// __step__ | Increment added and subtracted on increment and decrement button
-/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging
-///
-/// Returns the new modified double value
-*/
-NK_API double nk_propertyd(struct nk_context*, const char *name, double min, double val, double max, double step, float inc_per_pixel);
-/* =============================================================================
- *
- * TEXT EDIT
- *
- * ============================================================================= */
-enum nk_edit_flags {
- NK_EDIT_DEFAULT = 0,
- NK_EDIT_READ_ONLY = NK_FLAG(0),
- NK_EDIT_AUTO_SELECT = NK_FLAG(1),
- NK_EDIT_SIG_ENTER = NK_FLAG(2),
- NK_EDIT_ALLOW_TAB = NK_FLAG(3),
- NK_EDIT_NO_CURSOR = NK_FLAG(4),
- NK_EDIT_SELECTABLE = NK_FLAG(5),
- NK_EDIT_CLIPBOARD = NK_FLAG(6),
- NK_EDIT_CTRL_ENTER_NEWLINE = NK_FLAG(7),
- NK_EDIT_NO_HORIZONTAL_SCROLL = NK_FLAG(8),
- NK_EDIT_ALWAYS_INSERT_MODE = NK_FLAG(9),
- NK_EDIT_MULTILINE = NK_FLAG(10),
- NK_EDIT_GOTO_END_ON_ACTIVATE = NK_FLAG(11)
-};
-enum nk_edit_types {
- NK_EDIT_SIMPLE = NK_EDIT_ALWAYS_INSERT_MODE,
- NK_EDIT_FIELD = NK_EDIT_SIMPLE|NK_EDIT_SELECTABLE|NK_EDIT_CLIPBOARD,
- NK_EDIT_BOX = NK_EDIT_ALWAYS_INSERT_MODE| NK_EDIT_SELECTABLE| NK_EDIT_MULTILINE|NK_EDIT_ALLOW_TAB|NK_EDIT_CLIPBOARD,
- NK_EDIT_EDITOR = NK_EDIT_SELECTABLE|NK_EDIT_MULTILINE|NK_EDIT_ALLOW_TAB| NK_EDIT_CLIPBOARD
-};
-enum nk_edit_events {
- NK_EDIT_ACTIVE = NK_FLAG(0), /* edit widget is currently being modified */
- NK_EDIT_INACTIVE = NK_FLAG(1), /* edit widget is not active and is not being modified */
- NK_EDIT_ACTIVATED = NK_FLAG(2), /* edit widget went from state inactive to state active */
- NK_EDIT_DEACTIVATED = NK_FLAG(3), /* edit widget went from state active to state inactive */
- NK_EDIT_COMMITED = NK_FLAG(4) /* edit widget has received an enter and lost focus */
-};
-NK_API nk_flags nk_edit_string(struct nk_context*, nk_flags, char *buffer, int *len, int max, nk_plugin_filter);
-NK_API nk_flags nk_edit_string_zero_terminated(struct nk_context*, nk_flags, char *buffer, int max, nk_plugin_filter);
-NK_API nk_flags nk_edit_buffer(struct nk_context*, nk_flags, struct nk_text_edit*, nk_plugin_filter);
-NK_API void nk_edit_focus(struct nk_context*, nk_flags flags);
-NK_API void nk_edit_unfocus(struct nk_context*);
-/* =============================================================================
- *
- * CHART
- *
- * ============================================================================= */
-NK_API nk_bool nk_chart_begin(struct nk_context*, enum nk_chart_type, int num, float min, float max);
-NK_API nk_bool nk_chart_begin_colored(struct nk_context*, enum nk_chart_type, struct nk_color, struct nk_color active, int num, float min, float max);
-NK_API void nk_chart_add_slot(struct nk_context *ctx, const enum nk_chart_type, int count, float min_value, float max_value);
-NK_API void nk_chart_add_slot_colored(struct nk_context *ctx, const enum nk_chart_type, struct nk_color, struct nk_color active, int count, float min_value, float max_value);
-NK_API nk_flags nk_chart_push(struct nk_context*, float);
-NK_API nk_flags nk_chart_push_slot(struct nk_context*, float, int);
-NK_API void nk_chart_end(struct nk_context*);
-NK_API void nk_plot(struct nk_context*, enum nk_chart_type, const float *values, int count, int offset);
-NK_API void nk_plot_function(struct nk_context*, enum nk_chart_type, void *userdata, float(*value_getter)(void* user, int index), int count, int offset);
-/* =============================================================================
- *
- * POPUP
- *
- * ============================================================================= */
-NK_API nk_bool nk_popup_begin(struct nk_context*, enum nk_popup_type, const char*, nk_flags, struct nk_rect bounds);
-NK_API void nk_popup_close(struct nk_context*);
-NK_API void nk_popup_end(struct nk_context*);
-NK_API void nk_popup_get_scroll(struct nk_context*, nk_uint *offset_x, nk_uint *offset_y);
-NK_API void nk_popup_set_scroll(struct nk_context*, nk_uint offset_x, nk_uint offset_y);
-/* =============================================================================
- *
- * COMBOBOX
- *
- * ============================================================================= */
-NK_API int nk_combo(struct nk_context*, const char **items, int count, int selected, int item_height, struct nk_vec2 size);
-NK_API int nk_combo_separator(struct nk_context*, const char *items_separated_by_separator, int separator, int selected, int count, int item_height, struct nk_vec2 size);
-NK_API int nk_combo_string(struct nk_context*, const char *items_separated_by_zeros, int selected, int count, int item_height, struct nk_vec2 size);
-NK_API int nk_combo_callback(struct nk_context*, void(*item_getter)(void*, int, const char**), void *userdata, int selected, int count, int item_height, struct nk_vec2 size);
-NK_API void nk_combobox(struct nk_context*, const char **items, int count, int *selected, int item_height, struct nk_vec2 size);
-NK_API void nk_combobox_string(struct nk_context*, const char *items_separated_by_zeros, int *selected, int count, int item_height, struct nk_vec2 size);
-NK_API void nk_combobox_separator(struct nk_context*, const char *items_separated_by_separator, int separator, int *selected, int count, int item_height, struct nk_vec2 size);
-NK_API void nk_combobox_callback(struct nk_context*, void(*item_getter)(void*, int, const char**), void*, int *selected, int count, int item_height, struct nk_vec2 size);
-/* =============================================================================
- *
- * ABSTRACT COMBOBOX
- *
- * ============================================================================= */
-NK_API nk_bool nk_combo_begin_text(struct nk_context*, const char *selected, int, struct nk_vec2 size);
-NK_API nk_bool nk_combo_begin_label(struct nk_context*, const char *selected, struct nk_vec2 size);
-NK_API nk_bool nk_combo_begin_color(struct nk_context*, struct nk_color color, struct nk_vec2 size);
-NK_API nk_bool nk_combo_begin_symbol(struct nk_context*, enum nk_symbol_type, struct nk_vec2 size);
-NK_API nk_bool nk_combo_begin_symbol_label(struct nk_context*, const char *selected, enum nk_symbol_type, struct nk_vec2 size);
-NK_API nk_bool nk_combo_begin_symbol_text(struct nk_context*, const char *selected, int, enum nk_symbol_type, struct nk_vec2 size);
-NK_API nk_bool nk_combo_begin_image(struct nk_context*, struct nk_image img, struct nk_vec2 size);
-NK_API nk_bool nk_combo_begin_image_label(struct nk_context*, const char *selected, struct nk_image, struct nk_vec2 size);
-NK_API nk_bool nk_combo_begin_image_text(struct nk_context*, const char *selected, int, struct nk_image, struct nk_vec2 size);
-NK_API nk_bool nk_combo_item_label(struct nk_context*, const char*, nk_flags alignment);
-NK_API nk_bool nk_combo_item_text(struct nk_context*, const char*,int, nk_flags alignment);
-NK_API nk_bool nk_combo_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment);
-NK_API nk_bool nk_combo_item_image_text(struct nk_context*, struct nk_image, const char*, int,nk_flags alignment);
-NK_API nk_bool nk_combo_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment);
-NK_API nk_bool nk_combo_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment);
-NK_API void nk_combo_close(struct nk_context*);
-NK_API void nk_combo_end(struct nk_context*);
-/* =============================================================================
- *
- * CONTEXTUAL
- *
- * ============================================================================= */
-NK_API nk_bool nk_contextual_begin(struct nk_context*, nk_flags, struct nk_vec2, struct nk_rect trigger_bounds);
-NK_API nk_bool nk_contextual_item_text(struct nk_context*, const char*, int,nk_flags align);
-NK_API nk_bool nk_contextual_item_label(struct nk_context*, const char*, nk_flags align);
-NK_API nk_bool nk_contextual_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment);
-NK_API nk_bool nk_contextual_item_image_text(struct nk_context*, struct nk_image, const char*, int len, nk_flags alignment);
-NK_API nk_bool nk_contextual_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment);
-NK_API nk_bool nk_contextual_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment);
-NK_API void nk_contextual_close(struct nk_context*);
-NK_API void nk_contextual_end(struct nk_context*);
-/* =============================================================================
- *
- * TOOLTIP
- *
- * ============================================================================= */
-NK_API void nk_tooltip(struct nk_context*, const char*);
-#ifdef NK_INCLUDE_STANDARD_VARARGS
-NK_API void nk_tooltipf(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, ...) NK_PRINTF_VARARG_FUNC(2);
-NK_API void nk_tooltipfv(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(2);
-#endif
-NK_API nk_bool nk_tooltip_begin(struct nk_context*, float width);
-NK_API void nk_tooltip_end(struct nk_context*);
-/* =============================================================================
- *
- * MENU
- *
- * ============================================================================= */
-NK_API void nk_menubar_begin(struct nk_context*);
-NK_API void nk_menubar_end(struct nk_context*);
-NK_API nk_bool nk_menu_begin_text(struct nk_context*, const char* title, int title_len, nk_flags align, struct nk_vec2 size);
-NK_API nk_bool nk_menu_begin_label(struct nk_context*, const char*, nk_flags align, struct nk_vec2 size);
-NK_API nk_bool nk_menu_begin_image(struct nk_context*, const char*, struct nk_image, struct nk_vec2 size);
-NK_API nk_bool nk_menu_begin_image_text(struct nk_context*, const char*, int,nk_flags align,struct nk_image, struct nk_vec2 size);
-NK_API nk_bool nk_menu_begin_image_label(struct nk_context*, const char*, nk_flags align,struct nk_image, struct nk_vec2 size);
-NK_API nk_bool nk_menu_begin_symbol(struct nk_context*, const char*, enum nk_symbol_type, struct nk_vec2 size);
-NK_API nk_bool nk_menu_begin_symbol_text(struct nk_context*, const char*, int,nk_flags align,enum nk_symbol_type, struct nk_vec2 size);
-NK_API nk_bool nk_menu_begin_symbol_label(struct nk_context*, const char*, nk_flags align,enum nk_symbol_type, struct nk_vec2 size);
-NK_API nk_bool nk_menu_item_text(struct nk_context*, const char*, int,nk_flags align);
-NK_API nk_bool nk_menu_item_label(struct nk_context*, const char*, nk_flags alignment);
-NK_API nk_bool nk_menu_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment);
-NK_API nk_bool nk_menu_item_image_text(struct nk_context*, struct nk_image, const char*, int len, nk_flags alignment);
-NK_API nk_bool nk_menu_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment);
-NK_API nk_bool nk_menu_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment);
-NK_API void nk_menu_close(struct nk_context*);
-NK_API void nk_menu_end(struct nk_context*);
-/* =============================================================================
- *
- * STYLE
- *
- * ============================================================================= */
-enum nk_style_colors {
- NK_COLOR_TEXT,
- NK_COLOR_WINDOW,
- NK_COLOR_HEADER,
- NK_COLOR_BORDER,
- NK_COLOR_BUTTON,
- NK_COLOR_BUTTON_HOVER,
- NK_COLOR_BUTTON_ACTIVE,
- NK_COLOR_TOGGLE,
- NK_COLOR_TOGGLE_HOVER,
- NK_COLOR_TOGGLE_CURSOR,
- NK_COLOR_SELECT,
- NK_COLOR_SELECT_ACTIVE,
- NK_COLOR_SLIDER,
- NK_COLOR_SLIDER_CURSOR,
- NK_COLOR_SLIDER_CURSOR_HOVER,
- NK_COLOR_SLIDER_CURSOR_ACTIVE,
- NK_COLOR_PROPERTY,
- NK_COLOR_EDIT,
- NK_COLOR_EDIT_CURSOR,
- NK_COLOR_COMBO,
- NK_COLOR_CHART,
- NK_COLOR_CHART_COLOR,
- NK_COLOR_CHART_COLOR_HIGHLIGHT,
- NK_COLOR_SCROLLBAR,
- NK_COLOR_SCROLLBAR_CURSOR,
- NK_COLOR_SCROLLBAR_CURSOR_HOVER,
- NK_COLOR_SCROLLBAR_CURSOR_ACTIVE,
- NK_COLOR_TAB_HEADER,
- NK_COLOR_COUNT
-};
-enum nk_style_cursor {
- NK_CURSOR_ARROW,
- NK_CURSOR_TEXT,
- NK_CURSOR_MOVE,
- NK_CURSOR_RESIZE_VERTICAL,
- NK_CURSOR_RESIZE_HORIZONTAL,
- NK_CURSOR_RESIZE_TOP_LEFT_DOWN_RIGHT,
- NK_CURSOR_RESIZE_TOP_RIGHT_DOWN_LEFT,
- NK_CURSOR_COUNT
-};
-NK_API void nk_style_default(struct nk_context*);
-NK_API void nk_style_from_table(struct nk_context*, const struct nk_color*);
-NK_API void nk_style_load_cursor(struct nk_context*, enum nk_style_cursor, const struct nk_cursor*);
-NK_API void nk_style_load_all_cursors(struct nk_context*, struct nk_cursor*);
-NK_API const char* nk_style_get_color_by_name(enum nk_style_colors);
-NK_API void nk_style_set_font(struct nk_context*, const struct nk_user_font*);
-NK_API nk_bool nk_style_set_cursor(struct nk_context*, enum nk_style_cursor);
-NK_API void nk_style_show_cursor(struct nk_context*);
-NK_API void nk_style_hide_cursor(struct nk_context*);
-
-NK_API nk_bool nk_style_push_font(struct nk_context*, const struct nk_user_font*);
-NK_API nk_bool nk_style_push_float(struct nk_context*, float*, float);
-NK_API nk_bool nk_style_push_vec2(struct nk_context*, struct nk_vec2*, struct nk_vec2);
-NK_API nk_bool nk_style_push_style_item(struct nk_context*, struct nk_style_item*, struct nk_style_item);
-NK_API nk_bool nk_style_push_flags(struct nk_context*, nk_flags*, nk_flags);
-NK_API nk_bool nk_style_push_color(struct nk_context*, struct nk_color*, struct nk_color);
-
-NK_API nk_bool nk_style_pop_font(struct nk_context*);
-NK_API nk_bool nk_style_pop_float(struct nk_context*);
-NK_API nk_bool nk_style_pop_vec2(struct nk_context*);
-NK_API nk_bool nk_style_pop_style_item(struct nk_context*);
-NK_API nk_bool nk_style_pop_flags(struct nk_context*);
-NK_API nk_bool nk_style_pop_color(struct nk_context*);
-/* =============================================================================
- *
- * COLOR
- *
- * ============================================================================= */
-NK_API struct nk_color nk_rgb(int r, int g, int b);
-NK_API struct nk_color nk_rgb_iv(const int *rgb);
-NK_API struct nk_color nk_rgb_bv(const nk_byte* rgb);
-NK_API struct nk_color nk_rgb_f(float r, float g, float b);
-NK_API struct nk_color nk_rgb_fv(const float *rgb);
-NK_API struct nk_color nk_rgb_cf(struct nk_colorf c);
-NK_API struct nk_color nk_rgb_hex(const char *rgb);
-
-NK_API struct nk_color nk_rgba(int r, int g, int b, int a);
-NK_API struct nk_color nk_rgba_u32(nk_uint);
-NK_API struct nk_color nk_rgba_iv(const int *rgba);
-NK_API struct nk_color nk_rgba_bv(const nk_byte *rgba);
-NK_API struct nk_color nk_rgba_f(float r, float g, float b, float a);
-NK_API struct nk_color nk_rgba_fv(const float *rgba);
-NK_API struct nk_color nk_rgba_cf(struct nk_colorf c);
-NK_API struct nk_color nk_rgba_hex(const char *rgb);
-
-NK_API struct nk_colorf nk_hsva_colorf(float h, float s, float v, float a);
-NK_API struct nk_colorf nk_hsva_colorfv(float *c);
-NK_API void nk_colorf_hsva_f(float *out_h, float *out_s, float *out_v, float *out_a, struct nk_colorf in);
-NK_API void nk_colorf_hsva_fv(float *hsva, struct nk_colorf in);
-
-NK_API struct nk_color nk_hsv(int h, int s, int v);
-NK_API struct nk_color nk_hsv_iv(const int *hsv);
-NK_API struct nk_color nk_hsv_bv(const nk_byte *hsv);
-NK_API struct nk_color nk_hsv_f(float h, float s, float v);
-NK_API struct nk_color nk_hsv_fv(const float *hsv);
-
-NK_API struct nk_color nk_hsva(int h, int s, int v, int a);
-NK_API struct nk_color nk_hsva_iv(const int *hsva);
-NK_API struct nk_color nk_hsva_bv(const nk_byte *hsva);
-NK_API struct nk_color nk_hsva_f(float h, float s, float v, float a);
-NK_API struct nk_color nk_hsva_fv(const float *hsva);
-
-/* color (conversion nuklear --> user) */
-NK_API void nk_color_f(float *r, float *g, float *b, float *a, struct nk_color);
-NK_API void nk_color_fv(float *rgba_out, struct nk_color);
-NK_API struct nk_colorf nk_color_cf(struct nk_color);
-NK_API void nk_color_d(double *r, double *g, double *b, double *a, struct nk_color);
-NK_API void nk_color_dv(double *rgba_out, struct nk_color);
-
-NK_API nk_uint nk_color_u32(struct nk_color);
-NK_API void nk_color_hex_rgba(char *output, struct nk_color);
-NK_API void nk_color_hex_rgb(char *output, struct nk_color);
-
-NK_API void nk_color_hsv_i(int *out_h, int *out_s, int *out_v, struct nk_color);
-NK_API void nk_color_hsv_b(nk_byte *out_h, nk_byte *out_s, nk_byte *out_v, struct nk_color);
-NK_API void nk_color_hsv_iv(int *hsv_out, struct nk_color);
-NK_API void nk_color_hsv_bv(nk_byte *hsv_out, struct nk_color);
-NK_API void nk_color_hsv_f(float *out_h, float *out_s, float *out_v, struct nk_color);
-NK_API void nk_color_hsv_fv(float *hsv_out, struct nk_color);
-
-NK_API void nk_color_hsva_i(int *h, int *s, int *v, int *a, struct nk_color);
-NK_API void nk_color_hsva_b(nk_byte *h, nk_byte *s, nk_byte *v, nk_byte *a, struct nk_color);
-NK_API void nk_color_hsva_iv(int *hsva_out, struct nk_color);
-NK_API void nk_color_hsva_bv(nk_byte *hsva_out, struct nk_color);
-NK_API void nk_color_hsva_f(float *out_h, float *out_s, float *out_v, float *out_a, struct nk_color);
-NK_API void nk_color_hsva_fv(float *hsva_out, struct nk_color);
-/* =============================================================================
- *
- * IMAGE
- *
- * ============================================================================= */
-NK_API nk_handle nk_handle_ptr(void*);
-NK_API nk_handle nk_handle_id(int);
-NK_API struct nk_image nk_image_handle(nk_handle);
-NK_API struct nk_image nk_image_ptr(void*);
-NK_API struct nk_image nk_image_id(int);
-NK_API nk_bool nk_image_is_subimage(const struct nk_image* img);
-NK_API struct nk_image nk_subimage_ptr(void*, unsigned short w, unsigned short h, struct nk_rect sub_region);
-NK_API struct nk_image nk_subimage_id(int, unsigned short w, unsigned short h, struct nk_rect sub_region);
-NK_API struct nk_image nk_subimage_handle(nk_handle, unsigned short w, unsigned short h, struct nk_rect sub_region);
-/* =============================================================================
- *
- * MATH
- *
- * ============================================================================= */
-NK_API nk_hash nk_murmur_hash(const void *key, int len, nk_hash seed);
-NK_API void nk_triangle_from_direction(struct nk_vec2 *result, struct nk_rect r, float pad_x, float pad_y, enum nk_heading);
-
-NK_API struct nk_vec2 nk_vec2(float x, float y);
-NK_API struct nk_vec2 nk_vec2i(int x, int y);
-NK_API struct nk_vec2 nk_vec2v(const float *xy);
-NK_API struct nk_vec2 nk_vec2iv(const int *xy);
-
-NK_API struct nk_rect nk_get_null_rect(void);
-NK_API struct nk_rect nk_rect(float x, float y, float w, float h);
-NK_API struct nk_rect nk_recti(int x, int y, int w, int h);
-NK_API struct nk_rect nk_recta(struct nk_vec2 pos, struct nk_vec2 size);
-NK_API struct nk_rect nk_rectv(const float *xywh);
-NK_API struct nk_rect nk_rectiv(const int *xywh);
-NK_API struct nk_vec2 nk_rect_pos(struct nk_rect);
-NK_API struct nk_vec2 nk_rect_size(struct nk_rect);
-/* =============================================================================
- *
- * STRING
- *
- * ============================================================================= */
-NK_API int nk_strlen(const char *str);
-NK_API int nk_stricmp(const char *s1, const char *s2);
-NK_API int nk_stricmpn(const char *s1, const char *s2, int n);
-NK_API int nk_strtoi(const char *str, const char **endptr);
-NK_API float nk_strtof(const char *str, const char **endptr);
-#ifndef NK_STRTOD
-#define NK_STRTOD nk_strtod
-NK_API double nk_strtod(const char *str, const char **endptr);
-#endif
-NK_API int nk_strfilter(const char *text, const char *regexp);
-NK_API int nk_strmatch_fuzzy_string(char const *str, char const *pattern, int *out_score);
-NK_API int nk_strmatch_fuzzy_text(const char *txt, int txt_len, const char *pattern, int *out_score);
-/* =============================================================================
- *
- * UTF-8
- *
- * ============================================================================= */
-NK_API int nk_utf_decode(const char*, nk_rune*, int);
-NK_API int nk_utf_encode(nk_rune, char*, int);
-NK_API int nk_utf_len(const char*, int byte_len);
-NK_API const char* nk_utf_at(const char *buffer, int length, int index, nk_rune *unicode, int *len);
-/* ===============================================================
- *
- * FONT
- *
- * ===============================================================*/
-/* Font handling in this library was designed to be quite customizable and lets
- you decide what you want to use and what you want to provide. There are three
- different ways to use the font atlas. The first two will use your font
- handling scheme and only requires essential data to run nuklear. The next
- slightly more advanced features is font handling with vertex buffer output.
- Finally the most complex API wise is using nuklear's font baking API.
-
- 1.) Using your own implementation without vertex buffer output
- --------------------------------------------------------------
- So first up the easiest way to do font handling is by just providing a
- `nk_user_font` struct which only requires the height in pixel of the used
- font and a callback to calculate the width of a string. This way of handling
- fonts is best fitted for using the normal draw shape command API where you
- do all the text drawing yourself and the library does not require any kind
- of deeper knowledge about which font handling mechanism you use.
- IMPORTANT: the `nk_user_font` pointer provided to nuklear has to persist
- over the complete life time! I know this sucks but it is currently the only
- way to switch between fonts.
-
- float your_text_width_calculation(nk_handle handle, float height, const char *text, int len)
- {
- your_font_type *type = handle.ptr;
- float text_width = ...;
- return text_width;
- }
-
- struct nk_user_font font;
- font.userdata.ptr = &your_font_class_or_struct;
- font.height = your_font_height;
- font.width = your_text_width_calculation;
-
- struct nk_context ctx;
- nk_init_default(&ctx, &font);
-
- 2.) Using your own implementation with vertex buffer output
- --------------------------------------------------------------
- While the first approach works fine if you don't want to use the optional
- vertex buffer output it is not enough if you do. To get font handling working
- for these cases you have to provide two additional parameters inside the
- `nk_user_font`. First a texture atlas handle used to draw text as subimages
- of a bigger font atlas texture and a callback to query a character's glyph
- information (offset, size, ...). So it is still possible to provide your own
- font and use the vertex buffer output.
-
- float your_text_width_calculation(nk_handle handle, float height, const char *text, int len)
- {
- your_font_type *type = handle.ptr;
- float text_width = ...;
- return text_width;
- }
- void query_your_font_glyph(nk_handle handle, float font_height, struct nk_user_font_glyph *glyph, nk_rune codepoint, nk_rune next_codepoint)
- {
- your_font_type *type = handle.ptr;
- glyph.width = ...;
- glyph.height = ...;
- glyph.xadvance = ...;
- glyph.uv[0].x = ...;
- glyph.uv[0].y = ...;
- glyph.uv[1].x = ...;
- glyph.uv[1].y = ...;
- glyph.offset.x = ...;
- glyph.offset.y = ...;
- }
-
- struct nk_user_font font;
- font.userdata.ptr = &your_font_class_or_struct;
- font.height = your_font_height;
- font.width = your_text_width_calculation;
- font.query = query_your_font_glyph;
- font.texture.id = your_font_texture;
-
- struct nk_context ctx;
- nk_init_default(&ctx, &font);
-
- 3.) Nuklear font baker
- ------------------------------------
- The final approach if you do not have a font handling functionality or don't
- want to use it in this library is by using the optional font baker.
- The font baker APIs can be used to create a font plus font atlas texture
- and can be used with or without the vertex buffer output.
-
- It still uses the `nk_user_font` struct and the two different approaches
- previously stated still work. The font baker is not located inside
- `nk_context` like all other systems since it can be understood as more of
- an extension to nuklear and does not really depend on any `nk_context` state.
-
- Font baker need to be initialized first by one of the nk_font_atlas_init_xxx
- functions. If you don't care about memory just call the default version
- `nk_font_atlas_init_default` which will allocate all memory from the standard library.
- If you want to control memory allocation but you don't care if the allocated
- memory is temporary and therefore can be freed directly after the baking process
- is over or permanent you can call `nk_font_atlas_init`.
-
- After successfully initializing the font baker you can add Truetype(.ttf) fonts from
- different sources like memory or from file by calling one of the `nk_font_atlas_add_xxx`.
- functions. Adding font will permanently store each font, font config and ttf memory block(!)
- inside the font atlas and allows to reuse the font atlas. If you don't want to reuse
- the font baker by for example adding additional fonts you can call
- `nk_font_atlas_cleanup` after the baking process is over (after calling nk_font_atlas_end).
-
- As soon as you added all fonts you wanted you can now start the baking process
- for every selected glyph to image by calling `nk_font_atlas_bake`.
- The baking process returns image memory, width and height which can be used to
- either create your own image object or upload it to any graphics library.
- No matter which case you finally have to call `nk_font_atlas_end` which
- will free all temporary memory including the font atlas image so make sure
- you created our texture beforehand. `nk_font_atlas_end` requires a handle
- to your font texture or object and optionally fills a `struct nk_draw_null_texture`
- which can be used for the optional vertex output. If you don't want it just
- set the argument to `NULL`.
-
- At this point you are done and if you don't want to reuse the font atlas you
- can call `nk_font_atlas_cleanup` to free all truetype blobs and configuration
- memory. Finally if you don't use the font atlas and any of it's fonts anymore
- you need to call `nk_font_atlas_clear` to free all memory still being used.
-
- struct nk_font_atlas atlas;
- nk_font_atlas_init_default(&atlas);
- nk_font_atlas_begin(&atlas);
- nk_font *font = nk_font_atlas_add_from_file(&atlas, "Path/To/Your/TTF_Font.ttf", 13, 0);
- nk_font *font2 = nk_font_atlas_add_from_file(&atlas, "Path/To/Your/TTF_Font2.ttf", 16, 0);
- const void* img = nk_font_atlas_bake(&atlas, &img_width, &img_height, NK_FONT_ATLAS_RGBA32);
- nk_font_atlas_end(&atlas, nk_handle_id(texture), 0);
-
- struct nk_context ctx;
- nk_init_default(&ctx, &font->handle);
- while (1) {
-
- }
- nk_font_atlas_clear(&atlas);
-
- The font baker API is probably the most complex API inside this library and
- I would suggest reading some of my examples `example/` to get a grip on how
- to use the font atlas. There are a number of details I left out. For example
- how to merge fonts, configure a font with `nk_font_config` to use other languages,
- use another texture coordinate format and a lot more:
-
- struct nk_font_config cfg = nk_font_config(font_pixel_height);
- cfg.merge_mode = nk_false or nk_true;
- cfg.range = nk_font_korean_glyph_ranges();
- cfg.coord_type = NK_COORD_PIXEL;
- nk_font *font = nk_font_atlas_add_from_file(&atlas, "Path/To/Your/TTF_Font.ttf", 13, &cfg);
-
-*/
-struct nk_user_font_glyph;
-typedef float(*nk_text_width_f)(nk_handle, float h, const char*, int len);
-typedef void(*nk_query_font_glyph_f)(nk_handle handle, float font_height,
- struct nk_user_font_glyph *glyph,
- nk_rune codepoint, nk_rune next_codepoint);
-
-#if defined(NK_INCLUDE_VERTEX_BUFFER_OUTPUT) || defined(NK_INCLUDE_SOFTWARE_FONT)
-struct nk_user_font_glyph {
- struct nk_vec2 uv[2];
- /* texture coordinates */
- struct nk_vec2 offset;
- /* offset between top left and glyph */
- float width, height;
- /* size of the glyph */
- float xadvance;
- /* offset to the next glyph */
-};
-#endif
-
-struct nk_user_font {
- nk_handle userdata;
- /* user provided font handle */
- float height;
- /* max height of the font */
- nk_text_width_f width;
- /* font string width in pixel callback */
-#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT
- nk_query_font_glyph_f query;
- /* font glyph callback to query drawing info */
- nk_handle texture;
- /* texture handle to the used font atlas or texture */
-#endif
-};
-
-#ifdef NK_INCLUDE_FONT_BAKING
-enum nk_font_coord_type {
- NK_COORD_UV, /* texture coordinates inside font glyphs are clamped between 0-1 */
- NK_COORD_PIXEL /* texture coordinates inside font glyphs are in absolute pixel */
-};
-
-struct nk_font;
-struct nk_baked_font {
- float height;
- /* height of the font */
- float ascent, descent;
- /* font glyphs ascent and descent */
- nk_rune glyph_offset;
- /* glyph array offset inside the font glyph baking output array */
- nk_rune glyph_count;
- /* number of glyphs of this font inside the glyph baking array output */
- const nk_rune *ranges;
- /* font codepoint ranges as pairs of (from/to) and 0 as last element */
-};
-
-struct nk_font_config {
- struct nk_font_config *next;
- /* NOTE: only used internally */
- void *ttf_blob;
- /* pointer to loaded TTF file memory block.
- * NOTE: not needed for nk_font_atlas_add_from_memory and nk_font_atlas_add_from_file. */
- nk_size ttf_size;
- /* size of the loaded TTF file memory block
- * NOTE: not needed for nk_font_atlas_add_from_memory and nk_font_atlas_add_from_file. */
-
- unsigned char ttf_data_owned_by_atlas;
- /* used inside font atlas: default to: 0*/
- unsigned char merge_mode;
- /* merges this font into the last font */
- unsigned char pixel_snap;
- /* align every character to pixel boundary (if true set oversample (1,1)) */
- unsigned char oversample_v, oversample_h;
- /* rasterize at hight quality for sub-pixel position */
- unsigned char padding[3];
-
- float size;
- /* baked pixel height of the font */
- enum nk_font_coord_type coord_type;
- /* texture coordinate format with either pixel or UV coordinates */
- struct nk_vec2 spacing;
- /* extra pixel spacing between glyphs */
- const nk_rune *range;
- /* list of unicode ranges (2 values per range, zero terminated) */
- struct nk_baked_font *font;
- /* font to setup in the baking process: NOTE: not needed for font atlas */
- nk_rune fallback_glyph;
- /* fallback glyph to use if a given rune is not found */
- struct nk_font_config *n;
- struct nk_font_config *p;
-};
-
-struct nk_font_glyph {
- nk_rune codepoint;
- float xadvance;
- float x0, y0, x1, y1, w, h;
- float u0, v0, u1, v1;
-};
-
-struct nk_font {
- struct nk_font *next;
- struct nk_user_font handle;
- struct nk_baked_font info;
- float scale;
- struct nk_font_glyph *glyphs;
- const struct nk_font_glyph *fallback;
- nk_rune fallback_codepoint;
- nk_handle texture;
- struct nk_font_config *config;
-};
-
-enum nk_font_atlas_format {
- NK_FONT_ATLAS_ALPHA8,
- NK_FONT_ATLAS_RGBA32
-};
-
-struct nk_font_atlas {
- void *pixel;
- int tex_width;
- int tex_height;
-
- struct nk_allocator permanent;
- struct nk_allocator temporary;
-
- struct nk_recti custom;
- struct nk_cursor cursors[NK_CURSOR_COUNT];
-
- int glyph_count;
- struct nk_font_glyph *glyphs;
- struct nk_font *default_font;
- struct nk_font *fonts;
- struct nk_font_config *config;
- int font_num;
-};
-
-/* some language glyph codepoint ranges */
-NK_API const nk_rune *nk_font_default_glyph_ranges(void);
-NK_API const nk_rune *nk_font_chinese_glyph_ranges(void);
-NK_API const nk_rune *nk_font_cyrillic_glyph_ranges(void);
-NK_API const nk_rune *nk_font_korean_glyph_ranges(void);
-
-#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR
-NK_API void nk_font_atlas_init_default(struct nk_font_atlas*);
-#endif
-NK_API void nk_font_atlas_init(struct nk_font_atlas*, struct nk_allocator*);
-NK_API void nk_font_atlas_init_custom(struct nk_font_atlas*, struct nk_allocator *persistent, struct nk_allocator *transient);
-NK_API void nk_font_atlas_begin(struct nk_font_atlas*);
-NK_API struct nk_font_config nk_font_config(float pixel_height);
-NK_API struct nk_font *nk_font_atlas_add(struct nk_font_atlas*, const struct nk_font_config*);
-#ifdef NK_INCLUDE_DEFAULT_FONT
-NK_API struct nk_font* nk_font_atlas_add_default(struct nk_font_atlas*, float height, const struct nk_font_config*);
-#endif
-NK_API struct nk_font* nk_font_atlas_add_from_memory(struct nk_font_atlas *atlas, void *memory, nk_size size, float height, const struct nk_font_config *config);
-#ifdef NK_INCLUDE_STANDARD_IO
-NK_API struct nk_font* nk_font_atlas_add_from_file(struct nk_font_atlas *atlas, const char *file_path, float height, const struct nk_font_config*);
-#endif
-NK_API struct nk_font *nk_font_atlas_add_compressed(struct nk_font_atlas*, void *memory, nk_size size, float height, const struct nk_font_config*);
-NK_API struct nk_font* nk_font_atlas_add_compressed_base85(struct nk_font_atlas*, const char *data, float height, const struct nk_font_config *config);
-NK_API const void* nk_font_atlas_bake(struct nk_font_atlas*, int *width, int *height, enum nk_font_atlas_format);
-NK_API void nk_font_atlas_end(struct nk_font_atlas*, nk_handle tex, struct nk_draw_null_texture*);
-NK_API const struct nk_font_glyph* nk_font_find_glyph(struct nk_font*, nk_rune unicode);
-NK_API void nk_font_atlas_cleanup(struct nk_font_atlas *atlas);
-NK_API void nk_font_atlas_clear(struct nk_font_atlas*);
-
-#endif
-
-/* ==============================================================
- *
- * MEMORY BUFFER
- *
- * ===============================================================*/
-/* A basic (double)-buffer with linear allocation and resetting as only
- freeing policy. The buffer's main purpose is to control all memory management
- inside the GUI toolkit and still leave memory control as much as possible in
- the hand of the user while also making sure the library is easy to use if
- not as much control is needed.
- In general all memory inside this library can be provided from the user in
- three different ways.
-
- The first way and the one providing most control is by just passing a fixed
- size memory block. In this case all control lies in the hand of the user
- since he can exactly control where the memory comes from and how much memory
- the library should consume. Of course using the fixed size API removes the
- ability to automatically resize a buffer if not enough memory is provided so
- you have to take over the resizing. While being a fixed sized buffer sounds
- quite limiting, it is very effective in this library since the actual memory
- consumption is quite stable and has a fixed upper bound for a lot of cases.
-
- If you don't want to think about how much memory the library should allocate
- at all time or have a very dynamic UI with unpredictable memory consumption
- habits but still want control over memory allocation you can use the dynamic
- allocator based API. The allocator consists of two callbacks for allocating
- and freeing memory and optional userdata so you can plugin your own allocator.
-
- The final and easiest way can be used by defining
- NK_INCLUDE_DEFAULT_ALLOCATOR which uses the standard library memory
- allocation functions malloc and free and takes over complete control over
- memory in this library.
-*/
-struct nk_memory_status {
- void *memory;
- unsigned int type;
- nk_size size;
- nk_size allocated;
- nk_size needed;
- nk_size calls;
-};
-
-enum nk_allocation_type {
- NK_BUFFER_FIXED,
- NK_BUFFER_DYNAMIC
-};
-
-enum nk_buffer_allocation_type {
- NK_BUFFER_FRONT,
- NK_BUFFER_BACK,
- NK_BUFFER_MAX
-};
-
-struct nk_buffer_marker {
- nk_bool active;
- nk_size offset;
-};
-
-struct nk_memory {void *ptr;nk_size size;};
-struct nk_buffer {
- struct nk_buffer_marker marker[NK_BUFFER_MAX];
- /* buffer marker to free a buffer to a certain offset */
- struct nk_allocator pool;
- /* allocator callback for dynamic buffers */
- enum nk_allocation_type type;
- /* memory management type */
- struct nk_memory memory;
- /* memory and size of the current memory block */
- float grow_factor;
- /* growing factor for dynamic memory management */
- nk_size allocated;
- /* total amount of memory allocated */
- nk_size needed;
- /* totally consumed memory given that enough memory is present */
- nk_size calls;
- /* number of allocation calls */
- nk_size size;
- /* current size of the buffer */
-};
-
-#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR
-NK_API void nk_buffer_init_default(struct nk_buffer*);
-#endif
-NK_API void nk_buffer_init(struct nk_buffer*, const struct nk_allocator*, nk_size size);
-NK_API void nk_buffer_init_fixed(struct nk_buffer*, void *memory, nk_size size);
-NK_API void nk_buffer_info(struct nk_memory_status*, struct nk_buffer*);
-NK_API void nk_buffer_push(struct nk_buffer*, enum nk_buffer_allocation_type type, const void *memory, nk_size size, nk_size align);
-NK_API void nk_buffer_mark(struct nk_buffer*, enum nk_buffer_allocation_type type);
-NK_API void nk_buffer_reset(struct nk_buffer*, enum nk_buffer_allocation_type type);
-NK_API void nk_buffer_clear(struct nk_buffer*);
-NK_API void nk_buffer_free(struct nk_buffer*);
-NK_API void *nk_buffer_memory(struct nk_buffer*);
-NK_API const void *nk_buffer_memory_const(const struct nk_buffer*);
-NK_API nk_size nk_buffer_total(struct nk_buffer*);
-
-/* ==============================================================
- *
- * STRING
- *
- * ===============================================================*/
-/* Basic string buffer which is only used in context with the text editor
- * to manage and manipulate dynamic or fixed size string content. This is _NOT_
- * the default string handling method. The only instance you should have any contact
- * with this API is if you interact with an `nk_text_edit` object inside one of the
- * copy and paste functions and even there only for more advanced cases. */
-struct nk_str {
- struct nk_buffer buffer;
- int len; /* in codepoints/runes/glyphs */
-};
-
-#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR
-NK_API void nk_str_init_default(struct nk_str*);
-#endif
-NK_API void nk_str_init(struct nk_str*, const struct nk_allocator*, nk_size size);
-NK_API void nk_str_init_fixed(struct nk_str*, void *memory, nk_size size);
-NK_API void nk_str_clear(struct nk_str*);
-NK_API void nk_str_free(struct nk_str*);
-
-NK_API int nk_str_append_text_char(struct nk_str*, const char*, int);
-NK_API int nk_str_append_str_char(struct nk_str*, const char*);
-NK_API int nk_str_append_text_utf8(struct nk_str*, const char*, int);
-NK_API int nk_str_append_str_utf8(struct nk_str*, const char*);
-NK_API int nk_str_append_text_runes(struct nk_str*, const nk_rune*, int);
-NK_API int nk_str_append_str_runes(struct nk_str*, const nk_rune*);
-
-NK_API int nk_str_insert_at_char(struct nk_str*, int pos, const char*, int);
-NK_API int nk_str_insert_at_rune(struct nk_str*, int pos, const char*, int);
-
-NK_API int nk_str_insert_text_char(struct nk_str*, int pos, const char*, int);
-NK_API int nk_str_insert_str_char(struct nk_str*, int pos, const char*);
-NK_API int nk_str_insert_text_utf8(struct nk_str*, int pos, const char*, int);
-NK_API int nk_str_insert_str_utf8(struct nk_str*, int pos, const char*);
-NK_API int nk_str_insert_text_runes(struct nk_str*, int pos, const nk_rune*, int);
-NK_API int nk_str_insert_str_runes(struct nk_str*, int pos, const nk_rune*);
-
-NK_API void nk_str_remove_chars(struct nk_str*, int len);
-NK_API void nk_str_remove_runes(struct nk_str *str, int len);
-NK_API void nk_str_delete_chars(struct nk_str*, int pos, int len);
-NK_API void nk_str_delete_runes(struct nk_str*, int pos, int len);
-
-NK_API char *nk_str_at_char(struct nk_str*, int pos);
-NK_API char *nk_str_at_rune(struct nk_str*, int pos, nk_rune *unicode, int *len);
-NK_API nk_rune nk_str_rune_at(const struct nk_str*, int pos);
-NK_API const char *nk_str_at_char_const(const struct nk_str*, int pos);
-NK_API const char *nk_str_at_const(const struct nk_str*, int pos, nk_rune *unicode, int *len);
-
-NK_API char *nk_str_get(struct nk_str*);
-NK_API const char *nk_str_get_const(const struct nk_str*);
-NK_API int nk_str_len(struct nk_str*);
-NK_API int nk_str_len_char(struct nk_str*);
-
-/*===============================================================
- *
- * TEXT EDITOR
- *
- * ===============================================================*/
-/* Editing text in this library is handled by either `nk_edit_string` or
- * `nk_edit_buffer`. But like almost everything in this library there are multiple
- * ways of doing it and a balance between control and ease of use with memory
- * as well as functionality controlled by flags.
- *
- * This library generally allows three different levels of memory control:
- * First of is the most basic way of just providing a simple char array with
- * string length. This method is probably the easiest way of handling simple
- * user text input. Main upside is complete control over memory while the biggest
- * downside in comparison with the other two approaches is missing undo/redo.
- *
- * For UIs that require undo/redo the second way was created. It is based on
- * a fixed size nk_text_edit struct, which has an internal undo/redo stack.
- * This is mainly useful if you want something more like a text editor but don't want
- * to have a dynamically growing buffer.
- *
- * The final way is using a dynamically growing nk_text_edit struct, which
- * has both a default version if you don't care where memory comes from and an
- * allocator version if you do. While the text editor is quite powerful for its
- * complexity I would not recommend editing gigabytes of data with it.
- * It is rather designed for uses cases which make sense for a GUI library not for
- * an full blown text editor.
- */
-#ifndef NK_TEXTEDIT_UNDOSTATECOUNT
-#define NK_TEXTEDIT_UNDOSTATECOUNT 99
-#endif
-
-#ifndef NK_TEXTEDIT_UNDOCHARCOUNT
-#define NK_TEXTEDIT_UNDOCHARCOUNT 999
-#endif
-
-struct nk_text_edit;
-struct nk_clipboard {
- nk_handle userdata;
- nk_plugin_paste paste;
- nk_plugin_copy copy;
-};
-
-struct nk_text_undo_record {
- int where;
- short insert_length;
- short delete_length;
- short char_storage;
-};
-
-struct nk_text_undo_state {
- struct nk_text_undo_record undo_rec[NK_TEXTEDIT_UNDOSTATECOUNT];
- nk_rune undo_char[NK_TEXTEDIT_UNDOCHARCOUNT];
- short undo_point;
- short redo_point;
- short undo_char_point;
- short redo_char_point;
-};
-
-enum nk_text_edit_type {
- NK_TEXT_EDIT_SINGLE_LINE,
- NK_TEXT_EDIT_MULTI_LINE
-};
-
-enum nk_text_edit_mode {
- NK_TEXT_EDIT_MODE_VIEW,
- NK_TEXT_EDIT_MODE_INSERT,
- NK_TEXT_EDIT_MODE_REPLACE
-};
-
-struct nk_text_edit {
- struct nk_clipboard clip;
- struct nk_str string;
- nk_plugin_filter filter;
- struct nk_vec2 scrollbar;
-
- int cursor;
- int select_start;
- int select_end;
- unsigned char mode;
- unsigned char cursor_at_end_of_line;
- unsigned char initialized;
- unsigned char has_preferred_x;
- unsigned char single_line;
- unsigned char active;
- unsigned char padding1;
- float preferred_x;
- struct nk_text_undo_state undo;
-};
-
-/* filter function */
-NK_API nk_bool nk_filter_default(const struct nk_text_edit*, nk_rune unicode);
-NK_API nk_bool nk_filter_ascii(const struct nk_text_edit*, nk_rune unicode);
-NK_API nk_bool nk_filter_float(const struct nk_text_edit*, nk_rune unicode);
-NK_API nk_bool nk_filter_decimal(const struct nk_text_edit*, nk_rune unicode);
-NK_API nk_bool nk_filter_hex(const struct nk_text_edit*, nk_rune unicode);
-NK_API nk_bool nk_filter_oct(const struct nk_text_edit*, nk_rune unicode);
-NK_API nk_bool nk_filter_binary(const struct nk_text_edit*, nk_rune unicode);
-
-/* text editor */
-#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR
-NK_API void nk_textedit_init_default(struct nk_text_edit*);
-#endif
-NK_API void nk_textedit_init(struct nk_text_edit*, struct nk_allocator*, nk_size size);
-NK_API void nk_textedit_init_fixed(struct nk_text_edit*, void *memory, nk_size size);
-NK_API void nk_textedit_free(struct nk_text_edit*);
-NK_API void nk_textedit_text(struct nk_text_edit*, const char*, int total_len);
-NK_API void nk_textedit_delete(struct nk_text_edit*, int where, int len);
-NK_API void nk_textedit_delete_selection(struct nk_text_edit*);
-NK_API void nk_textedit_select_all(struct nk_text_edit*);
-NK_API nk_bool nk_textedit_cut(struct nk_text_edit*);
-NK_API nk_bool nk_textedit_paste(struct nk_text_edit*, char const*, int len);
-NK_API void nk_textedit_undo(struct nk_text_edit*);
-NK_API void nk_textedit_redo(struct nk_text_edit*);
-
-/* ===============================================================
- *
- * DRAWING
- *
- * ===============================================================*/
-/* This library was designed to be render backend agnostic so it does
- not draw anything to screen. Instead all drawn shapes, widgets
- are made of, are buffered into memory and make up a command queue.
- Each frame therefore fills the command buffer with draw commands
- that then need to be executed by the user and his own render backend.
- After that the command buffer needs to be cleared and a new frame can be
- started. It is probably important to note that the command buffer is the main
- drawing API and the optional vertex buffer API only takes this format and
- converts it into a hardware accessible format.
-
- To use the command queue to draw your own widgets you can access the
- command buffer of each window by calling `nk_window_get_canvas` after
- previously having called `nk_begin`:
-
- void draw_red_rectangle_widget(struct nk_context *ctx)
- {
- struct nk_command_buffer *canvas;
- struct nk_input *input = &ctx->input;
- canvas = nk_window_get_canvas(ctx);
-
- struct nk_rect space;
- enum nk_widget_layout_states state;
- state = nk_widget(&space, ctx);
- if (!state) return;
-
- if (state != NK_WIDGET_ROM)
- update_your_widget_by_user_input(...);
- nk_fill_rect(canvas, space, 0, nk_rgb(255,0,0));
- }
-
- if (nk_begin(...)) {
- nk_layout_row_dynamic(ctx, 25, 1);
- draw_red_rectangle_widget(ctx);
- }
- nk_end(..)
-
- Important to know if you want to create your own widgets is the `nk_widget`
- call. It allocates space on the panel reserved for this widget to be used,
- but also returns the state of the widget space. If your widget is not seen and does
- not have to be updated it is '0' and you can just return. If it only has
- to be drawn the state will be `NK_WIDGET_ROM` otherwise you can do both
- update and draw your widget. The reason for separating is to only draw and
- update what is actually necessary which is crucial for performance.
-*/
-enum nk_command_type {
- NK_COMMAND_NOP,
- NK_COMMAND_SCISSOR,
- NK_COMMAND_LINE,
- NK_COMMAND_CURVE,
- NK_COMMAND_RECT,
- NK_COMMAND_RECT_FILLED,
- NK_COMMAND_RECT_MULTI_COLOR,
- NK_COMMAND_CIRCLE,
- NK_COMMAND_CIRCLE_FILLED,
- NK_COMMAND_ARC,
- NK_COMMAND_ARC_FILLED,
- NK_COMMAND_TRIANGLE,
- NK_COMMAND_TRIANGLE_FILLED,
- NK_COMMAND_POLYGON,
- NK_COMMAND_POLYGON_FILLED,
- NK_COMMAND_POLYLINE,
- NK_COMMAND_TEXT,
- NK_COMMAND_IMAGE,
- NK_COMMAND_CUSTOM
-};
-
-/* command base and header of every command inside the buffer */
-struct nk_command {
- enum nk_command_type type;
- nk_size next;
-#ifdef NK_INCLUDE_COMMAND_USERDATA
- nk_handle userdata;
-#endif
-};
-
-struct nk_command_scissor {
- struct nk_command header;
- short x, y;
- unsigned short w, h;
-};
-
-struct nk_command_line {
- struct nk_command header;
- unsigned short line_thickness;
- struct nk_vec2i begin;
- struct nk_vec2i end;
- struct nk_color color;
-};
-
-struct nk_command_curve {
- struct nk_command header;
- unsigned short line_thickness;
- struct nk_vec2i begin;
- struct nk_vec2i end;
- struct nk_vec2i ctrl[2];
- struct nk_color color;
-};
-
-struct nk_command_rect {
- struct nk_command header;
- unsigned short rounding;
- unsigned short line_thickness;
- short x, y;
- unsigned short w, h;
- struct nk_color color;
-};
-
-struct nk_command_rect_filled {
- struct nk_command header;
- unsigned short rounding;
- short x, y;
- unsigned short w, h;
- struct nk_color color;
-};
-
-struct nk_command_rect_multi_color {
- struct nk_command header;
- short x, y;
- unsigned short w, h;
- struct nk_color left;
- struct nk_color top;
- struct nk_color bottom;
- struct nk_color right;
-};
-
-struct nk_command_triangle {
- struct nk_command header;
- unsigned short line_thickness;
- struct nk_vec2i a;
- struct nk_vec2i b;
- struct nk_vec2i c;
- struct nk_color color;
-};
-
-struct nk_command_triangle_filled {
- struct nk_command header;
- struct nk_vec2i a;
- struct nk_vec2i b;
- struct nk_vec2i c;
- struct nk_color color;
-};
-
-struct nk_command_circle {
- struct nk_command header;
- short x, y;
- unsigned short line_thickness;
- unsigned short w, h;
- struct nk_color color;
-};
-
-struct nk_command_circle_filled {
- struct nk_command header;
- short x, y;
- unsigned short w, h;
- struct nk_color color;
-};
-
-struct nk_command_arc {
- struct nk_command header;
- short cx, cy;
- unsigned short r;
- unsigned short line_thickness;
- float a[2];
- struct nk_color color;
-};
-
-struct nk_command_arc_filled {
- struct nk_command header;
- short cx, cy;
- unsigned short r;
- float a[2];
- struct nk_color color;
-};
-
-struct nk_command_polygon {
- struct nk_command header;
- struct nk_color color;
- unsigned short line_thickness;
- unsigned short point_count;
- struct nk_vec2i points[1];
-};
-
-struct nk_command_polygon_filled {
- struct nk_command header;
- struct nk_color color;
- unsigned short point_count;
- struct nk_vec2i points[1];
-};
-
-struct nk_command_polyline {
- struct nk_command header;
- struct nk_color color;
- unsigned short line_thickness;
- unsigned short point_count;
- struct nk_vec2i points[1];
-};
-
-struct nk_command_image {
- struct nk_command header;
- short x, y;
- unsigned short w, h;
- struct nk_image img;
- struct nk_color col;
-};
-
-typedef void (*nk_command_custom_callback)(void *canvas, short x,short y,
- unsigned short w, unsigned short h, nk_handle callback_data);
-struct nk_command_custom {
- struct nk_command header;
- short x, y;
- unsigned short w, h;
- nk_handle callback_data;
- nk_command_custom_callback callback;
-};
-
-struct nk_command_text {
- struct nk_command header;
- const struct nk_user_font *font;
- struct nk_color background;
- struct nk_color foreground;
- short x, y;
- unsigned short w, h;
- float height;
- int length;
- char string[1];
-};
-
-enum nk_command_clipping {
- NK_CLIPPING_OFF = nk_false,
- NK_CLIPPING_ON = nk_true
-};
-
-struct nk_command_buffer {
- struct nk_buffer *base;
- struct nk_rect clip;
- int use_clipping;
- nk_handle userdata;
- nk_size begin, end, last;
-};
-
-/* shape outlines */
-NK_API void nk_stroke_line(struct nk_command_buffer *b, float x0, float y0, float x1, float y1, float line_thickness, struct nk_color);
-NK_API void nk_stroke_curve(struct nk_command_buffer*, float, float, float, float, float, float, float, float, float line_thickness, struct nk_color);
-NK_API void nk_stroke_rect(struct nk_command_buffer*, struct nk_rect, float rounding, float line_thickness, struct nk_color);
-NK_API void nk_stroke_circle(struct nk_command_buffer*, struct nk_rect, float line_thickness, struct nk_color);
-NK_API void nk_stroke_arc(struct nk_command_buffer*, float cx, float cy, float radius, float a_min, float a_max, float line_thickness, struct nk_color);
-NK_API void nk_stroke_triangle(struct nk_command_buffer*, float, float, float, float, float, float, float line_thichness, struct nk_color);
-NK_API void nk_stroke_polyline(struct nk_command_buffer*, float *points, int point_count, float line_thickness, struct nk_color col);
-NK_API void nk_stroke_polygon(struct nk_command_buffer*, float*, int point_count, float line_thickness, struct nk_color);
-
-/* filled shades */
-NK_API void nk_fill_rect(struct nk_command_buffer*, struct nk_rect, float rounding, struct nk_color);
-NK_API void nk_fill_rect_multi_color(struct nk_command_buffer*, struct nk_rect, struct nk_color left, struct nk_color top, struct nk_color right, struct nk_color bottom);
-NK_API void nk_fill_circle(struct nk_command_buffer*, struct nk_rect, struct nk_color);
-NK_API void nk_fill_arc(struct nk_command_buffer*, float cx, float cy, float radius, float a_min, float a_max, struct nk_color);
-NK_API void nk_fill_triangle(struct nk_command_buffer*, float x0, float y0, float x1, float y1, float x2, float y2, struct nk_color);
-NK_API void nk_fill_polygon(struct nk_command_buffer*, float*, int point_count, struct nk_color);
-
-/* misc */
-NK_API void nk_draw_image(struct nk_command_buffer*, struct nk_rect, const struct nk_image*, struct nk_color);
-NK_API void nk_draw_text(struct nk_command_buffer*, struct nk_rect, const char *text, int len, const struct nk_user_font*, struct nk_color, struct nk_color);
-NK_API void nk_push_scissor(struct nk_command_buffer*, struct nk_rect);
-NK_API void nk_push_custom(struct nk_command_buffer*, struct nk_rect, nk_command_custom_callback, nk_handle usr);
-
-/* ===============================================================
- *
- * INPUT
- *
- * ===============================================================*/
-struct nk_mouse_button {
- nk_bool down;
- unsigned int clicked;
- struct nk_vec2 clicked_pos;
-};
-struct nk_mouse {
- struct nk_mouse_button buttons[NK_BUTTON_MAX];
- struct nk_vec2 pos;
- struct nk_vec2 prev;
- struct nk_vec2 delta;
- struct nk_vec2 scroll_delta;
- unsigned char grab;
- unsigned char grabbed;
- unsigned char ungrab;
-};
-
-struct nk_key {
- nk_bool down;
- unsigned int clicked;
-};
-struct nk_keyboard {
- struct nk_key keys[NK_KEY_MAX];
- char text[NK_INPUT_MAX];
- int text_len;
-};
-
-struct nk_input {
- struct nk_keyboard keyboard;
- struct nk_mouse mouse;
-};
-
-NK_API nk_bool nk_input_has_mouse_click(const struct nk_input*, enum nk_buttons);
-NK_API nk_bool nk_input_has_mouse_click_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect);
-NK_API nk_bool nk_input_has_mouse_click_down_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect, nk_bool down);
-NK_API nk_bool nk_input_is_mouse_click_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect);
-NK_API nk_bool nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, struct nk_rect b, nk_bool down);
-NK_API nk_bool nk_input_any_mouse_click_in_rect(const struct nk_input*, struct nk_rect);
-NK_API nk_bool nk_input_is_mouse_prev_hovering_rect(const struct nk_input*, struct nk_rect);
-NK_API nk_bool nk_input_is_mouse_hovering_rect(const struct nk_input*, struct nk_rect);
-NK_API nk_bool nk_input_mouse_clicked(const struct nk_input*, enum nk_buttons, struct nk_rect);
-NK_API nk_bool nk_input_is_mouse_down(const struct nk_input*, enum nk_buttons);
-NK_API nk_bool nk_input_is_mouse_pressed(const struct nk_input*, enum nk_buttons);
-NK_API nk_bool nk_input_is_mouse_released(const struct nk_input*, enum nk_buttons);
-NK_API nk_bool nk_input_is_key_pressed(const struct nk_input*, enum nk_keys);
-NK_API nk_bool nk_input_is_key_released(const struct nk_input*, enum nk_keys);
-NK_API nk_bool nk_input_is_key_down(const struct nk_input*, enum nk_keys);
-
-/* ===============================================================
- *
- * DRAW LIST
- *
- * ===============================================================*/
-#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT
-/* The optional vertex buffer draw list provides a 2D drawing context
- with antialiasing functionality which takes basic filled or outlined shapes
- or a path and outputs vertexes, elements and draw commands.
- The actual draw list API is not required to be used directly while using this
- library since converting the default library draw command output is done by
- just calling `nk_convert` but I decided to still make this library accessible
- since it can be useful.
-
- The draw list is based on a path buffering and polygon and polyline
- rendering API which allows a lot of ways to draw 2D content to screen.
- In fact it is probably more powerful than needed but allows even more crazy
- things than this library provides by default.
-*/
-#ifdef NK_UINT_DRAW_INDEX
-typedef nk_uint nk_draw_index;
-#else
-typedef nk_ushort nk_draw_index;
-#endif
-enum nk_draw_list_stroke {
- NK_STROKE_OPEN = nk_false,
- /* build up path has no connection back to the beginning */
- NK_STROKE_CLOSED = nk_true
- /* build up path has a connection back to the beginning */
-};
-
-enum nk_draw_vertex_layout_attribute {
- NK_VERTEX_POSITION,
- NK_VERTEX_COLOR,
- NK_VERTEX_TEXCOORD,
- NK_VERTEX_ATTRIBUTE_COUNT
-};
-
-enum nk_draw_vertex_layout_format {
- NK_FORMAT_SCHAR,
- NK_FORMAT_SSHORT,
- NK_FORMAT_SINT,
- NK_FORMAT_UCHAR,
- NK_FORMAT_USHORT,
- NK_FORMAT_UINT,
- NK_FORMAT_FLOAT,
- NK_FORMAT_DOUBLE,
-
-NK_FORMAT_COLOR_BEGIN,
- NK_FORMAT_R8G8B8 = NK_FORMAT_COLOR_BEGIN,
- NK_FORMAT_R16G15B16,
- NK_FORMAT_R32G32B32,
-
- NK_FORMAT_R8G8B8A8,
- NK_FORMAT_B8G8R8A8,
- NK_FORMAT_R16G15B16A16,
- NK_FORMAT_R32G32B32A32,
- NK_FORMAT_R32G32B32A32_FLOAT,
- NK_FORMAT_R32G32B32A32_DOUBLE,
-
- NK_FORMAT_RGB32,
- NK_FORMAT_RGBA32,
-NK_FORMAT_COLOR_END = NK_FORMAT_RGBA32,
- NK_FORMAT_COUNT
-};
-
-#define NK_VERTEX_LAYOUT_END NK_VERTEX_ATTRIBUTE_COUNT,NK_FORMAT_COUNT,0
-struct nk_draw_vertex_layout_element {
- enum nk_draw_vertex_layout_attribute attribute;
- enum nk_draw_vertex_layout_format format;
- nk_size offset;
-};
-
-struct nk_draw_command {
- unsigned int elem_count;
- /* number of elements in the current draw batch */
- struct nk_rect clip_rect;
- /* current screen clipping rectangle */
- nk_handle texture;
- /* current texture to set */
-#ifdef NK_INCLUDE_COMMAND_USERDATA
- nk_handle userdata;
-#endif
-};
-
-struct nk_draw_list {
- struct nk_rect clip_rect;
- struct nk_vec2 circle_vtx[12];
- struct nk_convert_config config;
-
- struct nk_buffer *buffer;
- struct nk_buffer *vertices;
- struct nk_buffer *elements;
-
- unsigned int element_count;
- unsigned int vertex_count;
- unsigned int cmd_count;
- nk_size cmd_offset;
-
- unsigned int path_count;
- unsigned int path_offset;
-
- enum nk_anti_aliasing line_AA;
- enum nk_anti_aliasing shape_AA;
-
-#ifdef NK_INCLUDE_COMMAND_USERDATA
- nk_handle userdata;
-#endif
-};
-
-/* draw list */
-NK_API void nk_draw_list_init(struct nk_draw_list*);
-NK_API void nk_draw_list_setup(struct nk_draw_list*, const struct nk_convert_config*, struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, enum nk_anti_aliasing line_aa,enum nk_anti_aliasing shape_aa);
-
-/* drawing */
-#define nk_draw_list_foreach(cmd, can, b) for((cmd)=nk__draw_list_begin(can, b); (cmd)!=0; (cmd)=nk__draw_list_next(cmd, b, can))
-NK_API const struct nk_draw_command* nk__draw_list_begin(const struct nk_draw_list*, const struct nk_buffer*);
-NK_API const struct nk_draw_command* nk__draw_list_next(const struct nk_draw_command*, const struct nk_buffer*, const struct nk_draw_list*);
-NK_API const struct nk_draw_command* nk__draw_list_end(const struct nk_draw_list*, const struct nk_buffer*);
-
-/* path */
-NK_API void nk_draw_list_path_clear(struct nk_draw_list*);
-NK_API void nk_draw_list_path_line_to(struct nk_draw_list*, struct nk_vec2 pos);
-NK_API void nk_draw_list_path_arc_to_fast(struct nk_draw_list*, struct nk_vec2 center, float radius, int a_min, int a_max);
-NK_API void nk_draw_list_path_arc_to(struct nk_draw_list*, struct nk_vec2 center, float radius, float a_min, float a_max, unsigned int segments);
-NK_API void nk_draw_list_path_rect_to(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, float rounding);
-NK_API void nk_draw_list_path_curve_to(struct nk_draw_list*, struct nk_vec2 p2, struct nk_vec2 p3, struct nk_vec2 p4, unsigned int num_segments);
-NK_API void nk_draw_list_path_fill(struct nk_draw_list*, struct nk_color);
-NK_API void nk_draw_list_path_stroke(struct nk_draw_list*, struct nk_color, enum nk_draw_list_stroke closed, float thickness);
-
-/* stroke */
-NK_API void nk_draw_list_stroke_line(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, struct nk_color, float thickness);
-NK_API void nk_draw_list_stroke_rect(struct nk_draw_list*, struct nk_rect rect, struct nk_color, float rounding, float thickness);
-NK_API void nk_draw_list_stroke_triangle(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, struct nk_vec2 c, struct nk_color, float thickness);
-NK_API void nk_draw_list_stroke_circle(struct nk_draw_list*, struct nk_vec2 center, float radius, struct nk_color, unsigned int segs, float thickness);
-NK_API void nk_draw_list_stroke_curve(struct nk_draw_list*, struct nk_vec2 p0, struct nk_vec2 cp0, struct nk_vec2 cp1, struct nk_vec2 p1, struct nk_color, unsigned int segments, float thickness);
-NK_API void nk_draw_list_stroke_poly_line(struct nk_draw_list*, const struct nk_vec2 *pnts, const unsigned int cnt, struct nk_color, enum nk_draw_list_stroke, float thickness, enum nk_anti_aliasing);
-
-/* fill */
-NK_API void nk_draw_list_fill_rect(struct nk_draw_list*, struct nk_rect rect, struct nk_color, float rounding);
-NK_API void nk_draw_list_fill_rect_multi_color(struct nk_draw_list*, struct nk_rect rect, struct nk_color left, struct nk_color top, struct nk_color right, struct nk_color bottom);
-NK_API void nk_draw_list_fill_triangle(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, struct nk_vec2 c, struct nk_color);
-NK_API void nk_draw_list_fill_circle(struct nk_draw_list*, struct nk_vec2 center, float radius, struct nk_color col, unsigned int segs);
-NK_API void nk_draw_list_fill_poly_convex(struct nk_draw_list*, const struct nk_vec2 *points, const unsigned int count, struct nk_color, enum nk_anti_aliasing);
-
-/* misc */
-NK_API void nk_draw_list_add_image(struct nk_draw_list*, struct nk_image texture, struct nk_rect rect, struct nk_color);
-NK_API void nk_draw_list_add_text(struct nk_draw_list*, const struct nk_user_font*, struct nk_rect, const char *text, int len, float font_height, struct nk_color);
-#ifdef NK_INCLUDE_COMMAND_USERDATA
-NK_API void nk_draw_list_push_userdata(struct nk_draw_list*, nk_handle userdata);
-#endif
-
-#endif
-
-/* ===============================================================
- *
- * GUI
- *
- * ===============================================================*/
-enum nk_style_item_type {
- NK_STYLE_ITEM_COLOR,
- NK_STYLE_ITEM_IMAGE
-};
-
-union nk_style_item_data {
- struct nk_image image;
- struct nk_color color;
-};
-
-struct nk_style_item {
- enum nk_style_item_type type;
- union nk_style_item_data data;
-};
-
-struct nk_style_text {
- struct nk_color color;
- struct nk_vec2 padding;
-};
-
-struct nk_style_button {
- /* background */
- struct nk_style_item normal;
- struct nk_style_item hover;
- struct nk_style_item active;
- struct nk_color border_color;
-
- /* text */
- struct nk_color text_background;
- struct nk_color text_normal;
- struct nk_color text_hover;
- struct nk_color text_active;
- nk_flags text_alignment;
-
- /* properties */
- float border;
- float rounding;
- struct nk_vec2 padding;
- struct nk_vec2 image_padding;
- struct nk_vec2 touch_padding;
-
- /* optional user callbacks */
- nk_handle userdata;
- void(*draw_begin)(struct nk_command_buffer*, nk_handle userdata);
- void(*draw_end)(struct nk_command_buffer*, nk_handle userdata);
-};
-
-struct nk_style_toggle {
- /* background */
- struct nk_style_item normal;
- struct nk_style_item hover;
- struct nk_style_item active;
- struct nk_color border_color;
-
- /* cursor */
- struct nk_style_item cursor_normal;
- struct nk_style_item cursor_hover;
-
- /* text */
- struct nk_color text_normal;
- struct nk_color text_hover;
- struct nk_color text_active;
- struct nk_color text_background;
- nk_flags text_alignment;
-
- /* properties */
- struct nk_vec2 padding;
- struct nk_vec2 touch_padding;
- float spacing;
- float border;
-
- /* optional user callbacks */
- nk_handle userdata;
- void(*draw_begin)(struct nk_command_buffer*, nk_handle);
- void(*draw_end)(struct nk_command_buffer*, nk_handle);
-};
-
-struct nk_style_selectable {
- /* background (inactive) */
- struct nk_style_item normal;
- struct nk_style_item hover;
- struct nk_style_item pressed;
-
- /* background (active) */
- struct nk_style_item normal_active;
- struct nk_style_item hover_active;
- struct nk_style_item pressed_active;
-
- /* text color (inactive) */
- struct nk_color text_normal;
- struct nk_color text_hover;
- struct nk_color text_pressed;
-
- /* text color (active) */
- struct nk_color text_normal_active;
- struct nk_color text_hover_active;
- struct nk_color text_pressed_active;
- struct nk_color text_background;
- nk_flags text_alignment;
-
- /* properties */
- float rounding;
- struct nk_vec2 padding;
- struct nk_vec2 touch_padding;
- struct nk_vec2 image_padding;
-
- /* optional user callbacks */
- nk_handle userdata;
- void(*draw_begin)(struct nk_command_buffer*, nk_handle);
- void(*draw_end)(struct nk_command_buffer*, nk_handle);
-};
-
-struct nk_style_slider {
- /* background */
- struct nk_style_item normal;
- struct nk_style_item hover;
- struct nk_style_item active;
- struct nk_color border_color;
-
- /* background bar */
- struct nk_color bar_normal;
- struct nk_color bar_hover;
- struct nk_color bar_active;
- struct nk_color bar_filled;
-
- /* cursor */
- struct nk_style_item cursor_normal;
- struct nk_style_item cursor_hover;
- struct nk_style_item cursor_active;
-
- /* properties */
- float border;
- float rounding;
- float bar_height;
- struct nk_vec2 padding;
- struct nk_vec2 spacing;
- struct nk_vec2 cursor_size;
-
- /* optional buttons */
- int show_buttons;
- struct nk_style_button inc_button;
- struct nk_style_button dec_button;
- enum nk_symbol_type inc_symbol;
- enum nk_symbol_type dec_symbol;
-
- /* optional user callbacks */
- nk_handle userdata;
- void(*draw_begin)(struct nk_command_buffer*, nk_handle);
- void(*draw_end)(struct nk_command_buffer*, nk_handle);
-};
-
-struct nk_style_progress {
- /* background */
- struct nk_style_item normal;
- struct nk_style_item hover;
- struct nk_style_item active;
- struct nk_color border_color;
-
- /* cursor */
- struct nk_style_item cursor_normal;
- struct nk_style_item cursor_hover;
- struct nk_style_item cursor_active;
- struct nk_color cursor_border_color;
-
- /* properties */
- float rounding;
- float border;
- float cursor_border;
- float cursor_rounding;
- struct nk_vec2 padding;
-
- /* optional user callbacks */
- nk_handle userdata;
- void(*draw_begin)(struct nk_command_buffer*, nk_handle);
- void(*draw_end)(struct nk_command_buffer*, nk_handle);
-};
-
-struct nk_style_scrollbar {
- /* background */
- struct nk_style_item normal;
- struct nk_style_item hover;
- struct nk_style_item active;
- struct nk_color border_color;
-
- /* cursor */
- struct nk_style_item cursor_normal;
- struct nk_style_item cursor_hover;
- struct nk_style_item cursor_active;
- struct nk_color cursor_border_color;
-
- /* properties */
- float border;
- float rounding;
- float border_cursor;
- float rounding_cursor;
- struct nk_vec2 padding;
-
- /* optional buttons */
- int show_buttons;
- struct nk_style_button inc_button;
- struct nk_style_button dec_button;
- enum nk_symbol_type inc_symbol;
- enum nk_symbol_type dec_symbol;
-
- /* optional user callbacks */
- nk_handle userdata;
- void(*draw_begin)(struct nk_command_buffer*, nk_handle);
- void(*draw_end)(struct nk_command_buffer*, nk_handle);
-};
-
-struct nk_style_edit {
- /* background */
- struct nk_style_item normal;
- struct nk_style_item hover;
- struct nk_style_item active;
- struct nk_color border_color;
- struct nk_style_scrollbar scrollbar;
-
- /* cursor */
- struct nk_color cursor_normal;
- struct nk_color cursor_hover;
- struct nk_color cursor_text_normal;
- struct nk_color cursor_text_hover;
-
- /* text (unselected) */
- struct nk_color text_normal;
- struct nk_color text_hover;
- struct nk_color text_active;
-
- /* text (selected) */
- struct nk_color selected_normal;
- struct nk_color selected_hover;
- struct nk_color selected_text_normal;
- struct nk_color selected_text_hover;
-
- /* properties */
- float border;
- float rounding;
- float cursor_size;
- struct nk_vec2 scrollbar_size;
- struct nk_vec2 padding;
- float row_padding;
-};
-
-struct nk_style_property {
- /* background */
- struct nk_style_item normal;
- struct nk_style_item hover;
- struct nk_style_item active;
- struct nk_color border_color;
-
- /* text */
- struct nk_color label_normal;
- struct nk_color label_hover;
- struct nk_color label_active;
-
- /* symbols */
- enum nk_symbol_type sym_left;
- enum nk_symbol_type sym_right;
-
- /* properties */
- float border;
- float rounding;
- struct nk_vec2 padding;
-
- struct nk_style_edit edit;
- struct nk_style_button inc_button;
- struct nk_style_button dec_button;
-
- /* optional user callbacks */
- nk_handle userdata;
- void(*draw_begin)(struct nk_command_buffer*, nk_handle);
- void(*draw_end)(struct nk_command_buffer*, nk_handle);
-};
-
-struct nk_style_chart {
- /* colors */
- struct nk_style_item background;
- struct nk_color border_color;
- struct nk_color selected_color;
- struct nk_color color;
-
- /* properties */
- float border;
- float rounding;
- struct nk_vec2 padding;
-};
-
-struct nk_style_combo {
- /* background */
- struct nk_style_item normal;
- struct nk_style_item hover;
- struct nk_style_item active;
- struct nk_color border_color;
-
- /* label */
- struct nk_color label_normal;
- struct nk_color label_hover;
- struct nk_color label_active;
-
- /* symbol */
- struct nk_color symbol_normal;
- struct nk_color symbol_hover;
- struct nk_color symbol_active;
-
- /* button */
- struct nk_style_button button;
- enum nk_symbol_type sym_normal;
- enum nk_symbol_type sym_hover;
- enum nk_symbol_type sym_active;
-
- /* properties */
- float border;
- float rounding;
- struct nk_vec2 content_padding;
- struct nk_vec2 button_padding;
- struct nk_vec2 spacing;
-};
-
-struct nk_style_tab {
- /* background */
- struct nk_style_item background;
- struct nk_color border_color;
- struct nk_color text;
-
- /* button */
- struct nk_style_button tab_maximize_button;
- struct nk_style_button tab_minimize_button;
- struct nk_style_button node_maximize_button;
- struct nk_style_button node_minimize_button;
- enum nk_symbol_type sym_minimize;
- enum nk_symbol_type sym_maximize;
-
- /* properties */
- float border;
- float rounding;
- float indent;
- struct nk_vec2 padding;
- struct nk_vec2 spacing;
-};
-
-enum nk_style_header_align {
- NK_HEADER_LEFT,
- NK_HEADER_RIGHT
-};
-struct nk_style_window_header {
- /* background */
- struct nk_style_item normal;
- struct nk_style_item hover;
- struct nk_style_item active;
-
- /* button */
- struct nk_style_button close_button;
- struct nk_style_button minimize_button;
- enum nk_symbol_type close_symbol;
- enum nk_symbol_type minimize_symbol;
- enum nk_symbol_type maximize_symbol;
-
- /* title */
- struct nk_color label_normal;
- struct nk_color label_hover;
- struct nk_color label_active;
-
- /* properties */
- enum nk_style_header_align align;
- struct nk_vec2 padding;
- struct nk_vec2 label_padding;
- struct nk_vec2 spacing;
-};
-
-struct nk_style_window {
- struct nk_style_window_header header;
- struct nk_style_item fixed_background;
- struct nk_color background;
-
- struct nk_color border_color;
- struct nk_color popup_border_color;
- struct nk_color combo_border_color;
- struct nk_color contextual_border_color;
- struct nk_color menu_border_color;
- struct nk_color group_border_color;
- struct nk_color tooltip_border_color;
- struct nk_style_item scaler;
-
- float border;
- float combo_border;
- float contextual_border;
- float menu_border;
- float group_border;
- float tooltip_border;
- float popup_border;
- float min_row_height_padding;
-
- float rounding;
- struct nk_vec2 spacing;
- struct nk_vec2 scrollbar_size;
- struct nk_vec2 min_size;
-
- struct nk_vec2 padding;
- struct nk_vec2 group_padding;
- struct nk_vec2 popup_padding;
- struct nk_vec2 combo_padding;
- struct nk_vec2 contextual_padding;
- struct nk_vec2 menu_padding;
- struct nk_vec2 tooltip_padding;
-};
-
-struct nk_style {
- const struct nk_user_font *font;
- const struct nk_cursor *cursors[NK_CURSOR_COUNT];
- const struct nk_cursor *cursor_active;
- struct nk_cursor *cursor_last;
- int cursor_visible;
-
- struct nk_style_text text;
- struct nk_style_button button;
- struct nk_style_button contextual_button;
- struct nk_style_button menu_button;
- struct nk_style_toggle option;
- struct nk_style_toggle checkbox;
- struct nk_style_selectable selectable;
- struct nk_style_slider slider;
- struct nk_style_progress progress;
- struct nk_style_property property;
- struct nk_style_edit edit;
- struct nk_style_chart chart;
- struct nk_style_scrollbar scrollh;
- struct nk_style_scrollbar scrollv;
- struct nk_style_tab tab;
- struct nk_style_combo combo;
- struct nk_style_window window;
-};
-
-NK_API struct nk_style_item nk_style_item_image(struct nk_image img);
-NK_API struct nk_style_item nk_style_item_color(struct nk_color);
-NK_API struct nk_style_item nk_style_item_hide(void);
-
-/*==============================================================
- * PANEL
- * =============================================================*/
-#ifndef NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS
-#define NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS 16
-#endif
-#ifndef NK_CHART_MAX_SLOT
-#define NK_CHART_MAX_SLOT 4
-#endif
-
-enum nk_panel_type {
- NK_PANEL_NONE = 0,
- NK_PANEL_WINDOW = NK_FLAG(0),
- NK_PANEL_GROUP = NK_FLAG(1),
- NK_PANEL_POPUP = NK_FLAG(2),
- NK_PANEL_CONTEXTUAL = NK_FLAG(4),
- NK_PANEL_COMBO = NK_FLAG(5),
- NK_PANEL_MENU = NK_FLAG(6),
- NK_PANEL_TOOLTIP = NK_FLAG(7)
-};
-enum nk_panel_set {
- NK_PANEL_SET_NONBLOCK = NK_PANEL_CONTEXTUAL|NK_PANEL_COMBO|NK_PANEL_MENU|NK_PANEL_TOOLTIP,
- NK_PANEL_SET_POPUP = NK_PANEL_SET_NONBLOCK|NK_PANEL_POPUP,
- NK_PANEL_SET_SUB = NK_PANEL_SET_POPUP|NK_PANEL_GROUP
-};
-
-struct nk_chart_slot {
- enum nk_chart_type type;
- struct nk_color color;
- struct nk_color highlight;
- float min, max, range;
- int count;
- struct nk_vec2 last;
- int index;
-};
-
-struct nk_chart {
- int slot;
- float x, y, w, h;
- struct nk_chart_slot slots[NK_CHART_MAX_SLOT];
-};
-
-enum nk_panel_row_layout_type {
- NK_LAYOUT_DYNAMIC_FIXED = 0,
- NK_LAYOUT_DYNAMIC_ROW,
- NK_LAYOUT_DYNAMIC_FREE,
- NK_LAYOUT_DYNAMIC,
- NK_LAYOUT_STATIC_FIXED,
- NK_LAYOUT_STATIC_ROW,
- NK_LAYOUT_STATIC_FREE,
- NK_LAYOUT_STATIC,
- NK_LAYOUT_TEMPLATE,
- NK_LAYOUT_COUNT
-};
-struct nk_row_layout {
- enum nk_panel_row_layout_type type;
- int index;
- float height;
- float min_height;
- int columns;
- const float *ratio;
- float item_width;
- float item_height;
- float item_offset;
- float filled;
- struct nk_rect item;
- int tree_depth;
- float templates[NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS];
-};
-
-struct nk_popup_buffer {
- nk_size begin;
- nk_size parent;
- nk_size last;
- nk_size end;
- nk_bool active;
-};
-
-struct nk_menu_state {
- float x, y, w, h;
- struct nk_scroll offset;
-};
-
-struct nk_panel {
- enum nk_panel_type type;
- nk_flags flags;
- struct nk_rect bounds;
- nk_uint *offset_x;
- nk_uint *offset_y;
- float at_x, at_y, max_x;
- float footer_height;
- float header_height;
- float border;
- unsigned int has_scrolling;
- struct nk_rect clip;
- struct nk_menu_state menu;
- struct nk_row_layout row;
- struct nk_chart chart;
- struct nk_command_buffer *buffer;
- struct nk_panel *parent;
-};
-
-/*==============================================================
- * WINDOW
- * =============================================================*/
-#ifndef NK_WINDOW_MAX_NAME
-#define NK_WINDOW_MAX_NAME 64
-#endif
-
-struct nk_table;
-enum nk_window_flags {
- NK_WINDOW_PRIVATE = NK_FLAG(11),
- NK_WINDOW_DYNAMIC = NK_WINDOW_PRIVATE,
- /* special window type growing up in height while being filled to a certain maximum height */
- NK_WINDOW_ROM = NK_FLAG(12),
- /* sets window widgets into a read only mode and does not allow input changes */
- NK_WINDOW_NOT_INTERACTIVE = NK_WINDOW_ROM|NK_WINDOW_NO_INPUT,
- /* prevents all interaction caused by input to either window or widgets inside */
- NK_WINDOW_HIDDEN = NK_FLAG(13),
- /* Hides window and stops any window interaction and drawing */
- NK_WINDOW_CLOSED = NK_FLAG(14),
- /* Directly closes and frees the window at the end of the frame */
- NK_WINDOW_MINIMIZED = NK_FLAG(15),
- /* marks the window as minimized */
- NK_WINDOW_REMOVE_ROM = NK_FLAG(16)
- /* Removes read only mode at the end of the window */
-};
-
-struct nk_popup_state {
- struct nk_window *win;
- enum nk_panel_type type;
- struct nk_popup_buffer buf;
- nk_hash name;
- nk_bool active;
- unsigned combo_count;
- unsigned con_count, con_old;
- unsigned active_con;
- struct nk_rect header;
-};
-
-struct nk_edit_state {
- nk_hash name;
- unsigned int seq;
- unsigned int old;
- int active, prev;
- int cursor;
- int sel_start;
- int sel_end;
- struct nk_scroll scrollbar;
- unsigned char mode;
- unsigned char single_line;
-};
-
-struct nk_property_state {
- int active, prev;
- char buffer[NK_MAX_NUMBER_BUFFER];
- int length;
- int cursor;
- int select_start;
- int select_end;
- nk_hash name;
- unsigned int seq;
- unsigned int old;
- int state;
-};
-
-struct nk_window {
- unsigned int seq;
- nk_hash name;
- char name_string[NK_WINDOW_MAX_NAME];
- nk_flags flags;
-
- struct nk_rect bounds;
- struct nk_scroll scrollbar;
- struct nk_command_buffer buffer;
- struct nk_panel *layout;
- float scrollbar_hiding_timer;
-
- /* persistent widget state */
- struct nk_property_state property;
- struct nk_popup_state popup;
- struct nk_edit_state edit;
- unsigned int scrolled;
-
- struct nk_table *tables;
- unsigned int table_count;
-
- /* window list hooks */
- struct nk_window *next;
- struct nk_window *prev;
- struct nk_window *parent;
-};
-
-/*==============================================================
- * STACK
- * =============================================================*/
-/* The style modifier stack can be used to temporarily change a
- * property inside `nk_style`. For example if you want a special
- * red button you can temporarily push the old button color onto a stack
- * draw the button with a red color and then you just pop the old color
- * back from the stack:
- *
- * nk_style_push_style_item(ctx, &ctx->style.button.normal, nk_style_item_color(nk_rgb(255,0,0)));
- * nk_style_push_style_item(ctx, &ctx->style.button.hover, nk_style_item_color(nk_rgb(255,0,0)));
- * nk_style_push_style_item(ctx, &ctx->style.button.active, nk_style_item_color(nk_rgb(255,0,0)));
- * nk_style_push_vec2(ctx, &cx->style.button.padding, nk_vec2(2,2));
- *
- * nk_button(...);
- *
- * nk_style_pop_style_item(ctx);
- * nk_style_pop_style_item(ctx);
- * nk_style_pop_style_item(ctx);
- * nk_style_pop_vec2(ctx);
- *
- * Nuklear has a stack for style_items, float properties, vector properties,
- * flags, colors, fonts and for button_behavior. Each has it's own fixed size stack
- * which can be changed at compile time.
- */
-#ifndef NK_BUTTON_BEHAVIOR_STACK_SIZE
-#define NK_BUTTON_BEHAVIOR_STACK_SIZE 8
-#endif
-
-#ifndef NK_FONT_STACK_SIZE
-#define NK_FONT_STACK_SIZE 8
-#endif
-
-#ifndef NK_STYLE_ITEM_STACK_SIZE
-#define NK_STYLE_ITEM_STACK_SIZE 16
-#endif
-
-#ifndef NK_FLOAT_STACK_SIZE
-#define NK_FLOAT_STACK_SIZE 32
-#endif
-
-#ifndef NK_VECTOR_STACK_SIZE
-#define NK_VECTOR_STACK_SIZE 16
-#endif
-
-#ifndef NK_FLAGS_STACK_SIZE
-#define NK_FLAGS_STACK_SIZE 32
-#endif
-
-#ifndef NK_COLOR_STACK_SIZE
-#define NK_COLOR_STACK_SIZE 32
-#endif
-
-#define NK_CONFIGURATION_STACK_TYPE(prefix, name, type)\
- struct nk_config_stack_##name##_element {\
- prefix##_##type *address;\
- prefix##_##type old_value;\
- }
-#define NK_CONFIG_STACK(type,size)\
- struct nk_config_stack_##type {\
- int head;\
- struct nk_config_stack_##type##_element elements[size];\
- }
-
-#define nk_float float
-NK_CONFIGURATION_STACK_TYPE(struct nk, style_item, style_item);
-NK_CONFIGURATION_STACK_TYPE(nk ,float, float);
-NK_CONFIGURATION_STACK_TYPE(struct nk, vec2, vec2);
-NK_CONFIGURATION_STACK_TYPE(nk ,flags, flags);
-NK_CONFIGURATION_STACK_TYPE(struct nk, color, color);
-NK_CONFIGURATION_STACK_TYPE(const struct nk, user_font, user_font*);
-NK_CONFIGURATION_STACK_TYPE(enum nk, button_behavior, button_behavior);
-
-NK_CONFIG_STACK(style_item, NK_STYLE_ITEM_STACK_SIZE);
-NK_CONFIG_STACK(float, NK_FLOAT_STACK_SIZE);
-NK_CONFIG_STACK(vec2, NK_VECTOR_STACK_SIZE);
-NK_CONFIG_STACK(flags, NK_FLAGS_STACK_SIZE);
-NK_CONFIG_STACK(color, NK_COLOR_STACK_SIZE);
-NK_CONFIG_STACK(user_font, NK_FONT_STACK_SIZE);
-NK_CONFIG_STACK(button_behavior, NK_BUTTON_BEHAVIOR_STACK_SIZE);
-
-struct nk_configuration_stacks {
- struct nk_config_stack_style_item style_items;
- struct nk_config_stack_float floats;
- struct nk_config_stack_vec2 vectors;
- struct nk_config_stack_flags flags;
- struct nk_config_stack_color colors;
- struct nk_config_stack_user_font fonts;
- struct nk_config_stack_button_behavior button_behaviors;
-};
-
-/*==============================================================
- * CONTEXT
- * =============================================================*/
-#define NK_VALUE_PAGE_CAPACITY \
- (((NK_MAX(sizeof(struct nk_window),sizeof(struct nk_panel)) / sizeof(nk_uint))) / 2)
-
-struct nk_table {
- unsigned int seq;
- unsigned int size;
- nk_hash keys[NK_VALUE_PAGE_CAPACITY];
- nk_uint values[NK_VALUE_PAGE_CAPACITY];
- struct nk_table *next, *prev;
-};
-
-union nk_page_data {
- struct nk_table tbl;
- struct nk_panel pan;
- struct nk_window win;
-};
-
-struct nk_page_element {
- union nk_page_data data;
- struct nk_page_element *next;
- struct nk_page_element *prev;
-};
-
-struct nk_page {
- unsigned int size;
- struct nk_page *next;
- struct nk_page_element win[1];
-};
-
-struct nk_pool {
- struct nk_allocator alloc;
- enum nk_allocation_type type;
- unsigned int page_count;
- struct nk_page *pages;
- struct nk_page_element *freelist;
- unsigned capacity;
- nk_size size;
- nk_size cap;
-};
-
-struct nk_context {
-/* public: can be accessed freely */
- struct nk_input input;
- struct nk_style style;
- struct nk_buffer memory;
- struct nk_clipboard clip;
- nk_flags last_widget_state;
- enum nk_button_behavior button_behavior;
- struct nk_configuration_stacks stacks;
- float delta_time_seconds;
-
-/* private:
- should only be accessed if you
- know what you are doing */
-#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT
- struct nk_draw_list draw_list;
-#endif
-#ifdef NK_INCLUDE_COMMAND_USERDATA
- nk_handle userdata;
-#endif
- /* text editor objects are quite big because of an internal
- * undo/redo stack. Therefore it does not make sense to have one for
- * each window for temporary use cases, so I only provide *one* instance
- * for all windows. This works because the content is cleared anyway */
- struct nk_text_edit text_edit;
- /* draw buffer used for overlay drawing operation like cursor */
- struct nk_command_buffer overlay;
-
- /* windows */
- int build;
- int use_pool;
- struct nk_pool pool;
- struct nk_window *begin;
- struct nk_window *end;
- struct nk_window *active;
- struct nk_window *current;
- struct nk_page_element *freelist;
- unsigned int count;
- unsigned int seq;
-};
-
-/* ==============================================================
- * MATH
- * =============================================================== */
-#define NK_PI 3.141592654f
-#define NK_UTF_INVALID 0xFFFD
-#define NK_MAX_FLOAT_PRECISION 2
-
-#define NK_UNUSED(x) ((void)(x))
-#define NK_SATURATE(x) (NK_MAX(0, NK_MIN(1.0f, x)))
-#define NK_LEN(a) (sizeof(a)/sizeof(a)[0])
-#define NK_ABS(a) (((a) < 0) ? -(a) : (a))
-#define NK_BETWEEN(x, a, b) ((a) <= (x) && (x) < (b))
-#define NK_INBOX(px, py, x, y, w, h)\
- (NK_BETWEEN(px,x,x+w) && NK_BETWEEN(py,y,y+h))
-#define NK_INTERSECT(x0, y0, w0, h0, x1, y1, w1, h1) \
- ((x1 < (x0 + w0)) && (x0 < (x1 + w1)) && \
- (y1 < (y0 + h0)) && (y0 < (y1 + h1)))
-#define NK_CONTAINS(x, y, w, h, bx, by, bw, bh)\
- (NK_INBOX(x,y, bx, by, bw, bh) && NK_INBOX(x+w,y+h, bx, by, bw, bh))
-
-#define nk_vec2_sub(a, b) nk_vec2((a).x - (b).x, (a).y - (b).y)
-#define nk_vec2_add(a, b) nk_vec2((a).x + (b).x, (a).y + (b).y)
-#define nk_vec2_len_sqr(a) ((a).x*(a).x+(a).y*(a).y)
-#define nk_vec2_muls(a, t) nk_vec2((a).x * (t), (a).y * (t))
-
-#define nk_ptr_add(t, p, i) ((t*)((void*)((nk_byte*)(p) + (i))))
-#define nk_ptr_add_const(t, p, i) ((const t*)((const void*)((const nk_byte*)(p) + (i))))
-#define nk_zero_struct(s) nk_zero(&s, sizeof(s))
-
-/* ==============================================================
- * ALIGNMENT
- * =============================================================== */
-/* Pointer to Integer type conversion for pointer alignment */
-#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC*/
-# define NK_UINT_TO_PTR(x) ((void*)(__PTRDIFF_TYPE__)(x))
-# define NK_PTR_TO_UINT(x) ((nk_size)(__PTRDIFF_TYPE__)(x))
-#elif !defined(__GNUC__) /* works for compilers other than LLVM */
-# define NK_UINT_TO_PTR(x) ((void*)&((char*)0)[x])
-# define NK_PTR_TO_UINT(x) ((nk_size)(((char*)x)-(char*)0))
-#elif defined(NK_USE_FIXED_TYPES) /* used if we have */
-# define NK_UINT_TO_PTR(x) ((void*)(uintptr_t)(x))
-# define NK_PTR_TO_UINT(x) ((uintptr_t)(x))
-#else /* generates warning but works */
-# define NK_UINT_TO_PTR(x) ((void*)(x))
-# define NK_PTR_TO_UINT(x) ((nk_size)(x))
-#endif
-
-#define NK_ALIGN_PTR(x, mask)\
- (NK_UINT_TO_PTR((NK_PTR_TO_UINT((nk_byte*)(x) + (mask-1)) & ~(mask-1))))
-#define NK_ALIGN_PTR_BACK(x, mask)\
- (NK_UINT_TO_PTR((NK_PTR_TO_UINT((nk_byte*)(x)) & ~(mask-1))))
-
-#define NK_OFFSETOF(st,m) ((nk_ptr)&(((st*)0)->m))
-#define NK_CONTAINER_OF(ptr,type,member)\
- (type*)((void*)((char*)(1 ? (ptr): &((type*)0)->member) - NK_OFFSETOF(type, member)))
-
-#ifdef __cplusplus
-}
-#endif
-
-#ifdef __cplusplus
-template struct nk_alignof;
-template struct nk_helper{enum {value = size_diff};};
-template struct nk_helper{enum {value = nk_alignof::value};};
-template struct nk_alignof{struct Big {T x; char c;}; enum {
- diff = sizeof(Big) - sizeof(T), value = nk_helper::value};};
-#define NK_ALIGNOF(t) (nk_alignof::value)
-#elif defined(_MSC_VER)
-#define NK_ALIGNOF(t) (__alignof(t))
-#else
-#define NK_ALIGNOF(t) ((char*)(&((struct {char c; t _h;}*)0)->_h) - (char*)0)
-#endif
-
-#endif /* NK_NUKLEAR_H_ */
-
-#ifdef NK_IMPLEMENTATION
-
-#ifndef NK_INTERNAL_H
-#define NK_INTERNAL_H
-
-#ifndef NK_POOL_DEFAULT_CAPACITY
-#define NK_POOL_DEFAULT_CAPACITY 16
-#endif
-
-#ifndef NK_DEFAULT_COMMAND_BUFFER_SIZE
-#define NK_DEFAULT_COMMAND_BUFFER_SIZE (4*1024)
-#endif
-
-#ifndef NK_BUFFER_DEFAULT_INITIAL_SIZE
-#define NK_BUFFER_DEFAULT_INITIAL_SIZE (4*1024)
-#endif
-
-/* standard library headers */
-#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR
-#include /* malloc, free */
-#endif
-#ifdef NK_INCLUDE_STANDARD_IO
-#include