changeset 1648:b7ecd0d6a77b mame_interp

Merge from default
author Michael Pavone <pavone@retrodev.com>
date Tue, 25 Dec 2018 11:12:26 -0800
parents 36732f5c2281 (current diff) 5a662692c215 (diff)
children 956c1cce05e2
files Makefile backend.c backend.h blastem.c fib.s68 genesis.c genesis.h m68k_core.c sms.c sms.h
diffstat 111 files changed, 48655 insertions(+), 3060 deletions(-) [+]
line wrap: on
line diff
--- a/68kinst.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/68kinst.c	Tue Dec 25 11:12:26 2018 -0800
@@ -1537,6 +1537,7 @@
 					immed = 8;
 				}
 				decoded->src.params.immed = immed;
+				decoded->variant = VAR_QUICK;
 			}
 			decoded->dst.addr_mode = MODE_REG;
 			decoded->dst.params.regs.pri = *istream & 0x7;
@@ -2608,9 +2609,12 @@
 #endif
 	default:
 		size = decoded->extra.size;
+		uint8_t is_quick = decoded->variant == VAR_QUICK && decoded->op != M68K_ASL && decoded->op != M68K_ASR 
+			&& decoded->op != M68K_LSL && decoded->op != M68K_LSR && decoded->op != M68K_ROXR && decoded->op != M68K_ROXL
+			&& decoded->op != M68K_ROR && decoded->op != M68K_ROL;
 		ret = sprintf(dst, "%s%s%s",
 				mnemonics[decoded->op],
-				decoded->variant == VAR_QUICK ? "q" : (decoded->variant == VAR_IMMEDIATE ? "i" : ""),
+				is_quick ? "q" : (decoded->variant == VAR_IMMEDIATE ? "i" : ""),
 				size == OPSIZE_BYTE ? ".b" : (size == OPSIZE_WORD ? ".w" : (size == OPSIZE_LONG ? ".l" : "")));
 	}
 	if (decoded->op == M68K_MOVEM) {
--- a/Makefile	Sun Dec 31 10:11:16 2017 -0800
+++ b/Makefile	Tue Dec 25 11:12:26 2018 -0800
@@ -16,6 +16,8 @@
 
 MEM:=mem_win.o
 TERMINAL:=terminal_win.o
+FONT:=nuklear_ui/font_win.o
+NET:=net_win.o
 EXE:=.exe
 CC:=i686-w64-mingw32-gcc-win32
 CFLAGS:=-std=gnu99 -Wreturn-type -Werror=return-type -Werror=implicit-function-declaration -I"$(SDL2_PREFIX)/include/SDL2" -I"$(GLEW_PREFIX)/include" -DGLEW_STATIC
@@ -26,16 +28,19 @@
 
 MEM:=mem.o
 TERMINAL:=terminal.o
+NET:=net.o
 EXE:=
 
 ifeq ($(OS),Darwin)
 LIBS=sdl2 glew
+FONT:=nuklear_ui/font_mac.o
 else
 LIBS=sdl2 glew gl
+FONT:=nuklear_ui/font.o
 endif #Darwin
 
 HAS_PROC:=$(shell if [ -d /proc ]; then /bin/echo -e -DHAS_PROC; fi)
-CFLAGS:=-std=gnu99 -Wreturn-type -Werror=return-type -Werror=implicit-function-declaration -Wno-unused-value $(HAS_PROC)
+CFLAGS:=-std=gnu99 -Wreturn-type -Werror=return-type -Werror=implicit-function-declaration -Wno-unused-value $(HAS_PROC) -DHAVE_UNISTD_H
 ifeq ($(OS),Darwin)
 #This should really be based on whether or not the C compiler is clang rather than based on the OS
 CFLAGS+= -Wno-logical-op-parentheses
@@ -46,7 +51,7 @@
 
 ifeq ($(OS),Darwin)
 CFLAGS+= -IFrameworks/SDL2.framework/Headers
-LDFLAGS+= -FFrameworks -framework SDL2 -framework OpenGL
+LDFLAGS+= -FFrameworks -framework SDL2 -framework OpenGL -framework AppKit
 FIXUP:=install_name_tool -change @rpath/SDL2.framework/Versions/A/SDL2 @executable_path/Frameworks/SDL2.framework/Versions/A/SDL2
 else
 CFLAGS+= -Isdl/include
@@ -58,14 +63,14 @@
 LDFLAGS:=-lm $(shell pkg-config --libs $(LIBS))
 
 ifeq ($(OS),Darwin)
-LDFLAGS+= -framework OpenGL
+LDFLAGS+= -framework OpenGL -framework AppKit
 endif
 
 endif #PORTABLE
 endif #Windows
 
 ifdef DEBUG
-OPT:=-ggdb -Og
+OPT:=-g3 -O0
 else
 ifdef NOLTO
 OPT:=-O2
@@ -86,6 +91,7 @@
 endif
 ifdef NOGL
 CFLAGS+= -DDISABLE_OPENGL
+NONUKLEAR:=1
 endif
 
 ifdef M68030
@@ -127,10 +133,28 @@
 #Z80OBJS=z80inst.o z80_to_x86.o
 Z80OBJS=z80inst.o mame_z80/z80.o
 AUDIOOBJS=ym2612.o psg.o wave.o
-CONFIGOBJS=config.o tern.o util.o
+CONFIGOBJS=config.o tern.o util.o paths.o 
+NUKLEAROBJS=$(FONT) nuklear_ui/blastem_nuklear.o nuklear_ui/sfnt.o controller_info.o
+RENDEROBJS=render_sdl.o ppm.o
+LIBZOBJS=zlib/adler32.o zlib/compress.o zlib/crc32.o zlib/deflate.o zlib/gzclose.o zlib/gzlib.o zlib/gzread.o\
+	zlib/gzwrite.o zlib/infback.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o zlib/trees.o zlib/uncompr.o zlib/zutil.o
+	
+ifdef NOZLIB
+CFLAGS+= -DDISABLE_ZLIB
+else
+RENDEROBJS+= $(LIBZOBJS) png.o
+endif
 
-#MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o render_sdl.o ppm.o io.o romdb.o hash.o menu.o xband.o realtec.o i2c.o nor.o sega_mapper.o multi_game.o serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS)
-MAINOBJS=blastem.o system.o genesis.o vdp.o render_sdl.o ppm.o io.o romdb.o hash.o menu.o xband.o realtec.o i2c.o nor.o sega_mapper.o multi_game.o serialize.o $(TERMINAL) $(CONFIGOBJS) $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS)
+#MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o $(RENDEROBJS) io.o romdb.o hash.o menu.o xband.o 
+MAINOBJS=blastem.o system.o genesis.o vdp.o $(RENDEROBJS) io.o romdb.o hash.o menu.o xband.o \
+	realtec.o i2c.o nor.o sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) \
+	$(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o
+	
+ifdef NONUKLEAR
+CFLAGS+= -DDISABLE_NUKLEAR
+else
+MAINOBJS+= $(NUKLEAROBJS)
+endif
 
 ifeq ($(CPU),x86_64)
 CFLAGS+=-DX86_64 -m64
@@ -140,7 +164,7 @@
 CFLAGS+=-DX86_32 -m32
 LDFLAGS+=-m32
 else
-$(error $(CPU) is not a supported architecture)
+#$(error $(CPU) is not a supported architecture)
 endif
 endif
 
@@ -165,7 +189,7 @@
 	$(CC) -o $@ $^ $(LDFLAGS)
 	$(FIXUP) ./$@
 	
-blastjag$(EXE) : jaguar.o jag_video.o render_sdl.o serialize.o $(M68KOBJS) $(TRANSOBJS) $(CONFIGOBJS)
+blastjag$(EXE) : jaguar.o jag_video.o $(RENDEROBJS) serialize.o $(M68KOBJS) $(TRANSOBJS) $(CONFIGOBJS)
 	$(CC) -o $@ $^ $(LDFLAGS)
 
 dis$(EXE) : dis.o 68kinst.o tern.o vos_program_module.o
@@ -192,11 +216,11 @@
 ztestgen : ztestgen.o z80inst.o
 	$(CC) -ggdb -o ztestgen ztestgen.o z80inst.o
 
-stateview$(EXE) : stateview.o vdp.o render_sdl.o ppm.o serialize.o $(CONFIGOBJS) gst.o
+stateview$(EXE) : stateview.o vdp.o $(RENDEROBJS) serialize.o $(CONFIGOBJS) gst.o
 	$(CC) -o $@ $^ $(LDFLAGS)
 	$(FIXUP) ./$@
 
-vgmplay$(EXE) : vgmplay.o render_sdl.o ppm.o serialize.o $(CONFIGOBJS) $(AUDIOOBJS)
+vgmplay$(EXE) : vgmplay.o $(RENDEROBJS) serialize.o $(CONFIGOBJS) $(AUDIOOBJS)
 	$(CC) -o $@ $^ $(LDFLAGS)
 	$(FIXUP) ./$@
 
@@ -232,6 +256,10 @@
 
 %.o : %.c
 	$(CC) $(CFLAGS) -c -o $@ $<
+  
+%.o : %.m
+	$(CC) $(CFLAGS) -c -o $@ $<
+
 %.png : %.xcf
 	xcf2png $< > $@
 
@@ -255,4 +283,4 @@
 menu.bin : font_interlace_variable.tiles arrow.tiles cursor.tiles button.tiles font.tiles
 
 clean :
-	rm -rf $(ALL) trans ztestrun ztestgen *.o musashi/*.o
+	rm -rf $(ALL) trans ztestrun ztestgen *.o nuklear_ui/*.o zlib/*.o
--- a/backend.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/backend.c	Tue Dec 25 11:12:26 2018 -0800
@@ -77,7 +77,7 @@
 	for (uint32_t chunk = 0; chunk < opts->memmap_chunks; chunk++)
 	{
 		if (address >= memmap[chunk].start && address < memmap[chunk].end) {
-			if (!(memmap[chunk].flags & MMAP_READ)) {
+			if (!(memmap[chunk].flags & (MMAP_READ|MMAP_READ_CODE))) {
 				return NULL;
 			}
 			uint8_t * base = memmap[chunk].flags & MMAP_PTR_IDX
--- a/backend.h	Sun Dec 31 10:11:16 2017 -0800
+++ b/backend.h	Tue Dec 25 11:12:26 2018 -0800
@@ -49,42 +49,7 @@
 	uint32_t             address;
 } deferred_addr;
 
-typedef enum {
-	READ_16,
-	READ_8,
-	WRITE_16,
-	WRITE_8
-} ftype;
-
-#define MMAP_READ      0x01
-#define MMAP_WRITE     0x02
-#define MMAP_CODE      0x04
-#define MMAP_PTR_IDX   0x08
-#define MMAP_ONLY_ODD  0x10
-#define MMAP_ONLY_EVEN 0x20
-#define MMAP_FUNC_NULL 0x40
-#define MMAP_BYTESWAP  0x80
-#define MMAP_AUX_BUFF  0x100
-
-typedef uint16_t (*read_16_fun)(uint32_t address, void * context);
-typedef uint8_t (*read_8_fun)(uint32_t address, void * context);
-typedef void * (*write_16_fun)(uint32_t address, void * context, uint16_t value);
-typedef void * (*write_8_fun)(uint32_t address, void * context, uint8_t value);
-
-typedef struct {
-	uint32_t     start;
-	uint32_t     end;
-	uint32_t     mask;
-	uint32_t     aux_mask;
-	uint16_t     ptr_index;
-	uint16_t     flags;
-	void *       buffer;
-	read_16_fun  read_16;
-	write_16_fun write_16;
-	read_8_fun   read_8;
-	write_8_fun  write_8;
-} memmap_chunk;
-
+#include "memmap.h"
 #include "system.h"
 
 typedef struct {
--- a/backend_x86.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/backend_x86.c	Tue Dec 25 11:12:26 2018 -0800
@@ -244,7 +244,7 @@
 						if (is_write && (memmap[chunk].flags & MMAP_CODE)) {
 							pop_r(code, opts->scratch2);
 						} else {
-							add_ir(code, sizeof(void*), RSP, SZ_D);
+							add_ir(code, sizeof(void*), RSP, SZ_PTR);
 							code->stack_off -= sizeof(void *);
 						}
 					} else {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,1056 @@
+#include <string.h>
+#include "render.h"
+#include "system.h"
+#include "io.h"
+#include "blastem.h"
+#include "saves.h"
+#include "util.h"
+#include "genesis.h"
+#include "sms.h"
+#include "menu.h"
+#include "bindings.h"
+#include "controller_info.h"
+#ifndef DISABLE_NUKLEAR
+#include "nuklear_ui/blastem_nuklear.h"
+#endif
+
+enum {
+	BIND_NONE,
+	BIND_UI,
+	BIND_GAMEPAD,
+	BIND_MOUSE
+};
+
+typedef enum {
+	UI_DEBUG_MODE_INC,
+	UI_ENTER_DEBUGGER,
+	UI_SAVE_STATE,
+	UI_SET_SPEED,
+	UI_NEXT_SPEED,
+	UI_PREV_SPEED,
+	UI_RELEASE_MOUSE,
+	UI_TOGGLE_KEYBOARD_CAPTURE,
+	UI_TOGGLE_FULLSCREEN,
+	UI_SOFT_RESET,
+	UI_RELOAD,
+	UI_SMS_PAUSE,
+	UI_SCREENSHOT,
+	UI_EXIT,
+	UI_PLANE_DEBUG,
+	UI_VRAM_DEBUG,
+	UI_CRAM_DEBUG,
+	UI_COMPOSITE_DEBUG
+} ui_action;
+
+typedef struct {
+	uint8_t bind_type;
+	uint8_t subtype_a;
+	uint8_t subtype_b;
+} keybinding;
+
+typedef struct {
+	keybinding bindings[4];
+	uint8_t    state;
+} joydpad;
+
+typedef struct {
+	keybinding positive;
+	keybinding negative;
+	int16_t    value;
+} joyaxis;
+
+typedef struct {
+	keybinding *buttons;
+	joydpad    *dpads;
+	joyaxis    *axes;
+	uint32_t   num_buttons; //number of entries in the buttons array, not necessarily the number of buttons on the device
+	uint32_t   num_dpads;   //number of entries in the dpads array, not necessarily the number of dpads on the device
+	uint32_t   num_axes;    //number of entries in the axes array, not necessarily the number of dpads on the device
+} joystick;
+
+typedef struct {
+	keybinding buttons[MAX_MOUSE_BUTTONS];
+	keybinding motion;
+} mousebinding;
+
+#define DEFAULT_JOYBUTTON_ALLOC 12
+static keybinding *bindings[0x10000];
+static joystick joysticks[MAX_JOYSTICKS];
+static mousebinding mice[MAX_MICE];
+const uint8_t dpadbits[] = {RENDER_DPAD_UP, RENDER_DPAD_DOWN, RENDER_DPAD_LEFT, RENDER_DPAD_RIGHT};
+
+static void do_bind(keybinding *binding, uint8_t bind_type, uint8_t subtype_a, uint8_t subtype_b)
+{
+	binding->bind_type = bind_type;
+	binding->subtype_a = subtype_a;
+	binding->subtype_b = subtype_b;
+}
+
+void bind_key(int keycode, uint8_t bind_type, uint8_t subtype_a, uint8_t subtype_b)
+{
+	int bucket = keycode >> 15 & 0xFFFF;
+	if (!bindings[bucket]) {
+		bindings[bucket] = malloc(sizeof(keybinding) * 0x8000);
+		memset(bindings[bucket], 0, sizeof(keybinding) * 0x8000);
+	}
+	int idx = keycode & 0x7FFF;
+	do_bind(bindings[bucket] + idx, bind_type, subtype_a, subtype_b);
+}
+
+void bind_button(int joystick, int button, uint8_t bind_type, uint8_t subtype_a, uint8_t subtype_b)
+{
+	if (joystick >= MAX_JOYSTICKS) {
+		return;
+	}
+	if (!joysticks[joystick].buttons) {
+		joysticks[joystick].num_buttons = button < DEFAULT_JOYBUTTON_ALLOC ? DEFAULT_JOYBUTTON_ALLOC : button + 1;
+		joysticks[joystick].buttons = calloc(joysticks[joystick].num_buttons, sizeof(keybinding));
+	} else if (joysticks[joystick].num_buttons <= button) {
+		uint32_t old_capacity = joysticks[joystick].num_buttons;
+		joysticks[joystick].num_buttons *= 2;
+		joysticks[joystick].buttons = realloc(joysticks[joystick].buttons, sizeof(keybinding) * joysticks[joystick].num_buttons);
+		memset(joysticks[joystick].buttons + old_capacity, 0, joysticks[joystick].num_buttons - old_capacity);
+	}
+	do_bind(joysticks[joystick].buttons + button, bind_type, subtype_a, subtype_b);
+}
+
+void bind_dpad(int joystick, int dpad, int direction, uint8_t bind_type, uint8_t subtype_a, uint8_t subtype_b)
+{
+	if (joystick >= MAX_JOYSTICKS) {
+		return;
+	}
+	if (!joysticks[joystick].dpads) {
+		//multiple D-pads/hats are not common, so don't allocate any extra space
+		joysticks[joystick].dpads = calloc(dpad+1, sizeof(joydpad));
+		joysticks[joystick].num_dpads = dpad+1;
+	} else if (joysticks[joystick].num_dpads <= dpad) {
+		uint32_t old_capacity = joysticks[joystick].num_dpads;
+		joysticks[joystick].num_dpads *= 2;
+		joysticks[joystick].dpads = realloc(joysticks[joystick].dpads, sizeof(joydpad) * joysticks[joystick].num_dpads);
+		memset(joysticks[joystick].dpads + old_capacity, 0, (joysticks[joystick].num_dpads - old_capacity) * sizeof(joydpad));
+	}
+	for (int i = 0; i < 4; i ++) {
+		if (dpadbits[i] & direction) {
+			do_bind(joysticks[joystick].dpads[dpad].bindings + i, bind_type, subtype_a, subtype_b);
+			break;
+		}
+	}
+}
+
+void bind_axis(int joystick, int axis, int positive, uint8_t bind_type, uint8_t subtype_a, uint8_t subtype_b)
+{
+	if (joystick >= MAX_JOYSTICKS) {
+		return;
+	}
+	if (!joysticks[joystick].axes) {
+		//typical gamepad has 4 axes
+		joysticks[joystick].num_axes = axis+1 > 4 ? axis+1 : 4;
+		joysticks[joystick].axes = calloc(joysticks[joystick].num_axes, sizeof(joyaxis));
+	} else if (joysticks[joystick].num_axes <= axis) {
+		uint32_t old_capacity = joysticks[joystick].num_axes;
+		joysticks[joystick].num_axes *= 2;
+		joysticks[joystick].axes = realloc(joysticks[joystick].axes, sizeof(joyaxis) * joysticks[joystick].num_axes);
+		memset(joysticks[joystick].axes + old_capacity, 0, (joysticks[joystick].num_axes - old_capacity) * sizeof(joyaxis));
+	}
+	if (positive) {
+		do_bind(&joysticks[joystick].axes[axis].positive, bind_type, subtype_a, subtype_b);
+	} else {
+		do_bind(&joysticks[joystick].axes[axis].negative, bind_type, subtype_a, subtype_b);
+	}
+}
+
+void reset_joystick_bindings(int joystick)
+{
+	if (joystick >= MAX_JOYSTICKS) {
+		return;
+	}
+	if (joysticks[joystick].buttons) {
+		for (int i = 0; i < joysticks[joystick].num_buttons; i++)
+		{
+			joysticks[joystick].buttons[i].bind_type = BIND_NONE;
+		}
+	}
+	if (joysticks[joystick].dpads) {
+		for (int i = 0; i < joysticks[joystick].num_dpads; i++)
+		{
+			for (int dir = 0; dir < 4; dir++)
+			{
+				joysticks[joystick].dpads[i].bindings[dir].bind_type = BIND_NONE;
+			}
+		}
+	}
+	if (joysticks[joystick].axes) {
+		for (int i = 0; i < joysticks[joystick].num_axes; i++)
+		{
+			joysticks[joystick].axes[i].positive.bind_type = BIND_NONE;
+			joysticks[joystick].axes[i].negative.bind_type = BIND_NONE;
+		}
+	}
+}
+
+void handle_binding_down(keybinding * binding)
+{
+	if (!current_system) {
+		return;
+	}
+	if (binding->bind_type == BIND_GAMEPAD && current_system && current_system->gamepad_down)
+	{
+		current_system->gamepad_down(current_system, binding->subtype_a, binding->subtype_b);
+	}
+	else if (binding->bind_type == BIND_MOUSE && current_system && current_system->mouse_down)
+	{
+		current_system->mouse_down(current_system, binding->subtype_a, binding->subtype_b);
+	}
+}
+
+static uint8_t keyboard_captured;
+void handle_keydown(int keycode, uint8_t scancode)
+{
+	int bucket = keycode >> 15 & 0xFFFF;
+	int idx = keycode & 0x7FFF;
+	keybinding * binding = bindings[bucket] ? bindings[bucket] + idx : NULL;
+	if (binding && (!keyboard_captured || (binding->bind_type == BIND_UI && binding->subtype_a == UI_TOGGLE_KEYBOARD_CAPTURE))) {
+		handle_binding_down(binding);
+	} else if (keyboard_captured && current_system && current_system->keyboard_down) {
+		current_system->keyboard_down(current_system, scancode);
+	}
+}
+
+void handle_joydown(int joystick, int button)
+{
+	if (joystick >= MAX_JOYSTICKS || button >= joysticks[joystick].num_buttons) {
+		return;
+	}
+	keybinding * binding = joysticks[joystick].buttons + button;
+	handle_binding_down(binding);
+}
+
+static uint8_t mouse_mode = MOUSE_NONE;
+static uint8_t mouse_captured;
+void handle_mousedown(int mouse, int button)
+{
+	if (mouse_mode == MOUSE_CAPTURE && !mouse_captured) {
+		mouse_captured = 1;
+		render_relative_mouse(1);
+		return;
+	}
+	if (mouse >= MAX_MICE || button > MAX_MOUSE_BUTTONS || button <= 0) {
+		return;
+	}
+	keybinding * binding = mice[mouse].buttons + button - 1;
+	handle_binding_down(binding);
+}
+
+static int current_speed = 0;
+static int num_speeds = 1;
+static uint32_t * speeds = NULL;
+
+static uint8_t mouse_captured;
+
+#ifdef _WIN32
+#define localtime_r(a,b) localtime(a)
+#endif
+
+void handle_binding_up(keybinding * binding)
+{
+	switch(binding->bind_type)
+	{
+	case BIND_GAMEPAD:
+		if (current_system && current_system->gamepad_up) {
+			current_system->gamepad_up(current_system, binding->subtype_a, binding->subtype_b);
+		}
+		break;
+	case BIND_MOUSE:
+		if (current_system && current_system->mouse_up) {
+			current_system->mouse_up(current_system, binding->subtype_a, binding->subtype_b);
+		}
+		break;
+	case BIND_UI:
+		switch (binding->subtype_a)
+		{
+		case UI_DEBUG_MODE_INC:
+			current_system->inc_debug_mode(current_system);
+			break;
+		case UI_ENTER_DEBUGGER:
+			current_system->enter_debugger = 1;
+			break;
+		case UI_SAVE_STATE:
+			current_system->save_state = QUICK_SAVE_SLOT+1;
+			break;
+		case UI_NEXT_SPEED:
+			current_speed++;
+			if (current_speed >= num_speeds) {
+				current_speed = 0;
+			}
+			printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]);
+			current_system->set_speed_percent(current_system, speeds[current_speed]);
+			break;
+		case UI_PREV_SPEED:
+			current_speed--;
+			if (current_speed < 0) {
+				current_speed = num_speeds - 1;
+			}
+			printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]);
+			current_system->set_speed_percent(current_system, speeds[current_speed]);
+			break;
+		case UI_SET_SPEED:
+			if (binding->subtype_b < num_speeds) {
+				current_speed = binding->subtype_b;
+				printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]);
+				current_system->set_speed_percent(current_system, speeds[current_speed]);
+			} else {
+				printf("Setting speed to %d\n", speeds[current_speed]);
+				current_system->set_speed_percent(current_system, speeds[current_speed]);
+			}
+			break;
+		case UI_RELEASE_MOUSE:
+			if (mouse_captured) {
+				mouse_captured = 0;
+				render_relative_mouse(0);
+			}
+			break;
+		case UI_TOGGLE_KEYBOARD_CAPTURE:
+			if (current_system && current_system->has_keyboard) {
+				keyboard_captured = !keyboard_captured;
+			}
+			break;
+		case UI_TOGGLE_FULLSCREEN:
+			render_toggle_fullscreen();
+			break;
+		case UI_SOFT_RESET:
+			current_system->soft_reset(current_system);
+			break;
+		case UI_RELOAD:
+			reload_media();
+			break;
+		case UI_SMS_PAUSE:
+			if (current_system && current_system->gamepad_down) {
+				current_system->gamepad_down(current_system, GAMEPAD_MAIN_UNIT, MAIN_UNIT_PAUSE);
+			}
+			break;
+		case UI_SCREENSHOT: {
+			char *screenshot_base = tern_find_path(config, "ui\0screenshot_path\0", TVAL_PTR).ptrval;
+			if (!screenshot_base) {
+				screenshot_base = "$HOME";
+			}
+			tern_node *vars = tern_insert_ptr(NULL, "HOME", get_home_dir());
+			vars = tern_insert_ptr(vars, "EXEDIR", get_exe_dir());
+			screenshot_base = replace_vars(screenshot_base, vars, 1);
+			tern_free(vars);
+			time_t now = time(NULL);
+			struct tm local_store;
+			char fname_part[256];
+			char *template = tern_find_path(config, "ui\0screenshot_template\0", TVAL_PTR).ptrval;
+			if (!template) {
+				template = "blastem_%c.ppm";
+			}
+			strftime(fname_part, sizeof(fname_part), template, localtime_r(&now, &local_store));
+			char const *parts[] = {screenshot_base, PATH_SEP, fname_part};
+			char *path = alloc_concat_m(3, parts);
+			free(screenshot_base);
+			render_save_screenshot(path);
+			break;
+		}
+		case UI_EXIT:
+#ifndef DISABLE_NUKLEAR
+			if (is_nuklear_active()) {
+				show_pause_menu();
+			} else {
+#endif
+			current_system->request_exit(current_system);
+			if (current_system->type == SYSTEM_GENESIS) {
+				genesis_context *gen = (genesis_context *)current_system;
+				if (gen->extra) {
+					//TODO: More robust mechanism for detecting menu
+					menu_context *menu = gen->extra;
+					menu->external_game_load = 1;
+				}
+			}
+#ifndef DISABLE_NUKLEAR
+			}
+#endif
+			break;
+		case UI_PLANE_DEBUG: 
+		case UI_VRAM_DEBUG: 
+		case UI_CRAM_DEBUG:
+		case UI_COMPOSITE_DEBUG: {
+			vdp_context *vdp = NULL;
+			if (current_system->type == SYSTEM_GENESIS) {
+				genesis_context *gen = (genesis_context *)current_system;
+				vdp = gen->vdp;
+			} else if (current_system->type == SYSTEM_SMS) {
+				sms_context *sms = (sms_context *)current_system;
+				vdp = sms->vdp;
+			}
+			if (vdp) {
+				uint8_t debug_type;
+				switch(binding->subtype_a)
+				{
+				case UI_PLANE_DEBUG: debug_type = VDP_DEBUG_PLANE; break;
+				case UI_VRAM_DEBUG: debug_type = VDP_DEBUG_VRAM; break;
+				case UI_CRAM_DEBUG: debug_type = VDP_DEBUG_CRAM; break;
+				case UI_COMPOSITE_DEBUG: debug_type = VDP_DEBUG_COMPOSITE; break;
+				default: return;
+				}
+				vdp_toggle_debug_view(vdp, debug_type);
+			}
+			break;
+		}
+		}
+		break;
+	}
+}
+
+void handle_keyup(int keycode, uint8_t scancode)
+{
+	int bucket = keycode >> 15 & 0xFFFF;
+	int idx = keycode & 0x7FFF;
+	keybinding * binding = bindings[bucket] ? bindings[bucket] + idx : NULL;
+	if (binding && (!keyboard_captured || (binding->bind_type == BIND_UI && binding->subtype_a == UI_TOGGLE_KEYBOARD_CAPTURE))) {
+		handle_binding_up(binding);
+	} else if (keyboard_captured && current_system && current_system->keyboard_up) {
+		current_system->keyboard_up(current_system, scancode);
+	}
+}
+
+void handle_joyup(int joystick, int button)
+{
+	if (joystick >= MAX_JOYSTICKS  || button >= joysticks[joystick].num_buttons) {
+		return;
+	}
+	keybinding * binding = joysticks[joystick].buttons + button;
+	handle_binding_up(binding);
+}
+
+void handle_joy_dpad(int joystick, int dpadnum, uint8_t value)
+{
+	if (joystick >= MAX_JOYSTICKS  || dpadnum >= joysticks[joystick].num_dpads) {
+		return;
+	}
+	joydpad * dpad = joysticks[joystick].dpads + dpadnum;
+	uint8_t newdown = (value ^ dpad->state) & value;
+	uint8_t newup = ((~value) ^ (~dpad->state)) & (~value);
+	dpad->state = value;
+	for (int i = 0; i < 4; i++) {
+		if (newdown & dpadbits[i]) {
+			handle_binding_down(dpad->bindings + i);
+		} else if(newup & dpadbits[i]) {
+			handle_binding_up(dpad->bindings + i);
+		}
+	}
+}
+
+#define JOY_AXIS_THRESHOLD 2000
+
+void handle_joy_axis(int joystick, int axis, int16_t value)
+{
+	if (joystick >= MAX_JOYSTICKS  || axis >= joysticks[joystick].num_axes) {
+		return;
+	}
+	joyaxis *jaxis = joysticks[joystick].axes + axis;
+	int old_active = abs(jaxis->value) > JOY_AXIS_THRESHOLD;
+	int new_active = abs(value) > JOY_AXIS_THRESHOLD;
+	int old_pos = jaxis->value > 0;
+	int new_pos = value > 0;
+	jaxis->value = value;
+	if (old_active && (!new_active || old_pos != new_pos)) {
+		//previously activated direction is no longer active
+		handle_binding_up(old_pos ? &jaxis->positive : &jaxis->negative);
+	}
+	if (new_active && (!old_active || old_pos != new_pos)) {
+		//previously unactivated direction is now active
+		handle_binding_down(new_pos ? &jaxis->positive : &jaxis->negative);
+	}
+}
+
+void handle_mouse_moved(int mouse, uint16_t x, uint16_t y, int16_t deltax, int16_t deltay)
+{
+	if (mouse >= MAX_MICE || !current_system) {
+		return;
+	}
+	if (mice[mouse].motion.bind_type == BIND_MOUSE && mice[mouse].motion.subtype_b == PSEUDO_BUTTON_MOTION) {
+		uint8_t target_mouse = mice[mouse].motion.subtype_a;
+		switch(mouse_mode)
+		{
+		case MOUSE_NONE:
+			break;
+		case MOUSE_ABSOLUTE: {
+			if (current_system->mouse_motion_absolute) {
+				float scale_x = (render_emulated_width() * 2.0f) / ((float)render_width());
+				float scale_y = (render_emulated_height() * 2.0f) / ((float)render_height());
+				int32_t adj_x = x * scale_x + 2 * render_overscan_left() - 2 * BORDER_LEFT;
+				int32_t adj_y = y * scale_y + 2 * render_overscan_top() - 4;
+				
+				current_system->mouse_motion_absolute(current_system, target_mouse, adj_x, adj_y);
+			}
+			break;
+		}
+		case MOUSE_RELATIVE: {
+			if (current_system->mouse_motion_relative) {
+				current_system->mouse_motion_relative(current_system, target_mouse, deltax, deltay);
+			}
+			break;
+		}
+		case MOUSE_CAPTURE: {
+			if (mouse_captured && current_system->mouse_motion_relative) {
+				current_system->mouse_motion_relative(current_system, target_mouse, deltax, deltay);
+			}
+			break;
+		}
+		}
+	} else {
+		handle_binding_up(&mice[mouse].motion);
+	}
+}
+
+void handle_mouseup(int mouse, int button)
+{
+	if (mouse >= MAX_MICE || button > MAX_MOUSE_BUTTONS || button <= 0) {
+		return;
+	}
+	keybinding * binding = mice[mouse].buttons + button - 1;
+	handle_binding_up(binding);
+}
+
+void bindings_release_capture(void)
+{
+	if (mouse_mode == MOUSE_RELATIVE || (mouse_mode == MOUSE_CAPTURE && mouse_captured)) {
+		render_relative_mouse(0);
+	}
+	keyboard_captured = 0;
+}
+
+void bindings_reacquire_capture(void)
+{
+	if (mouse_mode == MOUSE_RELATIVE || (mouse_mode == MOUSE_CAPTURE && mouse_captured)) {
+		render_relative_mouse(1);
+	}
+}
+
+int parse_binding_target(int device_num, char * target, tern_node * padbuttons, tern_node *mousebuttons, uint8_t * subtype_a, uint8_t * subtype_b)
+{
+	const int gpadslen = strlen("gamepads.");
+	const int mouselen = strlen("mouse.");
+	if (!strncmp(target, "gamepads.", gpadslen)) {
+		int padnum = target[gpadslen] == 'n' ? device_num + 1 : target[gpadslen] - '0';
+		if (padnum >= 1 && padnum <= 8) {
+			int button = tern_find_int(padbuttons, target + gpadslen + 1, 0);
+			if (button) {
+				*subtype_a = padnum;
+				*subtype_b = button;
+				return BIND_GAMEPAD;
+			} else {
+				if (target[gpadslen+1]) {
+					warning("Gamepad mapping string '%s' refers to an invalid button '%s'\n", target, target + gpadslen + 1);
+				} else {
+					warning("Gamepad mapping string '%s' has no button component\n", target);
+				}
+			}
+		} else {
+			warning("Gamepad mapping string '%s' refers to an invalid gamepad number %c\n", target, target[gpadslen]);
+		}
+	} else if(!strncmp(target, "mouse.", mouselen)) {
+		int mousenum = target[mouselen] == 'n' ? device_num + 1 : target[mouselen] - '0';
+		if (mousenum >= 1 && mousenum <= 8) {
+			int button = tern_find_int(mousebuttons, target + mouselen + 1, 0);
+			if (button) {
+				*subtype_a = mousenum;
+				*subtype_b = button;
+				return BIND_MOUSE;
+			} else {
+				if (target[mouselen+1]) {
+					warning("Mouse mapping string '%s' refers to an invalid button '%s'\n", target, target + mouselen + 1);
+				} else {
+					warning("Mouse mapping string '%s' has no button component\n", target);
+				}
+			}
+		} else {
+			warning("Gamepad mapping string '%s' refers to an invalid mouse number %c\n", target, target[mouselen]);
+		}
+	} else if(!strncmp(target, "ui.", strlen("ui."))) {
+		if (!strcmp(target + 3, "vdp_debug_mode")) {
+			*subtype_a = UI_DEBUG_MODE_INC;
+		} else if(!strcmp(target + 3, "vdp_debug_pal")) {
+			//legacy binding, ignore
+			return 0;
+		} else if(!strcmp(target + 3, "enter_debugger")) {
+			*subtype_a = UI_ENTER_DEBUGGER;
+		} else if(!strcmp(target + 3, "save_state")) {
+			*subtype_a = UI_SAVE_STATE;
+		} else if(!strncmp(target + 3, "set_speed.", strlen("set_speed."))) {
+			*subtype_a = UI_SET_SPEED;
+			*subtype_b = atoi(target + 3 + strlen("set_speed."));
+		} else if(!strcmp(target + 3, "next_speed")) {
+			*subtype_a = UI_NEXT_SPEED;
+		} else if(!strcmp(target + 3, "prev_speed")) {
+			*subtype_a = UI_PREV_SPEED;
+		} else if(!strcmp(target + 3, "release_mouse")) {
+			*subtype_a = UI_RELEASE_MOUSE;
+		} else if(!strcmp(target + 3, "toggle_keyboard_captured")) {
+			*subtype_a = UI_TOGGLE_KEYBOARD_CAPTURE;
+		} else if (!strcmp(target + 3, "toggle_fullscreen")) {
+			*subtype_a = UI_TOGGLE_FULLSCREEN;
+		} else if (!strcmp(target + 3, "soft_reset")) {
+			*subtype_a = UI_SOFT_RESET;
+		} else if (!strcmp(target + 3, "reload")) {
+			*subtype_a = UI_RELOAD;
+		} else if (!strcmp(target + 3, "sms_pause")) {
+			*subtype_a = UI_SMS_PAUSE;
+		} else if (!strcmp(target + 3, "screenshot")) {
+			*subtype_a = UI_SCREENSHOT;
+		} else if(!strcmp(target + 3, "exit")) {
+			*subtype_a = UI_EXIT;
+		} else if (!strcmp(target + 3, "plane_debug")) {
+			*subtype_a = UI_PLANE_DEBUG;
+		} else if (!strcmp(target + 3, "vram_debug")) {
+			*subtype_a = UI_VRAM_DEBUG;
+		} else if (!strcmp(target + 3, "cram_debug")) {
+			*subtype_a = UI_CRAM_DEBUG;
+		} else if (!strcmp(target + 3, "compositing_debug")) {
+			*subtype_a = UI_COMPOSITE_DEBUG;
+		} else {
+			warning("Unreconized UI binding type %s\n", target);
+			return 0;
+		}
+		return BIND_UI;
+	} else {
+		warning("Unrecognized binding type %s\n", target);
+	}
+	return 0;
+}
+
+void process_keys(tern_node * cur, tern_node * special, tern_node * padbuttons, tern_node *mousebuttons, char * prefix)
+{
+	char * curstr = NULL;
+	int len;
+	if (!cur) {
+		return;
+	}
+	char onec[2];
+	if (prefix) {
+		len = strlen(prefix);
+		curstr = malloc(len + 2);
+		memcpy(curstr, prefix, len);
+	} else {
+		curstr = onec;
+		len = 0;
+	}
+	curstr[len] = cur->el;
+	curstr[len+1] = 0;
+	if (cur->el) {
+		process_keys(cur->straight.next, special, padbuttons, mousebuttons, curstr);
+	} else {
+		int keycode = tern_find_int(special, curstr, 0);
+		if (!keycode) {
+			keycode = curstr[0];
+			if (curstr[1] != 0) {
+				warning("%s is not recognized as a key identifier, truncating to %c\n", curstr, curstr[0]);
+			}
+		}
+		char * target = cur->straight.value.ptrval;
+		uint8_t subtype_a = 0, subtype_b = 0;
+		int bindtype = parse_binding_target(0, target, padbuttons, mousebuttons, &subtype_a, &subtype_b);
+		bind_key(keycode, bindtype, subtype_a, subtype_b);
+	}
+	process_keys(cur->left, special, padbuttons, mousebuttons, prefix);
+	process_keys(cur->right, special, padbuttons, mousebuttons, prefix);
+	if (curstr && len) {
+		free(curstr);
+	}
+}
+
+void process_speeds(tern_node * cur, char * prefix)
+{
+	char * curstr = NULL;
+	int len;
+	if (!cur) {
+		return;
+	}
+	char onec[2];
+	if (prefix) {
+		len = strlen(prefix);
+		curstr = malloc(len + 2);
+		memcpy(curstr, prefix, len);
+	} else {
+		curstr = onec;
+		len = 0;
+	}
+	curstr[len] = cur->el;
+	curstr[len+1] = 0;
+	if (cur->el) {
+		process_speeds(cur->straight.next, curstr);
+	} else {
+		char *end;
+		long speed_index = strtol(curstr, &end, 10);
+		if (speed_index < 0 || end == curstr || *end) {
+			warning("%s is not a valid speed index", curstr);
+		} else {
+			if (speed_index >= num_speeds) {
+				speeds = realloc(speeds, sizeof(uint32_t) * (speed_index+1));
+				for(; num_speeds < speed_index + 1; num_speeds++) {
+					speeds[num_speeds] = 0;
+				}
+			}
+			speeds[speed_index] = atoi(cur->straight.value.ptrval);
+			if (speeds[speed_index] < 1) {
+				warning("%s is not a valid speed percentage, setting speed %d to 100", cur->straight.value.ptrval, speed_index);
+				speeds[speed_index] = 100;
+			}
+		}
+	}
+	process_speeds(cur->left, prefix);
+	process_speeds(cur->right, prefix);
+	if (curstr && len) {
+		free(curstr);
+	}
+}
+
+typedef struct {
+	tern_node *padbuttons;
+	tern_node *mousebuttons;
+	int       mouseidx;
+} pmb_state;
+
+void process_mouse_button(char *buttonstr, tern_val value, uint8_t valtype, void *data)
+{
+	pmb_state *state = data;
+	int buttonnum = atoi(buttonstr);
+	if (buttonnum < 1 || buttonnum > MAX_MOUSE_BUTTONS) {
+		warning("Mouse button %s is out of the supported range of 1-8\n", buttonstr);
+		return;
+	}
+	if (valtype != TVAL_PTR) {
+		warning("Mouse button %s is not a scalar value!\n", buttonstr);
+		return;
+	}
+	buttonnum--;
+	uint8_t subtype_a = 0, subtype_b = 0;
+	int bindtype = parse_binding_target(state->mouseidx, value.ptrval, state->padbuttons, state->mousebuttons, &subtype_a, &subtype_b);
+	mice[state->mouseidx].buttons[buttonnum].bind_type = bindtype;
+	mice[state->mouseidx].buttons[buttonnum].subtype_a = subtype_a;
+	mice[state->mouseidx].buttons[buttonnum].subtype_b = subtype_b;
+}
+
+void process_mouse(char *mousenum, tern_val value, uint8_t valtype, void *data)
+{
+	tern_node **buttonmaps = data;
+	if (valtype != TVAL_NODE) {
+		warning("Binding for mouse %s is a scalar!\n", mousenum);
+		return;
+	}
+	tern_node *mousedef = value.ptrval;
+	tern_node *padbuttons = buttonmaps[0];
+	tern_node *mousebuttons = buttonmaps[1];
+
+	int mouseidx = atoi(mousenum);
+	if (mouseidx < 0 || mouseidx >= MAX_MICE) {
+		warning("Mouse numbers must be between 0 and %d, but %d is not\n", MAX_MICE, mouseidx);
+		return;
+	}
+	char *motion = tern_find_ptr(mousedef, "motion");
+	if (motion) {
+		uint8_t subtype_a = 0, subtype_b = 0;
+		int bindtype = parse_binding_target(mouseidx, motion, padbuttons, mousebuttons, &subtype_a, &subtype_b);
+		mice[mouseidx].motion.bind_type = bindtype;
+		mice[mouseidx].motion.subtype_a = subtype_a;
+		mice[mouseidx].motion.subtype_b = subtype_b;
+	}
+	tern_node *buttons = tern_find_path(mousedef, "buttons\0\0", TVAL_NODE).ptrval;
+	if (buttons) {
+		pmb_state state = {padbuttons, mousebuttons, mouseidx};
+		tern_foreach(buttons, process_mouse_button, &state);
+	}
+}
+
+typedef struct {
+	int       padnum;
+	tern_node *padbuttons;
+	tern_node *mousebuttons;
+} pad_button_state;
+
+
+static long map_warning_pad = -1;
+void process_pad_button(char *key, tern_val val, uint8_t valtype, void *data)
+{
+	pad_button_state *state = data;
+	int hostpadnum = state->padnum;
+	if (valtype != TVAL_PTR) {
+		warning("Pad button %s has a non-scalar value\n", key);
+		return;
+	}
+	uint8_t subtype_a = 0, subtype_b = 0;
+	int bindtype = parse_binding_target(hostpadnum, val.ptrval, state->padbuttons, state->mousebuttons, &subtype_a, &subtype_b);
+	char *end;
+	long hostbutton = strtol(key, &end, 10);
+	if (*end) {
+		//key is not a valid base 10 integer
+		hostbutton = render_translate_input_name(hostpadnum, key, 0);
+		if (hostbutton < 0) {
+			if (hostbutton == RENDER_INVALID_NAME) {
+				warning("%s is not a valid gamepad input name\n", key);
+			} else if (hostbutton == RENDER_NOT_MAPPED && hostpadnum != map_warning_pad) {
+				warning("No SDL 2 mapping exists for input %s on gamepad %d\n", key, hostpadnum);
+				map_warning_pad = hostpadnum;
+			}
+			return;
+		}
+		if (hostbutton & RENDER_DPAD_BIT) {
+			bind_dpad(hostpadnum, render_dpad_part(hostbutton), render_direction_part(hostbutton), bindtype, subtype_a, subtype_b);
+			return;
+		} else if (hostbutton & RENDER_AXIS_BIT) {
+			bind_axis(hostpadnum, render_axis_part(hostbutton), 1, bindtype, subtype_a, subtype_b);
+			return;
+		}
+	}
+	bind_button(hostpadnum, hostbutton, bindtype, subtype_a, subtype_b);
+}
+
+void process_pad_axis(char *key, tern_val val, uint8_t valtype, void *data)
+{
+	key = strdup(key);
+	pad_button_state *state = data;
+	int hostpadnum = state->padnum;
+	if (valtype != TVAL_PTR) {
+		warning("Mapping for axis %s has a non-scalar value", key);
+		return;
+	}
+	uint8_t subtype_a = 0, subtype_b = 0;
+	int bindtype = parse_binding_target(hostpadnum, val.ptrval, state->padbuttons, state->mousebuttons, &subtype_a, &subtype_b);
+	char *modifier = strchr(key, '.');
+	int positive = 1;
+	if (modifier) {
+		*modifier = 0;
+		modifier++;
+		if (!strcmp("negative", modifier)) {
+			positive = 0;
+		} else if(strcmp("positive", modifier)) {
+			warning("Invalid axis modifier %s for axis %s on pad %d\n", modifier, key, hostpadnum);
+		}
+	}
+	char *end;
+	long axis = strtol(key, &end, 10);
+	if (*end) {
+		//key is not a valid base 10 integer
+		axis = render_translate_input_name(hostpadnum, key, 1);
+		if (axis < 0) {
+			if (axis == RENDER_INVALID_NAME) {
+				warning("%s is not a valid gamepad input name\n", key);
+			} else if (axis == RENDER_NOT_MAPPED && hostpadnum != map_warning_pad) {
+				warning("No SDL 2 mapping exists for input %s on gamepad %d\n", key, hostpadnum);
+				map_warning_pad = hostpadnum;
+			}
+			goto done;
+		}
+		if (axis & RENDER_DPAD_BIT) {
+			bind_dpad(hostpadnum, render_dpad_part(axis), render_direction_part(axis), bindtype, subtype_a, subtype_b);
+			goto done;
+		} else if (axis & RENDER_AXIS_BIT) {
+			axis = render_axis_part(axis);
+		} else {
+			bind_button(hostpadnum, axis, bindtype, subtype_a, subtype_b);
+			goto done;
+		}
+	}
+	bind_axis(hostpadnum, axis, positive, bindtype, subtype_a, subtype_b);
+done:
+	free(key);
+	return;
+}
+
+static tern_node *get_pad_buttons()
+{
+	static tern_node *padbuttons;
+	if (!padbuttons) {
+		padbuttons = tern_insert_int(NULL, ".up", DPAD_UP);
+		padbuttons = tern_insert_int(padbuttons, ".down", DPAD_DOWN);
+		padbuttons = tern_insert_int(padbuttons, ".left", DPAD_LEFT);
+		padbuttons = tern_insert_int(padbuttons, ".right", DPAD_RIGHT);
+		padbuttons = tern_insert_int(padbuttons, ".a", BUTTON_A);
+		padbuttons = tern_insert_int(padbuttons, ".b", BUTTON_B);
+		padbuttons = tern_insert_int(padbuttons, ".c", BUTTON_C);
+		padbuttons = tern_insert_int(padbuttons, ".x", BUTTON_X);
+		padbuttons = tern_insert_int(padbuttons, ".y", BUTTON_Y);
+		padbuttons = tern_insert_int(padbuttons, ".z", BUTTON_Z);
+		padbuttons = tern_insert_int(padbuttons, ".start", BUTTON_START);
+		padbuttons = tern_insert_int(padbuttons, ".mode", BUTTON_MODE);
+	}
+	return padbuttons;
+}
+
+static tern_node *get_mouse_buttons()
+{
+	static tern_node *mousebuttons;
+	if (!mousebuttons) {
+		mousebuttons = tern_insert_int(NULL, ".left", MOUSE_LEFT);
+		mousebuttons = tern_insert_int(mousebuttons, ".middle", MOUSE_MIDDLE);
+		mousebuttons = tern_insert_int(mousebuttons, ".right", MOUSE_RIGHT);
+		mousebuttons = tern_insert_int(mousebuttons, ".start", MOUSE_START);
+		mousebuttons = tern_insert_int(mousebuttons, ".motion", PSEUDO_BUTTON_MOTION);
+	}
+	return mousebuttons;
+}
+
+tern_node *get_binding_node_for_pad(int padnum)
+{
+	if (padnum > MAX_JOYSTICKS) {
+		return NULL;
+	}
+	tern_node * pads = tern_find_path(config, "bindings\0pads\0", TVAL_NODE).ptrval;
+	if (!pads) {
+		return NULL;
+	}
+	char numstr[11];
+	sprintf(numstr, "%d", padnum);
+	tern_node * pad = tern_find_node(pads, numstr);
+	if (!pad) {
+		char *type_id = render_joystick_type_id(padnum);
+		pad = tern_find_node(pads, type_id);
+		free(type_id);
+	}
+	if (!pad) {
+		controller_info info = get_controller_info(padnum);
+		char *key = make_controller_type_key(&info);
+		pad = tern_find_node(pads, key);
+		free(key);
+	}
+	if (!pad) {
+		pad = tern_find_node(pads, "default");
+	}
+	return pad;
+}
+
+void handle_joy_added(int joystick)
+{
+	tern_node *pad = get_binding_node_for_pad(joystick);
+	if (!pad) {
+		return;
+	}
+	tern_node * dpad_node = tern_find_node(pad, "dpads");
+	if (dpad_node) {
+		for (int dpad = 0; dpad < 10; dpad++)
+		{
+			char numstr[2] = {dpad + '0', 0};
+			tern_node * pad_dpad = tern_find_node(dpad_node, numstr);
+			char * dirs[] = {"up", "down", "left", "right"};
+			//TODO: Support controllers that have d-pads implemented as analog axes or buttons
+			int dirnums[] = {RENDER_DPAD_UP, RENDER_DPAD_DOWN, RENDER_DPAD_LEFT, RENDER_DPAD_RIGHT};
+			for (int dir = 0; dir < sizeof(dirs)/sizeof(dirs[0]); dir++) {
+				char * target = tern_find_ptr(pad_dpad, dirs[dir]);
+				if (target) {
+					uint8_t subtype_a = 0, subtype_b = 0;
+					int bindtype = parse_binding_target(joystick, target, get_pad_buttons(), get_mouse_buttons(), &subtype_a, &subtype_b);
+					bind_dpad(joystick, dpad, dirnums[dir], bindtype, subtype_a, subtype_b);
+				}
+			}
+		}
+	}
+	tern_node *button_node = tern_find_node(pad, "buttons");
+	if (button_node) {
+		pad_button_state state = {
+			.padnum = joystick,
+			.padbuttons = get_pad_buttons(),
+			.mousebuttons = get_mouse_buttons()
+		};
+		tern_foreach(button_node, process_pad_button, &state);
+	}
+	tern_node *axes_node = tern_find_node(pad, "axes");
+	if (axes_node) {
+		pad_button_state state = {
+			.padnum = joystick,
+			.padbuttons = get_pad_buttons(),
+			.mousebuttons = get_mouse_buttons()
+		};
+		tern_foreach(axes_node, process_pad_axis, &state);
+	}
+}
+
+//only handles keyboards and mice as gamepads are handled on hotplug events
+void set_bindings(void)
+{
+	tern_node * special = tern_insert_int(NULL, "up", RENDERKEY_UP);
+	special = tern_insert_int(special, "down", RENDERKEY_DOWN);
+	special = tern_insert_int(special, "left", RENDERKEY_LEFT);
+	special = tern_insert_int(special, "right", RENDERKEY_RIGHT);
+	special = tern_insert_int(special, "enter", '\r');
+	special = tern_insert_int(special, "space", ' ');
+	special = tern_insert_int(special, "tab", '\t');
+	special = tern_insert_int(special, "backspace", '\b');
+	special = tern_insert_int(special, "esc", RENDERKEY_ESC);
+	special = tern_insert_int(special, "delete", RENDERKEY_DEL);
+	special = tern_insert_int(special, "lshift", RENDERKEY_LSHIFT);
+	special = tern_insert_int(special, "rshift", RENDERKEY_RSHIFT);
+	special = tern_insert_int(special, "lctrl", RENDERKEY_LCTRL);
+	special = tern_insert_int(special, "rctrl", RENDERKEY_RCTRL);
+	special = tern_insert_int(special, "lalt", RENDERKEY_LALT);
+	special = tern_insert_int(special, "ralt", RENDERKEY_RALT);
+	special = tern_insert_int(special, "home", RENDERKEY_HOME);
+	special = tern_insert_int(special, "end", RENDERKEY_END);
+	special = tern_insert_int(special, "pageup", RENDERKEY_PAGEUP);
+	special = tern_insert_int(special, "pagedown", RENDERKEY_PAGEDOWN);
+	special = tern_insert_int(special, "f1", RENDERKEY_F1);
+	special = tern_insert_int(special, "f2", RENDERKEY_F2);
+	special = tern_insert_int(special, "f3", RENDERKEY_F3);
+	special = tern_insert_int(special, "f4", RENDERKEY_F4);
+	special = tern_insert_int(special, "f5", RENDERKEY_F5);
+	special = tern_insert_int(special, "f6", RENDERKEY_F6);
+	special = tern_insert_int(special, "f7", RENDERKEY_F7);
+	special = tern_insert_int(special, "f8", RENDERKEY_F8);
+	special = tern_insert_int(special, "f9", RENDERKEY_F9);
+	special = tern_insert_int(special, "f10", RENDERKEY_F10);
+	special = tern_insert_int(special, "f11", RENDERKEY_F11);
+	special = tern_insert_int(special, "f12", RENDERKEY_F12);
+	special = tern_insert_int(special, "select", RENDERKEY_SELECT);
+	special = tern_insert_int(special, "play", RENDERKEY_PLAY);
+	special = tern_insert_int(special, "search", RENDERKEY_SEARCH);
+	special = tern_insert_int(special, "back", RENDERKEY_BACK);
+	special = tern_insert_int(special, "np0", RENDERKEY_NP0);
+	special = tern_insert_int(special, "np1", RENDERKEY_NP1);
+	special = tern_insert_int(special, "np2", RENDERKEY_NP2);
+	special = tern_insert_int(special, "np3", RENDERKEY_NP3);
+	special = tern_insert_int(special, "np4", RENDERKEY_NP4);
+	special = tern_insert_int(special, "np5", RENDERKEY_NP5);
+	special = tern_insert_int(special, "np6", RENDERKEY_NP6);
+	special = tern_insert_int(special, "np7", RENDERKEY_NP7);
+	special = tern_insert_int(special, "np8", RENDERKEY_NP8);
+	special = tern_insert_int(special, "np9", RENDERKEY_NP9);
+	special = tern_insert_int(special, "np/", RENDERKEY_NP_DIV);
+	special = tern_insert_int(special, "np*", RENDERKEY_NP_MUL);
+	special = tern_insert_int(special, "np-", RENDERKEY_NP_MIN);
+	special = tern_insert_int(special, "np+", RENDERKEY_NP_PLUS);
+	special = tern_insert_int(special, "npenter", RENDERKEY_NP_ENTER);
+	special = tern_insert_int(special, "np.", RENDERKEY_NP_STOP);
+
+	tern_node *padbuttons = get_pad_buttons();
+
+	tern_node *mousebuttons = get_mouse_buttons();
+	
+	tern_node * keys = tern_find_path(config, "bindings\0keys\0", TVAL_NODE).ptrval;
+	process_keys(keys, special, padbuttons, mousebuttons, NULL);
+	tern_free(special);
+	
+	memset(mice, 0, sizeof(mice));
+	tern_node * mice = tern_find_path(config, "bindings\0mice\0", TVAL_NODE).ptrval;
+	if (mice) {
+		tern_node *buttonmaps[2] = {padbuttons, mousebuttons};
+		tern_foreach(mice, process_mouse, buttonmaps);
+	}
+	tern_node * speed_nodes = tern_find_path(config, "clocks\0speeds\0", TVAL_NODE).ptrval;
+	speeds = malloc(sizeof(uint32_t));
+	speeds[0] = 100;
+	process_speeds(speed_nodes, NULL);
+	for (int i = 0; i < num_speeds; i++)
+	{
+		if (!speeds[i]) {
+			warning("Speed index %d was not set to a valid percentage!", i);
+			speeds[i] = 100;
+		}
+	}
+}
+
+void bindings_set_mouse_mode(uint8_t mode)
+{
+	mouse_mode = mode;
+	if (mode == MOUSE_RELATIVE) {
+		render_relative_mouse(1);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,29 @@
+#ifndef BINDINGS_H_
+#define BINDINGS_H_
+#include <stdint.h>
+
+typedef enum {
+	MOUSE_NONE,     //mouse is ignored
+	MOUSE_ABSOLUTE, //really only useful for menu ROM
+	MOUSE_RELATIVE, //for full screen
+	MOUSE_CAPTURE   //for windowed mode
+} mouse_modes;
+
+void set_bindings(void);
+void bindings_set_mouse_mode(uint8_t mode);
+tern_node *get_binding_node_for_pad(int padnum);
+void handle_keydown(int keycode, uint8_t scancode);
+void handle_keyup(int keycode, uint8_t scancode);
+void handle_joydown(int joystick, int button);
+void handle_joyup(int joystick, int button);
+void handle_joy_dpad(int joystick, int dpad, uint8_t state);
+void handle_joy_axis(int joystick, int axis, int16_t value);
+void handle_joy_added(int joystick);
+void handle_mouse_moved(int mouse, uint16_t x, uint16_t y, int16_t deltax, int16_t deltay);
+void handle_mousedown(int mouse, int button);
+void handle_mouseup(int mouse, int button);
+
+void bindings_release_capture(void);
+void bindings_reacquire_capture(void);
+
+#endif //BINDINGS_H_
--- a/blastem.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/blastem.c	Tue Dec 25 11:12:26 2018 -0800
@@ -27,9 +27,14 @@
 #include "terminal.h"
 #include "arena.h"
 #include "config.h"
+#include "bindings.h"
 #include "menu.h"
+#include "zip.h"
+#ifndef DISABLE_NUKLEAR
+#include "nuklear_ui/blastem_nuklear.h"
+#endif
 
-#define BLASTEM_VERSION "0.5.2-pre"
+#define BLASTEM_VERSION "0.6.0-pre"
 
 #ifdef __ANDROID__
 #define FULLSCREEN_DEFAULT 1
@@ -51,37 +56,104 @@
 #define SMD_MAGIC3 0xBB
 #define SMD_BLOCK_SIZE 0x4000
 
-int load_smd_rom(long filesize, FILE * f, void **buffer)
+#ifdef DISABLE_ZLIB
+#define ROMFILE FILE*
+#define romopen fopen
+#define romread fread
+#define romseek fseek
+#define romgetc fgetc
+#define romclose fclose
+#else
+#include "zlib/zlib.h"
+#define ROMFILE gzFile
+#define romopen gzopen
+#define romread gzfread
+#define romseek gzseek
+#define romgetc gzgetc
+#define romclose gzclose
+#endif
+
+int load_smd_rom(ROMFILE f, void **buffer)
 {
 	uint8_t block[SMD_BLOCK_SIZE];
-	filesize -= SMD_HEADER_SIZE;
-	fseek(f, SMD_HEADER_SIZE, SEEK_SET);
+	romseek(f, SMD_HEADER_SIZE, SEEK_SET);
+
+	size_t filesize = 512 * 1024;
+	size_t readsize = 0;
+	uint16_t *dst = malloc(filesize);
+	
 
-	uint16_t *dst = *buffer = malloc(nearest_pow2(filesize));
-	int rom_size = filesize;
-	while (filesize > 0) {
-		fread(block, 1, SMD_BLOCK_SIZE, f);
-		for (uint8_t *low = block, *high = (block+SMD_BLOCK_SIZE/2), *end = block+SMD_BLOCK_SIZE; high < end; high++, low++) {
-			*(dst++) = *low << 8 | *high;
+	size_t read;
+	do {
+		if ((readsize + SMD_BLOCK_SIZE > filesize)) {
+			filesize *= 2;
+			dst = realloc(dst, filesize);
 		}
-		filesize -= SMD_BLOCK_SIZE;
-	}
-	return rom_size;
+		read = romread(block, 1, SMD_BLOCK_SIZE, f);
+		if (read > 0) {
+			for (uint8_t *low = block, *high = (block+read/2), *end = block+read; high < end; high++, low++) {
+				*(dst++) = *low << 8 | *high;
+			}
+			readsize += read;
+		}
+	} while(read > 0);
+	romclose(f);
+	
+	*buffer = dst;
+	
+	return readsize;
 }
 
-uint32_t load_rom(char * filename, void **dst, system_type *stype)
+uint32_t load_rom_zip(const char *filename, void **dst)
+{
+	static const char *valid_exts[] = {"bin", "md", "gen", "sms", "rom"};
+	const uint32_t num_exts = sizeof(valid_exts)/sizeof(*valid_exts);
+	zip_file *z = zip_open(filename);
+	if (!z) {
+		return 0;
+	}
+	
+	for (uint32_t i = 0; i < z->num_entries; i++)
+	{
+		char *ext = path_extension(z->entries[i].name);
+		if (!ext) {
+			continue;
+		}
+		for (uint32_t j = 0; j < num_exts; j++)
+		{
+			if (!strcasecmp(ext, valid_exts[j])) {
+				size_t out_size = nearest_pow2(z->entries[i].size);
+				*dst = zip_read(z, i, &out_size);
+				if (*dst) {
+					free(ext);
+					zip_close(z);
+					return out_size;
+				}
+			}
+		}
+		free(ext);
+	}
+	zip_close(z);
+	return 0;
+}
+
+uint32_t load_rom(const char * filename, void **dst, system_type *stype)
 {
 	uint8_t header[10];
-	FILE * f = fopen(filename, "rb");
+	char *ext = path_extension(filename);
+	if (!strcasecmp(ext, "zip")) {
+		free(ext);
+		return load_rom_zip(filename, dst);
+	}
+	free(ext);
+	ROMFILE f = romopen(filename, "rb");
 	if (!f) {
 		return 0;
 	}
-	if (sizeof(header) != fread(header, 1, sizeof(header), f)) {
+	if (sizeof(header) != romread(header, 1, sizeof(header), f)) {
 		fatal_error("Error reading from %s\n", filename);
 	}
-	fseek(f, 0, SEEK_END);
-	long filesize = ftell(f);
-	fseek(f, 0, SEEK_SET);
+	
 	if (header[1] == SMD_MAGIC1 && header[8] == SMD_MAGIC2 && header[9] == SMD_MAGIC3) {
 		int i;
 		for (i = 3; i < 8; i++) {
@@ -96,15 +168,38 @@
 			if (stype) {
 				*stype = SYSTEM_GENESIS;
 			}
-			return load_smd_rom(filesize, f, dst);
+			return load_smd_rom(f, dst);
 		}
 	}
-	*dst = malloc(nearest_pow2(filesize));
-	if (filesize != fread(*dst, 1, filesize, f)) {
-		fatal_error("Error reading from %s\n", filename);
-	}
-	fclose(f);
-	return filesize;
+	
+	size_t filesize = 512 * 1024;
+	size_t readsize = sizeof(header);
+		
+	char *buf = malloc(filesize);
+	memcpy(buf, header, readsize);
+	
+	size_t read;
+	do {
+		read = romread(buf + readsize, 1, filesize - readsize, f);
+		if (read > 0) {
+			readsize += read;
+			if (readsize == filesize) {
+				int one_more = romgetc(f);
+				if (one_more >= 0) {
+					filesize *= 2;
+					buf = realloc(buf, filesize);
+					buf[readsize++] = one_more;
+				} else {
+					read = 0;
+				}
+			}
+		}
+	} while (read > 0);
+	
+	*dst = buf;
+	
+	romclose(f);
+	return readsize;
 }
 
 
@@ -157,9 +252,10 @@
 	return save_dir;
 }
 
-void setup_saves(system_media *media, rom_info *info, system_header *context)
+void setup_saves(system_media *media, system_header *context)
 {
 	static uint8_t persist_save_registered;
+	rom_info *info = &context->info;
 	char *save_dir = get_save_dir(info->is_save_lock_on ? media->chain : media);
 	char const *parts[] = {save_dir, PATH_SEP, info->save_type == SAVE_I2C ? "save.eeprom" : info->save_type == SAVE_NOR ? "save.nor" : "save.sram"};
 	free(save_filename);
@@ -168,7 +264,7 @@
 		//initial save dir was calculated based on lock-on cartridge because that's where the save device is
 		//save directory used for save states should still be located in the normal place
 		free(save_dir);
-		save_dir = get_save_dir(media);
+		parts[0] = save_dir = get_save_dir(media);
 	}
 	if (use_native_states || context->type != SYSTEM_GENESIS) {
 		parts[2] = "quicksave.state";
@@ -187,29 +283,45 @@
 	}
 }
 
+void apply_updated_config(void)
+{
+	render_config_updated();
+	if (current_system && current_system->config_updated) {
+		current_system->config_updated(current_system);
+	}
+}
+
 static void on_drag_drop(const char *filename)
 {
-	if (current_system->next_rom) {
-		free(current_system->next_rom);
-	}
-	current_system->next_rom = strdup(filename);
-	current_system->request_exit(current_system);
-	if (menu_system && menu_system->type == SYSTEM_GENESIS) {
-		genesis_context *gen = (genesis_context *)menu_system;
-		if (gen->extra) {
-			menu_context *menu = gen->extra;
-			menu->external_game_load = 1;
-		} else {
-			puts("No extra");
+	if (current_system) {
+		if (current_system->next_rom) {
+			free(current_system->next_rom);
+		}
+		current_system->next_rom = strdup(filename);
+		current_system->request_exit(current_system);
+		if (menu_system && menu_system->type == SYSTEM_GENESIS) {
+			genesis_context *gen = (genesis_context *)menu_system;
+			if (gen->extra) {
+				menu_context *menu = gen->extra;
+				menu->external_game_load = 1;
+			}
 		}
 	} else {
-		puts("no menu");
+		init_system_with_media(filename, SYSTEM_UNKNOWN);
 	}
+#ifndef DISABLE_NUKLEAR
+	if (is_nuklear_active()) {
+		show_play_view();
+	}
+#endif
 }
 
 static system_media cart, lock_on;
 void reload_media(void)
 {
+	if (!current_system) {
+		return;
+	}
 	if (current_system->next_rom) {
 		free(current_system->next_rom);
 	}
@@ -238,6 +350,58 @@
 	lock_on.size = load_rom(lock_on_path, &lock_on.buffer, NULL);
 }
 
+static uint32_t opts = 0;
+static uint8_t force_region = 0;
+void init_system_with_media(const char *path, system_type force_stype)
+{
+	if (game_system) {
+		game_system->persist_save(game_system);
+#ifdef USE_NATIVE
+		//swap to game context arena and mark all allocated pages in it free
+		if (current_system == menu_system) {
+			current_system->arena = set_current_arena(game_system->arena);
+		}
+		mark_all_free();
+#endif
+		game_system->free_context(game_system);
+#ifdef USE_NATIVE
+	} else if(current_system) {
+		//start a new arena and save old one in suspended system context
+		current_system->arena = start_new_arena();
+#endif
+	}
+	system_type stype = SYSTEM_UNKNOWN;
+	if (!(cart.size = load_rom(path, &cart.buffer, &stype))) {
+		fatal_error("Failed to open %s for reading\n", path);
+	}
+	free(cart.dir);
+	free(cart.name);
+	free(cart.extension);
+	cart.dir = path_dirname(path);
+	cart.name = basename_no_extension(path);
+	cart.extension = path_extension(path);
+	if (force_stype != SYSTEM_UNKNOWN) {
+		stype = force_stype;
+	}
+	if (stype == SYSTEM_UNKNOWN) {
+		stype = detect_system_type(&cart);
+	}
+	if (stype == SYSTEM_UNKNOWN) {
+		fatal_error("Failed to detect system type for %s\n", path);
+	}
+	//allocate new system context
+	game_system = alloc_config_system(stype, &cart, opts, force_region);
+	if (!game_system) {
+		fatal_error("Failed to configure emulated machine for %s\n", path);
+	}
+	if (menu_system) {
+		menu_system->next_context = game_system;
+	}
+	game_system->next_context = menu_system;
+	setup_saves(&cart, game_system);
+	update_title(game_system->info.name);
+}
+
 int main(int argc, char ** argv)
 {
 	set_exe_str(argv[0]);
@@ -245,10 +409,8 @@
 	int width = -1;
 	int height = -1;
 	int debug = 0;
-	uint32_t opts = 0;
 	int loaded = 0;
 	system_type stype = SYSTEM_UNKNOWN, force_stype = SYSTEM_UNKNOWN;
-	uint8_t force_region = 0;
 	char * romfname = NULL;
 	char * statefile = NULL;
 	debugger_type dtype = DEBUGGER_NATIVE;
@@ -387,35 +549,6 @@
 			height = atoi(argv[i]);
 		}
 	}
-	uint8_t menu = !loaded;
-	if (!loaded) {
-		//load menu
-		romfname = tern_find_path(config, "ui\0rom\0", TVAL_PTR).ptrval;
-		if (!romfname) {
-			romfname = "menu.bin";
-		}
-		if (is_absolute_path(romfname)) {
-			if (!(cart.size = load_rom(romfname, &cart.buffer, &stype))) {
-				fatal_error("Failed to open UI ROM %s for reading", romfname);
-			}
-		} else {
-			cart.buffer = (uint16_t *)read_bundled_file(romfname, &cart.size);
-			if (!cart.buffer) {
-				fatal_error("Failed to open UI ROM %s for reading", romfname);
-			}
-			uint32_t rom_size = nearest_pow2(cart.size);
-			if (rom_size > cart.size) {
-				cart.buffer = realloc(cart.buffer, rom_size);
-				cart.size = rom_size;
-			}
-		}
-		//force system detection, value on command line is only for games not the menu
-		stype = detect_system_type(&cart);
-		cart.dir = path_dirname(romfname);
-		cart.name = basename_no_extension(romfname);
-		cart.extension = path_extension(romfname);
-		loaded = 1;
-	}
 	
 	int def_width = 0, def_height = 0;
 	char *config_width = tern_find_path(config, "video\0width\0", TVAL_PTR).ptrval;
@@ -443,17 +576,40 @@
 		render_init(width, height, "BlastEm", fullscreen);
 		render_set_drag_drop_handler(on_drag_drop);
 	}
-
-	if (stype == SYSTEM_UNKNOWN) {
+	set_bindings();
+	
+	uint8_t menu = !loaded;
+	uint8_t use_nuklear = 0;
+#ifndef DISABLE_NUKLEAR
+	use_nuklear = is_nuklear_available();
+#endif
+	if (!loaded && !use_nuklear) {
+		//load menu
+		romfname = tern_find_path(config, "ui\0rom\0", TVAL_PTR).ptrval;
+		if (!romfname) {
+			romfname = "menu.bin";
+		}
+		if (is_absolute_path(romfname)) {
+			if (!(cart.size = load_rom(romfname, &cart.buffer, &stype))) {
+				fatal_error("Failed to open UI ROM %s for reading", romfname);
+			}
+		} else {
+			cart.buffer = (uint16_t *)read_bundled_file(romfname, &cart.size);
+			if (!cart.buffer) {
+				fatal_error("Failed to open UI ROM %s for reading", romfname);
+			}
+			uint32_t rom_size = nearest_pow2(cart.size);
+			if (rom_size > cart.size) {
+				cart.buffer = realloc(cart.buffer, rom_size);
+				cart.size = rom_size;
+			}
+		}
+		//force system detection, value on command line is only for games not the menu
 		stype = detect_system_type(&cart);
-	}
-	if (stype == SYSTEM_UNKNOWN) {
-		fatal_error("Failed to detect system type for %s\n", romfname);
-	}
-	rom_info info;
-	current_system = alloc_config_system(stype, &cart, menu ? 0 : opts, force_region, &info);
-	if (!current_system) {
-		fatal_error("Failed to configure emulated machine for %s\n", romfname);
+		cart.dir = path_dirname(romfname);
+		cart.name = basename_no_extension(romfname);
+		cart.extension = path_extension(romfname);
+		loaded = 1;
 	}
 	char *state_format = tern_find_path(config, "ui\0state_format\0", TVAL_PTR).ptrval;
 	if (state_format && !strcmp(state_format, "gst")) {
@@ -461,14 +617,36 @@
 	} else if (state_format && strcmp(state_format, "native")) {
 		warning("%s is not a valid value for the ui.state_format setting. Valid values are gst and native\n", state_format);
 	}
-	setup_saves(&cart, &info, current_system);
-	update_title(info.name);
-	if (menu) {
-		menu_system = current_system;
-	} else {
-		game_system = current_system;
+
+	if (loaded) {
+		if (stype == SYSTEM_UNKNOWN) {
+			stype = detect_system_type(&cart);
+		}
+		if (stype == SYSTEM_UNKNOWN) {
+			fatal_error("Failed to detect system type for %s\n", romfname);
+		}
+		current_system = alloc_config_system(stype, &cart, menu ? 0 : opts, force_region);
+		if (!current_system) {
+			fatal_error("Failed to configure emulated machine for %s\n", romfname);
+		}
+	
+		setup_saves(&cart, current_system);
+		update_title(current_system->info.name);
+		if (menu) {
+			menu_system = current_system;
+		} else {
+			game_system = current_system;
+		}
 	}
-
+	
+#ifndef DISABLE_NUKLEAR
+	if (use_nuklear) {
+		blastem_nuklear_init(!menu);
+		current_system = game_system;
+		menu = 0;
+	}
+#endif
+	
 	current_system->debugger_type = dtype;
 	current_system->enter_debugger = start_in_debugger && menu == debug_target;
 	current_system->start_context(current_system,  menu ? NULL : statefile);
@@ -480,49 +658,7 @@
 		if (current_system->next_rom) {
 			char *next_rom = current_system->next_rom;
 			current_system->next_rom = NULL;
-			if (game_system) {
-				game_system->persist_save(game_system);
-#ifdef USE_NATIVE
-				//swap to game context arena and mark all allocated pages in it free
-				if (menu) {
-					current_system->arena = set_current_arena(game_system->arena);
-				}
-				mark_all_free();
-#endif
-				game_system->free_context(game_system);
-			} else {
-#ifdef USE_NATIVE
-				//start a new arena and save old one in suspended genesis context
-				current_system->arena = start_new_arena();
-#endif
-			}
-			if (!(cart.size = load_rom(next_rom, &cart.buffer, &stype))) {
-				fatal_error("Failed to open %s for reading\n", next_rom);
-			}
-			free(cart.dir);
-			free(cart.name);
-			free(cart.extension);
-			cart.dir = path_dirname(next_rom);
-			cart.name = basename_no_extension(next_rom);
-			cart.extension = path_extension(next_rom);
-			stype = force_stype;
-			if (stype == SYSTEM_UNKNOWN) {
-				stype = detect_system_type(&cart);
-			}
-			if (stype == SYSTEM_UNKNOWN) {
-				fatal_error("Failed to detect system type for %s\n", next_rom);
-			}
-			//allocate new system context
-			game_system = alloc_config_system(stype, &cart, opts,force_region, &info);
-			if (!game_system) {
-				fatal_error("Failed to configure emulated machine for %s\n", next_rom);
-			}
-			if (menu_system) {
-				menu_system->next_context = game_system;
-			}
-			game_system->next_context = menu_system;
-			setup_saves(&cart, &info, game_system);
-			update_title(info.name);
+			init_system_with_media(next_rom, force_stype);
 			free(next_rom);
 			menu = 0;
 			current_system = game_system;
@@ -536,13 +672,21 @@
 			current_system = game_system;
 			menu = 0;
 			current_system->resume_context(current_system);
-		} else if (!menu && menu_system) {
-#ifdef USE_NATIVE
-			current_system->arena = set_current_arena(menu_system->arena);
+		} else if (!menu && (menu_system || use_nuklear)) {
+			if (use_nuklear) {
+#ifndef DISABLE_NUKLEAR
+				ui_idle_loop();
 #endif
-			current_system = menu_system;
-			menu = 1;
-			current_system->resume_context(current_system);
+			} else {
+#ifdef USE_NATIVE
+				current_system->arena = set_current_arena(menu_system->arena);
+#endif
+				current_system = menu_system;
+				menu = 1;
+			}
+			if (!current_system->next_rom) {
+				current_system->resume_context(current_system);
+			}
 		} else {
 			break;
 		}
--- a/blastem.h	Sun Dec 31 10:11:16 2017 -0800
+++ b/blastem.h	Tue Dec 25 11:12:26 2018 -0800
@@ -15,8 +15,9 @@
 extern char *save_state_path;
 extern char *save_filename;
 extern uint8_t use_native_states;
-#define QUICK_SAVE_SLOT 10
 void reload_media(void);
 void lockon_media(char *lock_on_path);
+void init_system_with_media(const char *path, system_type force_stype);
+void apply_updated_config(void);
 
 #endif //BLASTEM_H_
--- a/config.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/config.c	Tue Dec 25 11:12:26 2018 -0800
@@ -5,6 +5,7 @@
 */
 #include "tern.h"
 #include "util.h"
+#include "paths.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -36,7 +37,7 @@
 }
 #endif
 
-tern_node * parse_config_int(char **state, int started, int *line)
+static tern_node * parse_config_int(char **state, int started, int *line)
 {
 	char *config_data, *curline;
 	tern_node * head = NULL;
@@ -88,6 +89,80 @@
 	return parse_config_int(&config_data, 0, &line);
 }
 
+typedef struct {
+	char     *buf;
+	uint32_t capacity;
+	uint32_t size;
+	uint32_t indent;
+} serialize_state;
+
+static void ensure_buf_capacity(uint32_t ensure, serialize_state *state)
+{
+	if (ensure + state->size > state->capacity) {
+		state->capacity = state->capacity * 2;
+		state->buf = realloc(state->buf, state->capacity);
+	}
+}
+
+static void indent(serialize_state *state)
+{
+	memset(state->buf + state->size, '\t', state->indent);
+	state->size += state->indent;
+}
+
+static void serialize_config_int(tern_node *config, serialize_state *state);
+
+static void serialize_iter(char *key, tern_val val, uint8_t valtype, void *data)
+{
+	serialize_state *state = data;
+	uint32_t keylen = strlen(key);
+	uint32_t vallen = 0;
+	if (valtype == TVAL_PTR) {
+		vallen = strlen(val.ptrval);
+	}
+	ensure_buf_capacity(state->indent + keylen + 2 + vallen, state);
+	state->buf[state->size++] = '\n';
+	indent(state);
+	memcpy(state->buf + state->size, key, keylen);
+	state->size += keylen;
+	state->buf[state->size++] = ' ';
+	if (valtype == TVAL_PTR) {
+		memcpy(state->buf + state->size, val.ptrval, vallen);
+		state->size += vallen;
+	} else {
+		serialize_config_int(val.ptrval, state);
+	}
+}
+
+static void serialize_config_int(tern_node *config, serialize_state *state)
+{
+	ensure_buf_capacity(1, state);
+	state->buf[state->size++] = '{';
+	state->indent++;
+	
+	tern_foreach(config, serialize_iter, state);
+
+	--state->indent;
+	ensure_buf_capacity(2 + state->indent, state);
+	state->buf[state->size++] = '\n';
+	indent(state);
+	state->buf[state->size++] = '}';
+}
+
+char *serialize_config(tern_node *config, uint32_t *size_out)
+{
+	serialize_state state = {
+		.size = 0,
+		.capacity = 1024,
+		.indent = 0
+	};
+	state.buf = malloc(state.capacity);
+	tern_foreach(config, serialize_iter, &state);
+	//serialize_config_int(config, &state);
+	*size_out = state.size;
+	return state.buf;
+}
+
 tern_node *parse_config_file(char *config_path)
 {
 	tern_node * ret = NULL;
@@ -114,6 +189,20 @@
 	return ret;
 }
 
+uint8_t serialize_config_file(tern_node *config, char *path)
+{
+	FILE *f = fopen(path, "w");
+	if (!f) {
+		return 0;
+	}
+	uint32_t buf_size;
+	char *buffer = serialize_config(config, &buf_size);
+	uint8_t ret = buf_size == fwrite(buffer, 1, buf_size, f);
+	free(buffer);
+	fclose(f);
+	return ret;
+}
+
 tern_node *parse_bundled_config(char *config_name)
 {
 	uint32_t confsize;
@@ -127,13 +216,35 @@
 	return ret;
 }
 
-tern_node *load_config()
+tern_node *load_overrideable_config(char *name, char *bundled_name)
 {
 	char const *confdir = get_config_dir();
 	char *confpath = NULL;
 	tern_node *ret;
 	if (confdir) {
-		confpath = alloc_concat(confdir, "/blastem.cfg");
+		confpath = path_append(confdir, name);
+		ret = parse_config_file(confpath);
+		if (ret) {
+			free(confpath);
+			return ret;
+		}
+	}
+
+	ret = parse_bundled_config(bundled_name);
+	if (ret) {
+		free(confpath);
+		return ret;
+	}
+	return NULL;
+}
+
+tern_node *load_config()
+{
+	char const *confdir = get_config_dir();
+	char *confpath = NULL;
+	tern_node *ret = load_overrideable_config("blastem.cfg", "default.cfg");
+	if (confdir) {
+		confpath = path_append(confdir, "blastem.cfg");
 		ret = parse_config_file(confpath);
 		if (ret) {
 			free(confpath);
@@ -147,11 +258,56 @@
 		return ret;
 	}
 
-	if (confpath) {
-		fatal_error("Failed to find a config file at %s or in the blastem executable directory\n", confpath);
+	if (get_config_dir()) {
+		fatal_error("Failed to find a config file at %s or in the blastem executable directory\n", get_config_dir());
 	} else {
 		fatal_error("Failed to find a config file in the BlastEm executable directory and the config directory path could not be determined\n");
 	}
 	//this will never get reached, but the compiler doesn't know that. Let's make it happy
 	return NULL;
 }
+
+void persist_config_at(tern_node *config, char *fname)
+{
+	char const *confdir = get_config_dir();
+	if (!confdir) {
+		fatal_error("Failed to locate config file directory\n");
+	}
+	ensure_dir_exists(confdir);
+	char *confpath = path_append(confdir, fname);
+	if (!serialize_config_file(config, confpath)) {
+		fatal_error("Failed to write config to %s\n", confpath);
+	}
+	free(confpath);
+}
+
+void persist_config(tern_node *config)
+{
+	persist_config_at(config, "blastem.cfg");
+}
+
+char **get_extension_list(tern_node *config, uint32_t *num_exts_out)
+{
+	char *ext_filter = strdup(tern_find_path_default(config, "ui\0extensions\0", (tern_val){.ptrval = "bin gen md smd sms gg"}, TVAL_PTR).ptrval);
+	uint32_t num_exts = 0, ext_storage = 5;
+	char **ext_list = malloc(sizeof(char *) * ext_storage);
+	char *cur_filter = ext_filter;
+	while (*cur_filter)
+	{
+		if (num_exts == ext_storage) {
+			ext_storage *= 2;
+			ext_list = realloc(ext_list, sizeof(char *) * ext_storage);
+		}
+		ext_list[num_exts++] = cur_filter;
+		cur_filter = split_keyval(cur_filter);
+	}
+	*num_exts_out = num_exts;
+	return ext_list;
+}
+
+#define DEFAULT_LOWPASS_CUTOFF 3390
+uint32_t get_lowpass_cutoff(tern_node *config)
+{
+	char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0", TVAL_PTR).ptrval;
+	return lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : DEFAULT_LOWPASS_CUTOFF;
+}
--- a/config.h	Sun Dec 31 10:11:16 2017 -0800
+++ b/config.h	Tue Dec 25 11:12:26 2018 -0800
@@ -9,7 +9,14 @@
 
 tern_node *parse_config_file(char *config_path);
 tern_node *parse_bundled_config(char *config_name);
+tern_node *load_overrideable_config(char *name, char *bundled_name);
 tern_node *load_config();
+char *serialize_config(tern_node *config, uint32_t *size_out);
+uint8_t serialize_config_file(tern_node *config, char *path);
+void persist_config_at(tern_node *config, char *fname);
+void persist_config(tern_node *config);
+char **get_extension_list(tern_node *config, uint32_t *num_exts_out);
+uint32_t get_lowpass_cutoff(tern_node *config);
 
 #endif //CONFIG_H_
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/controller_info.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,334 @@
+#include <string.h>
+#include "render_sdl.h"
+#include "controller_info.h"
+#include "config.h"
+#include "util.h"
+
+typedef struct {
+	char const      *name;
+	controller_info info;
+} heuristic;
+
+static heuristic heuristics[] = {
+	//TODO: Add more heuristic rules
+	{"DualShock 4", {.type = TYPE_PSX, .subtype = SUBTYPE_PS4}},
+	{"PS4", {.type = TYPE_PSX, .subtype = SUBTYPE_PS4}},
+	{"PS3", {.type = TYPE_PSX, .subtype = SUBTYPE_PS3}},
+	{"X360", {.type = TYPE_XBOX, .subtype = SUBTYPE_X360}},
+	{"Xbox 360", {.type = TYPE_XBOX, .subtype = SUBTYPE_X360}},
+	{"X-box 360", {.type = TYPE_XBOX, .subtype = SUBTYPE_X360}},
+	{"Xbox One", {.type = TYPE_XBOX, .subtype = SUBTYPE_XBONE}},
+	{"X-box One", {.type = TYPE_XBOX, .subtype = SUBTYPE_XBONE}},
+	{"WiiU", {.type = TYPE_NINTENDO, .subtype = SUBTYPE_WIIU}},
+	{"Wii U", {.type = TYPE_NINTENDO, .subtype = SUBTYPE_WIIU}},
+	{"Nintendo Switch", {.type = TYPE_NINTENDO, .subtype = SUBTYPE_SWITCH}},
+	{"Saturn", {.type = TYPE_SEGA, .subtype = SUBTYPE_SATURN}}
+};
+const uint32_t num_heuristics = sizeof(heuristics)/sizeof(*heuristics);
+
+static tern_node *info_config;
+static uint8_t loaded;
+static const char *subtype_names[] = {
+	"unknown",
+	"xbox",
+	"xbox 360",
+	"xbone",
+	"ps2",
+	"ps3",
+	"ps4",
+	"wiiu",
+	"switch",
+	"genesis",
+	"saturn"
+};
+static const char *subtype_human_names[] = {
+	"unknown",
+	"Xbos",
+	"Xbox 360",
+	"Xbox One",
+	"PS2",
+	"PS3",
+	"PS4",
+	"Wii-U",
+	"Switch",
+	"Genesis",
+	"Saturn"
+};
+static const char *variant_names[] = {
+	"normal",
+	"6b bumpers",
+	"6b right"
+};
+
+static void load_ctype_config(void)
+{
+	if (!loaded) {
+		info_config = load_overrideable_config("controller_types.cfg", "controller_types.cfg");
+		loaded = 1;
+	}
+}
+
+controller_info get_controller_info(int joystick)
+{
+	load_ctype_config();
+	char guid_string[33];
+	SDL_Joystick *stick = render_get_joystick(joystick);
+	SDL_GameController *control = render_get_controller(joystick);
+	SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(stick), guid_string, sizeof(guid_string));
+	tern_node *info = tern_find_node(info_config, guid_string);
+	if (info) {
+		controller_info res;
+		char *subtype = tern_find_ptr(info, "subtype");
+		res.subtype = SUBTYPE_UNKNOWN;
+		if (subtype) {
+			for (int i = 0; i < SUBTYPE_NUM; i++)
+			{
+				if (!strcmp(subtype_names[i], subtype)) {
+					res.subtype = i;
+					break;
+				}
+			}
+		}
+		switch (res.subtype)
+		{
+		case SUBTYPE_XBOX:
+		case SUBTYPE_X360:
+		case SUBTYPE_XBONE:
+			res.type = TYPE_XBOX;
+			break;
+		case SUBTYPE_PS2:
+		case SUBTYPE_PS3:
+		case SUBTYPE_PS4:
+			res.type = TYPE_PSX;
+			break;
+		case SUBTYPE_WIIU:
+		case SUBTYPE_SWITCH:
+			res.type = TYPE_NINTENDO;
+			break;
+		case SUBTYPE_GENESIS:
+		case SUBTYPE_SATURN:
+			res.type = TYPE_SEGA;
+			break;
+		default:
+			res.type = TYPE_UNKNOWN;
+			break;
+		}
+		char *variant = tern_find_ptr(info, "variant");
+		res.variant = VARIANT_NORMAL;
+		if (variant) {
+			for (int i = 0; i < VARIANT_NUM; i++)
+			{
+				if (!strcmp(variant_names[i], variant)) {
+					res.variant = i;
+					break;
+				}
+			}
+		}
+		res.name = control ? SDL_GameControllerName(control) : SDL_JoystickName(stick);
+		if (control) {
+			SDL_GameControllerClose(control);
+		}
+		return res;
+	}
+	if (!control) {
+		return (controller_info) {
+			.type = TYPE_UNKNOWN,
+			.subtype = SUBTYPE_UNKNOWN,
+			.variant = VARIANT_NORMAL,
+			.name = SDL_JoystickName(stick)
+		};
+	}
+	const char *name = SDL_GameControllerName(control);
+	SDL_GameControllerClose(control);
+	for (uint32_t i = 0; i < num_heuristics; i++)
+	{
+		if (strstr(name, heuristics[i].name)) {
+			controller_info res = heuristics[i].info;
+			res.name = name;
+			return res;
+		}
+	}
+	//default to a 360
+	return (controller_info){
+		.type = TYPE_GENERIC_MAPPING,
+		.subtype = SUBTYPE_UNKNOWN,
+		.variant = VARIANT_NORMAL,
+		.name = name
+	};
+}
+
+static void mappings_iter(char *key, tern_val val, uint8_t valtype, void *data)
+{
+	if (valtype != TVAL_NODE) {
+		return;
+	}
+	char *mapping = tern_find_ptr(val.ptrval, "mapping");
+	if (mapping) {
+		const char *parts[] = {key, ",", mapping};
+		char * full = alloc_concat_m(3, parts);
+		SDL_GameControllerAddMapping(full);
+		free(full);
+	}
+}
+
+void controller_add_mappings(void)
+{
+	load_ctype_config();
+	if (info_config) {
+		tern_foreach(info_config, mappings_iter, NULL);
+	}
+}
+
+void save_controller_info(int joystick, controller_info *info)
+{
+	char guid_string[33];
+	SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(render_get_joystick(joystick)), guid_string, sizeof(guid_string));
+	tern_node *existing = tern_find_node(info_config, guid_string);
+	existing = tern_insert_ptr(existing, "subtype", (void *)subtype_names[info->subtype]);
+	existing = tern_insert_ptr(existing, "variant",  (void *)variant_names[info->variant]);
+	info_config = tern_insert_node(info_config, guid_string, existing);
+	persist_config_at(info_config, "controller_types.cfg");
+	
+}
+
+void save_controller_mapping(int joystick, char *mapping_string)
+{
+	char guid_string[33];
+	SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(render_get_joystick(joystick)), guid_string, sizeof(guid_string));
+	tern_node *existing = tern_find_node(info_config, guid_string);
+	existing = tern_insert_ptr(existing, "mapping", mapping_string);
+	info_config = tern_insert_node(info_config, guid_string, existing);
+	persist_config_at(info_config, "controller_types.cfg");
+}
+
+char const *labels_xbox[] = {
+	"A", "B", "X", "Y", "Back", NULL, "Start", "Click", "Click", "White", "Black", "LT", "RT"
+};
+char const *labels_360[] = {
+	"A", "B", "X", "Y", "Back", "Xbox", "Start", "Click", "Click", "LB", "RB", "LT", "RT"
+};
+static char const *labels_xbone[] = {
+	"A", "B", "X", "Y", "View", "Xbox", "Menu", "Click", "Click", "LB", "RB", "LT", "RT"
+};
+static char const *labels_ps3[] = {
+	"cross", "circle", "square", "triangle", "Select", "PS", "Start", "L3", "R3", "L1", "R1", "L2", "R2"
+};
+static char const *labels_ps4[] = {
+	"cross", "circle", "square", "triangle", "Share", "PS", "Options", "L3", "R3", "L1", "R1", "L2", "R2"
+};
+static char const *labels_nintendo[] = {
+	"B", "A", "Y", "X", "-", "Home", "+", "Click", "Click", "L", "R", "ZL", "ZR"
+};
+static char const *labels_genesis[] = {
+	"A", "B", "X", "Y", NULL, NULL, "Start", NULL, NULL, "Z", "C", NULL, "Mode"
+};
+static char const *labels_saturn[] = {
+	"A", "B", "X", "Y", NULL, NULL, "Start", NULL, NULL, "Z", "C", "LT", "RT"
+};
+
+static const char** label_source(controller_info *info)
+{
+	if (info->type == TYPE_UNKNOWN || info->type == TYPE_GENERIC_MAPPING || info->subtype ==SUBTYPE_X360) {
+		return labels_360;
+	} else if (info->type == TYPE_NINTENDO) {
+		return labels_nintendo;
+	} else if (info->type == TYPE_PSX) {
+		if (info->subtype == SUBTYPE_PS4) {
+			return labels_ps4;
+		} else {
+			return labels_ps3;
+		}
+	} else if (info->type == TYPE_XBOX) {
+		if (info->subtype == SUBTYPE_XBONE) {
+			return labels_xbone;
+		} else {
+			return labels_xbox;
+		}
+	} else {
+		if (info->subtype == SUBTYPE_GENESIS) {
+			return labels_genesis;
+		} else {
+			return labels_saturn;
+		}
+	}
+}
+
+const char *get_button_label(controller_info *info, int button)
+{
+	if (button >= SDL_CONTROLLER_BUTTON_DPAD_UP) {
+		static char const * dirs[] = {"Up", "Down", "Left", "Right"};
+		return dirs[button - SDL_CONTROLLER_BUTTON_DPAD_UP];
+	}
+	return label_source(info)[button];
+}
+
+static char const *axis_labels[] = {
+	"Left X", "Left Y", "Right X", "Right Y"
+};
+const char *get_axis_label(controller_info *info, int axis)
+{
+	if (axis < SDL_CONTROLLER_AXIS_TRIGGERLEFT) {
+		return axis_labels[axis];
+	} else {
+		return label_source(info)[axis - SDL_CONTROLLER_AXIS_TRIGGERLEFT + SDL_CONTROLLER_BUTTON_RIGHTSHOULDER + 1];
+	}
+}
+
+char *make_controller_type_key(controller_info *info)
+{
+	const char *subtype;
+	if (info->subtype == SUBTYPE_UNKNOWN) {
+		switch(info->type)
+		{
+		case TYPE_XBOX:
+			subtype = subtype_names[SUBTYPE_X360];
+			break;
+		case TYPE_PSX:
+			subtype = subtype_names[SUBTYPE_PS4];
+			break;
+		case TYPE_NINTENDO:
+			subtype = subtype_names[SUBTYPE_SWITCH];
+			break;
+		default:
+			subtype = "unknown";
+		}
+	} else {
+		subtype = subtype_names[info->subtype];
+	}
+	const char *variant = variant_names[info->variant];
+	const char *parts[] = {subtype, "_", variant};
+	char *ret = alloc_concat_m(3, parts);
+	for (char *cur = ret; *cur; cur++)
+	{
+		if (*cur == ' ')
+		{
+			*cur = '_';
+		}
+	}
+	return ret;
+}
+
+char *make_human_readable_type_name(controller_info *info)
+{
+	const char *base = subtype_human_names[info->subtype];
+	char *prefix;
+	if (info->variant == VARIANT_NORMAL) {
+		prefix = "Normal ";
+	} else {
+		static const char *parts[] = {"6 button (", NULL, "/", NULL, ") "};
+		if (info->variant == VARIANT_6B_BUMPERS) {
+			parts[1] = get_button_label(info, SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
+			parts[3] = get_button_label(info, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
+		} else {
+			parts[1] = get_button_label(info, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
+			parts[3] = get_axis_label(info, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
+		}
+		prefix = alloc_concat_m(5, parts);
+	}
+	char *ret = alloc_concat(prefix, base);
+	if (info->variant != VARIANT_NORMAL) {
+		free(prefix);
+	}
+	return ret;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/controller_info.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,52 @@
+#ifndef CONTROLLER_INFO_H_
+#define CONTROLLER_INFO_H_
+#include <stdint.h>
+
+enum {
+	TYPE_UNKNOWN,
+	TYPE_GENERIC_MAPPING,
+	TYPE_XBOX,
+	TYPE_PSX,
+	TYPE_NINTENDO,
+	TYPE_SEGA
+};
+
+enum {
+	SUBTYPE_UNKNOWN,
+	SUBTYPE_XBOX,
+	SUBTYPE_X360,
+	SUBTYPE_XBONE,
+	SUBTYPE_PS2,
+	SUBTYPE_PS3,
+	SUBTYPE_PS4,
+	SUBTYPE_WIIU,
+	SUBTYPE_SWITCH,
+	SUBTYPE_GENESIS,
+	SUBTYPE_SATURN,
+	SUBTYPE_NUM
+};
+
+enum {
+	VARIANT_NORMAL,
+	VARIANT_6B_BUMPERS, //C and Z positions are RB and LB respectively
+	VARIANT_6B_RIGHT, //C and Z positions are RT and RB respectively
+	VARIANT_NUM
+};
+
+typedef struct {
+	char const *name;
+	uint8_t    type;
+	uint8_t    subtype;
+	uint8_t    variant;
+} controller_info;
+
+controller_info get_controller_info(int index);
+const char *get_button_label(controller_info *info, int button);
+const char *get_axis_label(controller_info *info, int axis);
+void save_controller_info(int joystick, controller_info *info);
+void save_controller_mapping(int joystick, char *mapping_string);
+void controller_add_mappings(void);
+char *make_controller_type_key(controller_info *info);
+char *make_human_readable_type_name(controller_info *info);
+
+#endif //CONTROLLER_INFO_H_
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cpu_dsl.py	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,1194 @@
+#!/usr/bin/env python3
+
+
+class Block:
+	def addOp(self, op):
+		pass
+	
+	def processLine(self, parts):
+		if parts[0] == 'switch':
+			o = Switch(self, parts[1])
+			self.addOp(o)
+			return o
+		elif parts[0] == 'if':
+			o = If(self, parts[1])
+			self.addOp(o)
+			return o
+		elif parts[0] == 'end':
+			raise Exception('end is only allowed inside a switch or if block')
+		else:
+			self.addOp(NormalOp(parts))
+		return self
+		
+	def resolveLocal(self, name):
+		return None
+			
+class ChildBlock(Block):
+	def processLine(self, parts):
+		if parts[0] == 'end':
+			return self.parent
+		return super().processLine(parts)
+
+#Represents an instruction of the emulated CPU
+class Instruction(Block):
+	def __init__(self, value, fields, name):
+		self.value = value
+		self.fields = fields
+		self.name = name
+		self.implementation = []
+		self.locals = {}
+		self.regValues = {}
+		self.varyingBits = 0
+		self.invalidFieldValues = {}
+		self.newLocals = []
+		for field in fields:
+			self.varyingBits += fields[field][1]
+	
+	def addOp(self, op):
+		if op.op == 'local':
+			name = op.params[0]
+			size = op.params[1]
+			self.locals[name] = size
+		elif op.op == 'invalid':
+			name = op.params[0]
+			value = int(op.params[1])
+			self.invalidFieldValues.setdefault(name, set()).add(value)
+		else:
+			self.implementation.append(op)
+			
+	def resolveLocal(self, name):
+		if name in self.locals:
+			return name
+		return None
+	
+	def addLocal(self, name, size):
+		self.locals[name] = size
+		self.newLocals.append(name)
+		
+	def localSize(self, name):
+		return self.locals.get(name)
+			
+	def __lt__(self, other):
+		if isinstance(other, Instruction):
+			if self.varyingBits != other.varyingBits:
+				return self.varyingBits < other.varyingBits
+			return self.value < other.value
+		else:
+			return NotImplemented
+			
+	def allValues(self):
+		values = []
+		for i in range(0, 1 << self.varyingBits):
+			iword = self.value
+			doIt = True
+			for field in self.fields:
+				shift,bits = self.fields[field]
+				val = i & ((1 << bits) - 1)
+				if field in self.invalidFieldValues and val in self.invalidFieldValues[field]:
+					doIt = False
+					break
+				i >>= bits
+				iword |= val << shift
+			if doIt:
+				values.append(iword)
+		return values
+		
+	def getFieldVals(self, value):
+		fieldVals = {}
+		fieldBits = {}
+		for field in self.fields:
+			shift,bits = self.fields[field]
+			val = (value >> shift) & ((1 << bits) - 1)
+			fieldVals[field] = val
+			fieldBits[field] = bits
+		return (fieldVals, fieldBits)
+	
+	def generateName(self, value):
+		fieldVals,fieldBits = self.getFieldVals(value)
+		names = list(fieldVals.keys())
+		names.sort()
+		funName = self.name
+		for name in names:
+			funName += '_{0}_{1:0>{2}}'.format(name, bin(fieldVals[name])[2:], fieldBits[name])
+		return funName
+		
+	def generateBody(self, value, prog, otype):
+		output = []
+		prog.meta = {}
+		prog.pushScope(self)
+		self.regValues = {}
+		for var in self.locals:
+			output.append('\n\tuint{sz}_t {name};'.format(sz=self.locals[var], name=var))
+		self.newLocals = []
+		fieldVals,_ = self.getFieldVals(value)
+		for op in self.implementation:
+			op.generate(prog, self, fieldVals, output, otype)
+		begin = '\nvoid ' + self.generateName(value) + '(' + prog.context_type + ' *context)\n{'
+		if prog.needFlagCoalesce:
+			begin += prog.flags.coalesceFlags(prog, otype)
+		if prog.needFlagDisperse:
+			output.append(prog.flags.disperseFlags(prog, otype))
+		for var in self.newLocals:
+			begin += '\n\tuint{sz}_t {name};'.format(sz=self.locals[var], name=var)
+		prog.popScope()
+		return begin + ''.join(output) + '\n}'
+		
+	def __str__(self):
+		pieces = [self.name + ' ' + hex(self.value) + ' ' + str(self.fields)]
+		for name in self.locals:
+			pieces.append('\n\tlocal {0} {1}'.format(name, self.locals[name]))
+		for op in self.implementation:
+			pieces.append(str(op))
+		return ''.join(pieces)
+	
+#Represents the definition of a helper function
+class SubRoutine(Block):
+	def __init__(self, name):
+		self.name = name
+		self.implementation = []
+		self.args = []
+		self.arg_map = {}
+		self.locals = {}
+		self.regValues = {}
+	
+	def addOp(self, op):
+		if op.op == 'arg':
+			name = op.params[0]
+			size = op.params[1]
+			self.arg_map[name] = len(self.args)
+			self.args.append((name, size))
+		elif op.op == 'local':
+			name = op.params[0]
+			size = op.params[1]
+			self.locals[name] = size
+		else:
+			self.implementation.append(op)
+			
+	def resolveLocal(self, name):
+		if name in self.locals:
+			return self.name + '_' + name
+		return None
+	
+	def addLocal(self, name, size):
+		self.locals[name] = size
+	
+	def localSize(self, name):
+		return self.locals.get(name)
+			
+	def inline(self, prog, params, output, otype, parent):
+		if len(params) != len(self.args):
+			raise Exception('{0} expects {1} arguments, but was called with {2}'.format(self.name, len(self.args), len(params)))
+		argValues = {}
+		if parent:
+			self.regValues = parent.regValues
+		prog.pushScope(self)
+		i = 0
+		for name,size in self.args:
+			argValues[name] = params[i]
+			i += 1
+		for name in self.locals:
+			size = self.locals[name]
+			output.append('\n\tuint{size}_t {sub}_{local};'.format(size=size, sub=self.name, local=name))
+		for op in self.implementation:
+			op.generate(prog, self, argValues, output, otype)
+		prog.popScope()
+		
+	def __str__(self):
+		pieces = [self.name]
+		for name,size in self.args:
+			pieces.append('\n\targ {0} {1}'.format(name, size))
+		for name in self.locals:
+			pieces.append('\n\tlocal {0} {1}'.format(name, self.locals[name]))
+		for op in self.implementation:
+			pieces.append(str(op))
+		return ''.join(pieces)
+	
+class Op:
+	def __init__(self, evalFun = None):
+		self.evalFun = evalFun
+		self.impls = {}
+		self.outOp = ()
+	def cBinaryOperator(self, op):
+		def _impl(prog, params):
+			if op == '-':
+				a = params[1]
+				b = params[0]
+			else:
+				a = params[0]
+				b = params[1]
+			return '\n\t{dst} = {a} {op} {b};'.format(
+				dst = params[2], a = a, b = b, op = op
+			)
+		self.impls['c'] = _impl
+		self.outOp = (2,)
+		return self
+	def cUnaryOperator(self, op):
+		def _impl(prog, params):
+			return '\n\t{dst} = {op}{a};'.format(
+				dst = params[1], a = params[0], op = op
+			)
+		self.impls['c'] = _impl
+		self.outOp = (1,)
+		return self
+	def addImplementation(self, lang, outOp, impl):
+		self.impls[lang] = impl
+		if not outOp is None:
+			if type(outOp) is tuple:
+				self.outOp = outOp
+			else:
+				self.outOp = (outOp,)
+		return self
+	def evaluate(self, params):
+		return self.evalFun(*params)
+	def canEval(self):
+		return not self.evalFun is None
+	def numArgs(self):
+		return self.evalFun.__code__.co_argcount
+	def generate(self, otype, prog, params, rawParams):
+		if self.impls[otype].__code__.co_argcount == 2:
+			return self.impls[otype](prog, params)
+		else:
+			return self.impls[otype](prog, params, rawParams)
+		
+		
+def _xchgCImpl(prog, params, rawParams):
+	size = prog.paramSize(rawParams[0])
+	decl,name = prog.getTemp(size)
+	return decl + '\n\t{tmp} = {a};\n\t{a} = {b};\n\t{b} = {tmp};'.format(a = params[0], b = params[1], tmp = name)
+
+def _dispatchCImpl(prog, params):
+	if len(params) == 1:
+		table = 'main'
+	else:
+		table = params[1]
+	return '\n\timpl_{tbl}[{op}](context);'.format(tbl = table, op = params[0])
+
+def _updateFlagsCImpl(prog, params, rawParams):
+	i = 0
+	last = ''
+	autoUpdate = set()
+	explicit = {}
+	for c in params[0]:
+		if c.isdigit():
+			if last.isalpha():
+				num = int(c)
+				if num > 1:
+					raise Exception(c + ' is not a valid digit for update_flags')
+				explicit[last] = num
+				last = c
+			else:
+				raise Exception('Digit must follow flag letter in update_flags')
+		else:
+			if last.isalpha():
+				autoUpdate.add(last)
+			last = c
+	if last.isalpha():
+		autoUpdate.add(last)
+	output = []
+	#TODO: handle autoUpdate flags
+	for flag in autoUpdate:
+		calc = prog.flags.flagCalc[flag]
+		calc,_,resultBit = calc.partition('-')
+		lastDst = prog.resolveParam(prog.lastDst, None, {})
+		storage = prog.flags.getStorage(flag)
+		if calc == 'bit' or calc == 'sign':
+			if calc == 'sign':
+				resultBit = prog.paramSize(prog.lastDst) - 1
+			else:
+				resultBit = int(resultBit)
+			if type(storage) is tuple:
+				reg,storageBit = storage
+				reg = prog.resolveParam(reg, None, {})
+				if storageBit == resultBit:
+					#TODO: optimize this case
+					output.append('\n\t{reg} = ({reg} & ~{mask}U) | ({res} & {mask}U);'.format(
+						reg = reg, mask = 1 << resultBit, res = lastDst
+					))
+				else:
+					if resultBit > storageBit:
+						op = '>>'
+						shift = resultBit - storageBit
+					else:
+						op = '<<'
+						shift = storageBit - resultBit
+					output.append('\n\t{reg} = ({reg} & ~{mask}U) | ({res} {op} {shift}U & {mask}U);'.format(
+						reg = reg, mask = 1 << storageBit, res = lastDst, op = op, shift = shift
+					))
+			else:
+				reg = prog.resolveParam(storage, None, {})
+				output.append('\n\t{reg} = {res} & {mask}U;'.format(reg=reg, res=lastDst, mask = 1 << resultBit))
+		elif calc == 'zero':
+			if type(storage) is tuple:
+				reg,storageBit = storage
+				reg = prog.resolveParam(reg, None, {})
+				output.append('\n\t{reg} = {res} ? ({reg} & {mask}U) : ({reg} | {bit}U);'.format(
+					reg = reg, mask = ~(1 << storageBit), res = lastDst, bit = 1 << storageBit
+				))
+			elif prog.paramSize(prog.lastDst) > prog.paramSize(storage):
+				reg = prog.resolveParam(storage, None, {})
+				output.append('\n\t{reg} = {res} != 0;'.format(
+					reg = reg, res = lastDst
+				))
+			else:
+				reg = prog.resolveParam(storage, None, {})
+				output.append('\n\t{reg} = {res};'.format(reg = reg, res = lastDst))
+		elif calc == 'half-carry':
+			pass
+		elif calc == 'carry':
+			pass
+		elif calc == 'overflow':
+			pass
+		elif calc == 'parity':
+			pass
+	#TODO: combine explicit flags targeting the same storage location
+	for flag in explicit:
+		location = prog.flags.getStorage(flag)
+		if type(location) is tuple:
+			reg,bit = location
+			reg = prog.resolveReg(reg, None, {})
+			value = str(1 << bit)
+			if explicit[flag]:
+				operator = '|='
+			else:
+				operator = '&='
+				value = '~' + value
+			output.append('\n\t{reg} {op} {val};'.format(reg=reg, op=operator, val=value))
+		else:
+			reg = prog.resolveReg(location, None, {})
+			output.append('\n\t{reg} = {val};'.format(reg=reg, val=explicit[flag]))
+	return ''.join(output)
+	
+def _cmpCImpl(prog, params):
+	size = prog.paramSize(params[1])
+	tmpvar = 'cmp_tmp{sz}__'.format(sz=size)
+	typename = ''
+	scope = prog.getRootScope()
+	if not scope.resolveLocal(tmpvar):
+		scope.addLocal(tmpvar, size)
+	prog.lastDst = tmpvar
+	return '\n\t{var} = {b} - {a};'.format(var = tmpvar, a = params[0], b = params[1])
+
+def _asrCImpl(prog, params, rawParams):
+	shiftSize = prog.paramSize(rawParams[0])
+	mask = 1 << (shiftSize - 1)
+	return '\n\t{dst} = ({a} >> {b}) | ({a} & {mask});'.format(a = params[0], b = params[1], dst = params[2], mask = mask)
+		
+_opMap = {
+	'mov': Op(lambda val: val).cUnaryOperator(''),
+	'not': Op(lambda val: ~val).cUnaryOperator('~'),
+	'lnot': Op(lambda val: 0 if val else 1).cUnaryOperator('!'),
+	'neg': Op(lambda val: -val).cUnaryOperator('-'),
+	'add': Op(lambda a, b: a + b).cBinaryOperator('+'),
+	'sub': Op(lambda a, b: b - a).cBinaryOperator('-'),
+	'lsl': Op(lambda a, b: a << b).cBinaryOperator('<<'),
+	'lsr': Op(lambda a, b: a >> b).cBinaryOperator('>>'),
+	'asr': Op(lambda a, b: a >> b).addImplementation('c', 2, _asrCImpl),
+	'and': Op(lambda a, b: a & b).cBinaryOperator('&'),
+	'or':  Op(lambda a, b: a | b).cBinaryOperator('|'),
+	'xor': Op(lambda a, b: a ^ b).cBinaryOperator('^'),
+	'abs': Op(lambda val: abs(val)).addImplementation(
+		'c', 1, lambda prog, params: '\n\t{dst} = abs({src});'.format(dst=params[1], src=params[0])
+	),
+	'cmp': Op().addImplementation('c', None, _cmpCImpl),
+	'ocall': Op().addImplementation('c', None, lambda prog, params: '\n\t{pre}{fun}({args});'.format(
+		pre = prog.prefix, fun = params[0], args = ', '.join(['context'] + [str(p) for p in params[1:]])
+	)),
+	'cycles': Op().addImplementation('c', None,
+		lambda prog, params: '\n\tcontext->cycles += context->opts->gen.clock_divider * {0};'.format(
+			params[0]
+		)
+	),
+	'addsize': Op(
+		lambda a, b: b + (2 * a if a else 1)
+	).addImplementation('c', 2, lambda prog, params: '\n\t{dst} = {val} + {sz} ? {sz} * 2 : 1;'.format(
+		dst = params[2], sz = params[0], val = params[1]
+	)),
+	'decsize': Op(
+		lambda a, b: b - (2 * a if a else 1)
+	).addImplementation('c', 2, lambda prog, params: '\n\t{dst} = {val} - {sz} ? {sz} * 2 : 1;'.format(
+		dst = params[2], sz = params[0], val = params[1]
+	)),
+	'xchg': Op().addImplementation('c', (0,1), _xchgCImpl),
+	'dispatch': Op().addImplementation('c', None, _dispatchCImpl),
+	'update_flags': Op().addImplementation('c', None, _updateFlagsCImpl)
+}
+
+#represents a simple DSL instruction
+class NormalOp:
+	def __init__(self, parts):
+		self.op = parts[0]
+		self.params = parts[1:]
+		
+	def generate(self, prog, parent, fieldVals, output, otype):
+		procParams = []
+		allParamsConst = True
+		opDef = _opMap.get(self.op)
+		for param in self.params:
+			allowConst = (self.op in prog.subroutines or len(procParams) != len(self.params) - 1) and param in parent.regValues
+			isDst = (not opDef is None) and len(procParams) in opDef.outOp
+			param = prog.resolveParam(param, parent, fieldVals, allowConst, isDst)
+			
+			if (not type(param) is int) and len(procParams) != len(self.params) - 1:
+				allParamsConst = False
+			procParams.append(param)
+			
+		if self.op == 'meta':
+			param,_,index = self.params[1].partition('.')
+			if index:
+				index = (parent.resolveLocal(index) or index)
+				if index in fieldVals:
+					index = str(fieldVals[index])
+				param = param + '.' + index
+			else:
+				param = parent.resolveLocal(param) or param
+				if param in fieldVals:
+					param = fieldVals[index]
+			prog.meta[self.params[0]] = param
+		elif self.op == 'dis':
+			#TODO: Disassembler
+			pass
+		elif not opDef is None:
+			if opDef.canEval() and allParamsConst:
+				#do constant folding
+				if opDef.numArgs() >= len(procParams):
+					raise Exception('Insufficient args for ' + self.op + ' (' + ', '.join(self.params) + ')')
+				dst = self.params[opDef.numArgs()]
+				result = opDef.evaluate(procParams[:opDef.numArgs()])
+				while dst in prog.meta:
+					dst = prog.meta[dst]
+				maybeLocal = parent.resolveLocal(dst)
+				if maybeLocal:
+					dst = maybeLocal
+				parent.regValues[dst] = result
+				if prog.isReg(dst):
+					output.append(_opMap['mov'].generate(otype, prog, procParams, self.params))
+			else:
+				output.append(opDef.generate(otype, prog, procParams, self.params))
+		elif self.op in prog.subroutines:
+			prog.subroutines[self.op].inline(prog, procParams, output, otype, parent)
+		else:
+			output.append('\n\t' + self.op + '(' + ', '.join([str(p) for p in procParams]) + ');')
+		prog.lastOp = self
+	
+	def __str__(self):
+		return '\n\t' + self.op + ' ' + ' '.join(self.params)
+		
+#represents a DSL switch construct
+class Switch(ChildBlock):
+	def __init__(self, parent, param):
+		self.op = 'switch'
+		self.parent = parent
+		self.param = param
+		self.cases = {}
+		self.regValues = None
+		self.current_locals = {}
+		self.case_locals = {}
+		self.current_case = None
+		self.default = None
+		self.default_locals = None
+	
+	def addOp(self, op):
+		if op.op == 'case':
+			val = int(op.params[0], 16) if op.params[0].startswith('0x') else int(op.params[0])
+			self.cases[val] = self.current_case = []
+			self.case_locals[val] = self.current_locals = {}
+		elif op.op == 'default':
+			self.default = self.current_case = []
+			self.default_locals = self.current_locals = {}
+		elif self.current_case == None:
+			raise ion('Orphan instruction in switch')
+		elif op.op == 'local':
+			name = op.params[0]
+			size = op.params[1]
+			self.current_locals[name] = size
+		else:
+			self.current_case.append(op)
+			
+	def resolveLocal(self, name):
+		if name in self.current_locals:
+			return name
+		return self.parent.resolveLocal(name)
+	
+	def addLocal(self, name, size):
+		self.current_locals[name] = size
+		
+	def localSize(self, name):
+		if name in self.current_locals:
+			return self.current_locals[name]
+		return self.parent.localSize(name)
+			
+	def generate(self, prog, parent, fieldVals, output, otype):
+		prog.pushScope(self)
+		param = prog.resolveParam(self.param, parent, fieldVals)
+		if type(param) is int:
+			self.regValues = self.parent.regValues
+			if param in self.cases:
+				self.current_locals = self.case_locals[param]
+				output.append('\n\t{')
+				for local in self.case_locals[param]:
+					output.append('\n\tuint{0}_t {1};'.format(self.case_locals[param][local], local))
+				for op in self.cases[param]:
+					op.generate(prog, self, fieldVals, output, otype)
+				output.append('\n\t}')
+			elif self.default:
+				self.current_locals = self.default_locals
+				output.append('\n\t{')
+				for local in self.default_locals:
+					output.append('\n\tuint{0}_t {1};'.format(self.default[local], local))
+				for op in self.default:
+					op.generate(prog, self, fieldVals, output, otype)
+				output.append('\n\t}')
+		else:
+			output.append('\n\tswitch(' + param + ')')
+			output.append('\n\t{')
+			for case in self.cases:
+				self.current_locals = self.case_locals[case]
+				self.regValues = dict(self.parent.regValues)
+				output.append('\n\tcase {0}U: '.format(case) + '{')
+				for local in self.case_locals[case]:
+					output.append('\n\tuint{0}_t {1};'.format(self.case_locals[case][local], local))
+				for op in self.cases[case]:
+					op.generate(prog, self, fieldVals, output, otype)
+				output.append('\n\tbreak;')
+				output.append('\n\t}')
+			if self.default:
+				self.current_locals = self.default_locals
+				self.regValues = dict(self.parent.regValues)
+				output.append('\n\tdefault: {')
+				for local in self.default_locals:
+					output.append('\n\tuint{0}_t {1};'.format(self.default_locals[local], local))
+				for op in self.default:
+					op.generate(prog, self, fieldVals, output, otype)
+			output.append('\n\t}')
+		prog.popScope()
+	
+	def __str__(self):
+		keys = self.cases.keys()
+		keys.sort()
+		lines = ['\n\tswitch']
+		for case in keys:
+			lines.append('\n\tcase {0}'.format(case))
+			lines.append(''.join([str(op) for op in self.cases[case]]))
+		lines.append('\n\tend')
+		return ''.join(lines)
+
+		
+def _geuCImpl(prog, parent, fieldVals, output):
+	if prog.lastOp.op == 'cmp':
+		output.pop()
+		params = [prog.resolveParam(p, parent, fieldVals) for p in prog.lastOp.params]
+		return '\n\tif ({a} >= {b}) '.format(a=params[1], b = params[0]) + '{'
+	else:
+		raise ion(">=U not implemented in the general case yet")
+	
+_ifCmpImpl = {
+	'c': {
+		'>=U': _geuCImpl
+	}
+}
+#represents a DSL conditional construct
+class If(ChildBlock):
+	def __init__(self, parent, cond):
+		self.op = 'if'
+		self.parent = parent
+		self.cond = cond
+		self.body = []
+		self.elseBody = []
+		self.curBody = self.body
+		self.locals = {}
+		self.elseLocals = {}
+		self.curLocals = self.locals
+		self.regValues = None
+		
+	def addOp(self, op):
+		if op.op in ('case', 'arg'):
+			raise Exception(self.op + ' is not allows inside an if block')
+		if op.op == 'local':
+			name = op.params[0]
+			size = op.params[1]
+			self.locals[name] = size
+		elif op.op == 'else':
+			self.curLocals = self.elseLocals
+			self.curBody = self.elseBody
+		else:
+			self.curBody.append(op)
+			
+	def localSize(self, name):
+		return self.curLocals.get(name)
+		
+	def resolveLocal(self, name):
+		if name in self.locals:
+			return name
+		return self.parent.resolveLocal(name)
+		
+	def _genTrueBody(self, prog, fieldVals, output, otype):
+		self.curLocals = self.locals
+		for local in self.locals:
+			output.append('\n\tuint{sz}_t {nm};'.format(sz=self.locals[local], nm=local))
+		for op in self.body:
+			op.generate(prog, self, fieldVals, output, otype)
+			
+	def _genFalseBody(self, prog, fieldVals, output, otype):
+		self.curLocals = self.elseLocals
+		for local in self.elseLocals:
+			output.append('\n\tuint{sz}_t {nm};'.format(sz=self.elseLocals[local], nm=local))
+		for op in self.elseBody:
+			op.generate(prog, self, fieldVals, output, otype)
+	
+	def _genConstParam(self, param, prog, fieldVals, output, otype):
+		if param:
+			self._genTrueBody(prog, fieldVals, output, otype)
+		else:
+			self._genFalseBody(prog, fieldVals, output, otype)
+			
+	def generate(self, prog, parent, fieldVals, output, otype):
+		self.regValues = parent.regValues
+		try:
+			self._genConstParam(prog.checkBool(self.cond), prog, fieldVals, output, otype)
+		except Exception:
+			if self.cond in _ifCmpImpl[otype]:
+				output.append(_ifCmpImpl[otype][self.cond](prog, parent, fieldVals, output))
+				self._genTrueBody(prog, fieldVals, output, otype)
+				if self.elseBody:
+					output.append('\n\t} else {')
+					self._genFalseBody(prog, fieldVals, output, otype)
+				output.append('\n\t}')
+			else:
+				cond = prog.resolveParam(self.cond, parent, fieldVals)
+				if type(cond) is int:
+					self._genConstParam(cond, prog, fieldVals, output, otype)
+				else:
+					output.append('\n\tif ({cond}) '.format(cond=cond) + '{')
+					self._genTrueBody(prog, fieldVals, output, otype)
+					if self.elseBody:
+						output.append('\n\t} else {')
+						self._genFalseBody(prog, fieldVals, output, otype)
+					output.append('\n\t}')
+						
+	
+	def __str__(self):
+		lines = ['\n\tif']
+		for op in self.body:
+			lines.append(str(op))
+		lines.append('\n\tend')
+		return ''.join(lines)
+
+class Registers:
+	def __init__(self):
+		self.regs = {}
+		self.pointers = {}
+		self.regArrays = {}
+		self.regToArray = {}
+	
+	def addReg(self, name, size):
+		self.regs[name] = size
+		
+	def addPointer(self, name, size):
+		self.pointers[name] = size
+	
+	def addRegArray(self, name, size, regs):
+		self.regArrays[name] = (size, regs)
+		idx = 0
+		if not type(regs) is int:
+			for reg in regs:
+				self.regs[reg] = size
+				self.regToArray[reg] = (name, idx)
+				idx += 1
+	
+	def isReg(self, name):
+		return name in self.regs
+	
+	def isRegArray(self, name):
+		return name in self.regArrays
+		
+	def isRegArrayMember(self, name):
+		return name in self.regToArray
+		
+	def arrayMemberParent(self, name):
+		return self.regToArray[name][0]
+	
+	def arrayMemberIndex(self, name):
+		return self.regToArray[name][1]
+	
+	def arrayMemberName(self, array, index):
+		if type(index) is int and not type(self.regArrays[array][1]) is int:
+			return self.regArrays[array][1][index]
+		else:
+			return None
+			
+	def isNamedArray(self, array):
+		return array in self.regArrays and type(self.regArrays[array][1]) is int
+	
+	def processLine(self, parts):
+		if len(parts) == 3:
+			self.addRegArray(parts[0], int(parts[1]), int(parts[2]))
+		elif len(parts) > 2:
+			self.addRegArray(parts[0], int(parts[1]), parts[2:])
+		else:
+			if parts[1].startswith('ptr'):
+				self.addPointer(parts[0], int(parts[1][3:]))
+			else:
+				self.addReg(parts[0], int(parts[1]))
+		return self
+
+	def writeHeader(self, otype, hFile):
+		fieldList = []
+		for pointer in self.pointers:
+			hFile.write('\n\tuint{sz}_t *{nm};'.format(nm=pointer, sz=self.pointers[pointer]))
+		for reg in self.regs:
+			if not self.isRegArrayMember(reg):
+				fieldList.append((self.regs[reg], 1, reg))
+		for arr in self.regArrays:
+			size,regs = self.regArrays[arr]
+			if not type(regs) is int:
+				regs = len(regs)
+			fieldList.append((size, regs, arr))
+		fieldList.sort()
+		fieldList.reverse()
+		for size, count, name in fieldList:
+			if count > 1:
+				hFile.write('\n\tuint{sz}_t {nm}[{ct}];'.format(sz=size, nm=name, ct=count))
+			else:
+				hFile.write('\n\tuint{sz}_t {nm};'.format(sz=size, nm=name))
+	
+class Flags:
+	def __init__(self):
+		self.flagBits = {}
+		self.flagCalc = {}
+		self.flagStorage = {}
+		self.flagReg = None
+		self.maxBit = -1
+	
+	def processLine(self, parts):
+		if parts[0] == 'register':
+			self.flagReg = parts[1]
+		else:
+			flag,bit,calc,storage = parts
+			bit,_,top = bit.partition('-')
+			bit = int(bit)
+			if top:
+				top = int(bit)
+				if top > self.maxBit:
+					self.maxBit = top
+				self.flagBits[flag] = (bit,top)
+			else:
+				if bit > self.maxBit:
+					self.maxBit = bit
+				self.flagBits[flag] = bit
+			self.flagCalc[flag] = calc
+			self.flagStorage[flag] = storage
+		return self
+	
+	def getStorage(self, flag):
+		if not flag in self.flagStorage:
+			raise Exception('Undefined flag ' + flag)
+		loc,_,bit = self.flagStorage[flag].partition('.')
+		if bit:
+			return (loc, int(bit))
+		else:
+			return loc 
+	
+	def disperseFlags(self, prog, otype):
+		bitToFlag = [None] * (self.maxBit+1)
+		src = prog.resolveReg(self.flagReg, None, {})
+		output = []
+		for flag in self.flagBits:
+			bit = self.flagBits[flag]
+			if type(bit) is tuple:
+				bot,top = bit
+				mask = ((1 << (top + 1 - bot)) - 1) << bot
+				output.append('\n\t{dst} = {src} & mask;'.format(
+					dst=prog.resolveReg(self.flagStorage[flag], None, {}), src=src, mask=mask
+				))
+			else:
+				bitToFlag[self.flagBits[flag]] = flag		
+		multi = {}
+		for bit in range(len(bitToFlag)-1,-1,-1):
+			flag = bitToFlag[bit]
+			if not flag is None:
+				field,_,dstbit = self.flagStorage[flag].partition('.')
+				dst = prog.resolveReg(field, None, {})
+				if dstbit:
+					dstbit = int(dstbit)
+					multi.setdefault(dst, []).append((dstbit, bit))
+				else:
+					output.append('\n\t{dst} = {src} & {mask};'.format(dst=dst, src=src, mask=(1 << bit)))
+		for dst in multi:
+			didClear = False
+			direct = []
+			for dstbit, bit in multi[dst]:
+				if dstbit == bit:
+					direct.append(bit)
+				else:
+					if not didClear:
+						output.append('\n\t{dst} = 0;'.format(dst=dst))
+						didClear = True
+					if dstbit > bit:
+						shift = '<<'
+						diff = dstbit - bit
+					else:
+						shift = '>>'
+						diff = bit - dstbit
+					output.append('\n\t{dst} |= {src} {shift} {diff} & {mask};'.format(
+						src=src, dst=dst, shift=shift, diff=diff, mask=(1 << dstbit)
+					))
+			if direct:
+				if len(direct) == len(multi[dst]):
+					output.append('\n\t{dst} = {src};'.format(dst=dst, src=src))
+				else:
+					mask = 0
+					for bit in direct:
+						mask = mask | (1 << bit)
+					output.append('\n\t{dst} = {src} & {mask};'.format(dst=dst, src=src, mask=mask))
+		return ''.join(output)
+	
+	def coalesceFlags(self, prog, otype):
+		dst = prog.resolveReg(self.flagReg, None, {})
+		output = ['\n\t{dst} = 0;'.format(dst=dst)]
+		bitToFlag = [None] * (self.maxBit+1)
+		for flag in self.flagBits:
+			bit = self.flagBits[flag]
+			if type(bit) is tuple:
+				bot,_ = bit
+				src = prog.resolveReg(self.flagStorage[flag], None, {})
+				if bot:
+					output.append('\n\t{dst} |= {src} << {shift};'.format(
+						dst=dst, src = src, shift = bot
+					))
+				else:
+					output.append('\n\t{dst} |= {src};'.format(
+						dst=dst, src = src
+					))
+			else:
+				bitToFlag[bit] = flag
+		multi = {}
+		for bit in range(len(bitToFlag)-1,-1,-1):
+			flag = bitToFlag[bit]
+			if not flag is None:
+				field,_,srcbit = self.flagStorage[flag].partition('.')
+				src = prog.resolveReg(field, None, {})
+				if srcbit:
+					srcbit = int(srcbit)
+					multi.setdefault(src, []).append((srcbit,bit))
+				else:
+					output.append('\n\tif ({src}) {{\n\t\t{dst} |= 1 << {bit};\n\t}}'.format(
+						dst=dst, src=src, bit=bit
+					))
+		for src in multi:
+			direct = 0
+			for srcbit, dstbit in multi[src]:
+				if srcbit == dstbit:
+					direct = direct | (1 << srcbit)
+				else:
+					output.append('\n\tif ({src} & (1 << {srcbit})) {{\n\t\t{dst} |= 1 << {dstbit};\n\t}}'.format(
+						src=src, dst=dst, srcbit=srcbit, dstbit=dstbit
+					))
+			if direct:
+				output.append('\n\t{dst} |= {src} & {mask}'.format(
+					dst=dst, src=src, mask=direct
+				))
+		return ''.join(output)
+		
+		
+class Program:
+	def __init__(self, regs, instructions, subs, info, flags):
+		self.regs = regs
+		self.instructions = instructions
+		self.subroutines = subs
+		self.meta = {}
+		self.booleans = {}
+		self.prefix = info.get('prefix', [''])[0]
+		self.opsize = int(info.get('opcode_size', ['8'])[0])
+		self.extra_tables = info.get('extra_tables', [])
+		self.context_type = self.prefix + 'context'
+		self.body = info.get('body', [None])[0]
+		self.includes = info.get('include', [])
+		self.flags = flags
+		self.lastDst = None
+		self.scopes = []
+		self.currentScope = None
+		self.lastOp = None
+		
+	def __str__(self):
+		pieces = []
+		for reg in self.regs:
+			pieces.append(str(self.regs[reg]))
+		for name in self.subroutines:
+			pieces.append('\n'+str(self.subroutines[name]))
+		for instruction in self.instructions:
+			pieces.append('\n'+str(instruction))
+		return ''.join(pieces)
+		
+	def writeHeader(self, otype, header):
+		hFile = open(header, 'w')
+		macro = header.upper().replace('.', '_')
+		hFile.write('#ifndef {0}_'.format(macro))
+		hFile.write('\n#define {0}_'.format(macro))
+		hFile.write('\n#include "backend.h"')
+		hFile.write('\n\ntypedef struct {')
+		hFile.write('\n\tcpu_options gen;')
+		hFile.write('\n}} {0}options;'.format(self.prefix))
+		hFile.write('\n\ntypedef struct {')
+		hFile.write('\n\t{0}options *opts;'.format(self.prefix))
+		hFile.write('\n\tuint32_t cycles;')
+		self.regs.writeHeader(otype, hFile)
+		hFile.write('\n}} {0}context;'.format(self.prefix))
+		hFile.write('\n')
+		hFile.write('\n#endif //{0}_'.format(macro))
+		hFile.write('\n')
+		hFile.close()
+	def build(self, otype):
+		body = []
+		pieces = []
+		for include in self.includes:
+			body.append('#include "{0}"\n'.format(include))
+		for table in self.instructions:
+			opmap = [None] * (1 << self.opsize)
+			bodymap = {}
+			instructions = self.instructions[table]
+			instructions.sort()
+			for inst in instructions:
+				for val in inst.allValues():
+					if opmap[val] is None:
+						self.meta = {}
+						self.temp = {}
+						self.needFlagCoalesce = False
+						self.needFlagDisperse = False
+						self.lastOp = None
+						opmap[val] = inst.generateName(val)
+						bodymap[val] = inst.generateBody(val, self, otype)
+			
+			pieces.append('\ntypedef void (*impl_fun)({pre}context *context);'.format(pre=self.prefix))
+			pieces.append('\nstatic impl_fun impl_{name}[{sz}] = {{'.format(name = table, sz=len(opmap)))
+			for inst in range(0, len(opmap)):
+				op = opmap[inst]
+				if op is None:
+					pieces.append('\n\tunimplemented,')
+				else:
+					pieces.append('\n\t' + op + ',')
+					body.append(bodymap[inst])
+			pieces.append('\n};')
+		if self.body in self.subroutines:
+			pieces.append('\nvoid {pre}execute({type} *context, uint32_t target_cycle)'.format(pre = self.prefix, type = self.context_type))
+			pieces.append('\n{')
+			pieces.append('\n\twhile (context->cycles < target_cycle)')
+			pieces.append('\n\t{')
+			self.meta = {}
+			self.temp = {}
+			self.subroutines[self.body].inline(self, [], pieces, otype, None)
+			pieces.append('\n\t}')
+			pieces.append('\n}')
+		body.append('\nstatic void unimplemented({pre}context *context)'.format(pre = self.prefix))
+		body.append('\n{')
+		body.append('\n\tfatal_error("Unimplemented instruction");')
+		body.append('\n}\n')
+		return ''.join(body) +  ''.join(pieces)
+		
+	def checkBool(self, name):
+		if not name in self.booleans:
+			raise Exception(name + ' is not a defined boolean flag')
+		return self.booleans[name]
+	
+	def getTemp(self, size):
+		if size in self.temp:
+			return ('', self.temp[size])
+		self.temp[size] = 'tmp{sz}'.format(sz=size);
+		return ('\n\tuint{sz}_t tmp{sz};'.format(sz=size), self.temp[size])
+		
+	def resolveParam(self, param, parent, fieldVals, allowConstant=True, isdst=False):
+		keepGoing = True
+		while keepGoing:
+			keepGoing = False
+			try:
+				if type(param) is int:
+					pass
+				elif param.startswith('0x'):
+					param = int(param, 16)
+				else:
+					param = int(param)
+			except ValueError:
+				
+				if parent:
+					if param in parent.regValues and allowConstant:
+						return parent.regValues[param]
+					maybeLocal = parent.resolveLocal(param)
+					if maybeLocal:
+						return maybeLocal
+				if param in fieldVals:
+					param = fieldVals[param]
+				elif param in self.meta:
+					param = self.meta[param]
+					keepGoing = True
+				elif self.isReg(param):
+					param = self.resolveReg(param, parent, fieldVals, isdst)
+		return param
+	
+	def isReg(self, name):
+		if not type(name) is str:
+			return False
+		begin,sep,_ = name.partition('.')
+		if sep:
+			if begin in self.meta:
+				begin = self.meta[begin]
+			return self.regs.isRegArray(begin)
+		else:
+			return self.regs.isReg(name)
+	
+	def resolveReg(self, name, parent, fieldVals, isDst=False):
+		begin,sep,end = name.partition('.')
+		if sep:
+			if begin in self.meta:
+				begin = self.meta[begin]
+			if not self.regs.isRegArrayMember(end):
+				end = self.resolveParam(end, parent, fieldVals)
+			if not type(end) is int and self.regs.isRegArrayMember(end):
+				arrayName = self.regs.arrayMemberParent(end)
+				end = self.regs.arrayMemberIndex(end)
+				if arrayName != begin:
+					end = 'context->{0}[{1}]'.format(arrayName, end)
+			if self.regs.isNamedArray(begin):
+				regName = self.regs.arrayMemberName(begin, end)
+			else:
+				regName = '{0}.{1}'.format(begin, end)
+			ret = 'context->{0}[{1}]'.format(begin, end)
+		else:
+			regName = name
+			if self.regs.isRegArrayMember(name):
+				arr,idx = self.regs.regToArray[name]
+				ret = 'context->{0}[{1}]'.format(arr, idx)
+			else:
+				ret = 'context->' + name
+		if regName == self.flags.flagReg:
+			if isDst:
+				self.needFlagDisperse = True
+			else:
+				self.needFlagCoalesce = True
+		if isDst:
+			self.lastDst = regName
+		return ret
+		
+	
+	
+	def paramSize(self, name):
+		size = self.currentScope.localSize(name)
+		if size:
+			return size
+		begin,sep,_ = name.partition('.')
+		if sep and self.regs.isRegArray(begin):
+			return self.regs.regArrays[begin][0]
+		if self.regs.isReg(name):
+			return self.regs.regs[name]
+		return 32
+	
+	def pushScope(self, scope):
+		self.scopes.append(scope)
+		self.currentScope = scope
+		
+	def popScope(self):
+		ret = self.scopes.pop()
+		self.currentScope = self.scopes[-1] if self.scopes else None
+		return ret
+		
+	def getRootScope(self):
+		return self.scopes[0]
+
+def parse(f):
+	instructions = {}
+	subroutines = {}
+	registers = None
+	flags = None
+	errors = []
+	info = {}
+	line_num = 0
+	cur_object = None
+	for line in f:
+		line_num += 1
+		line,_,comment = line.partition('#')
+		if not line.strip():
+			continue
+		if line[0].isspace():
+			if not cur_object is None:
+				parts = [el.strip() for el in line.split(' ')]
+				if type(cur_object) is dict:
+					cur_object[parts[0]] = parts[1:]
+				else:
+					cur_object = cur_object.processLine(parts)
+				
+#				if type(cur_object) is Registers:
+#					if len(parts) > 2:
+#						cur_object.addRegArray(parts[0], int(parts[1]), parts[2:])
+#					else:
+#						cur_object.addReg(parts[0], int(parts[1]))
+#				elif type(cur_object) is dict:
+#					cur_object[parts[0]] = parts[1:]
+#				elif parts[0] == 'switch':
+#					o = Switch(cur_object, parts[1])
+#					cur_object.addOp(o)
+#					cur_object = o
+#				elif parts[0] == 'if':
+#					o = If(cur_object, parts[1])
+#					cur_object.addOp(o)
+#					cur_object = o
+#				elif parts[0] == 'end':
+#					cur_object = cur_object.parent
+#				else:
+#					cur_object.addOp(NormalOp(parts))
+			else:
+				errors.append("Orphan instruction on line {0}".format(line_num))
+		else:
+			parts = line.split(' ')
+			if len(parts) > 1:
+				if len(parts) > 2:
+					table,bitpattern,name = parts
+				else:
+					bitpattern,name = parts
+					table = 'main'
+				value = 0
+				fields = {}
+				curbit = len(bitpattern) - 1
+				for char in bitpattern:
+					value <<= 1
+					if char in ('0', '1'):
+						value |= int(char)
+					else:
+						if char in fields:
+							fields[char] = (curbit, fields[char][1] + 1)
+						else:
+							fields[char] = (curbit, 1)
+					curbit -= 1
+				cur_object = Instruction(value, fields, name.strip())
+				instructions.setdefault(table, []).append(cur_object)
+			elif line.strip() == 'regs':
+				if registers is None:
+					registers = Registers()
+				cur_object = registers
+			elif line.strip() == 'info':
+				cur_object = info
+			elif line.strip() == 'flags':
+				if flags is None:
+					flags = Flags()
+				cur_object = flags
+			else:
+				cur_object = SubRoutine(line.strip())
+				subroutines[cur_object.name] = cur_object
+	if errors:
+		print(errors)
+	else:
+		p = Program(registers, instructions, subroutines, info, flags)
+		p.booleans['dynarec'] = False
+		p.booleans['interp'] = True
+		
+		if 'header' in info:
+			print('#include "{0}"'.format(info['header'][0]))
+			p.writeHeader('c', info['header'][0])
+		print('#include "util.h"')
+		print('#include <stdlib.h>')
+		print(p.build('c'))
+
+def main(argv):
+	f =  open(argv[1])
+	parse(f)
+
+if __name__ == '__main__':
+	from sys import argv
+	main(argv)
\ No newline at end of file
--- a/debug.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/debug.c	Tue Dec 25 11:12:26 2018 -0800
@@ -787,31 +787,81 @@
 			insert_breakpoint(context, after, debugger);
 			return 0;
 		case 's':
-			if (inst.op == M68K_RTS) {
-				after = m68k_read_long(context->aregs[7], context);
-			} else if (inst.op == M68K_RTE || inst.op == M68K_RTR) {
-				after = m68k_read_long(context->aregs[7] + 2, context);
-			} else if(m68k_is_branch(&inst)) {
-				if (inst.op == M68K_BCC && inst.extra.cond != COND_TRUE) {
-					branch_f = after;
-					branch_t = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
-					insert_breakpoint(context, branch_t, debugger);
-				} else if(inst.op == M68K_DBCC) {
-					if (inst.extra.cond == COND_FALSE) {
-						if (context->dregs[inst.dst.params.regs.pri] & 0xFFFF) {
-							after = m68k_branch_target(&inst, context->dregs, context->aregs);
+			if (input_buf[1] == 'e') {
+				param = find_param(input_buf);
+				if (!param) {
+					fputs("Missing destination parameter for set\n", stderr);
+				}
+				char *val = find_param(param);
+				if (!val) {
+					fputs("Missing value parameter for set\n", stderr);
+				}
+				long int_val;
+				int reg_num;
+				switch (val[0])
+				{
+				case 'd':
+				case 'a':
+					reg_num = val[1] - '0';
+					if (reg_num < 0 || reg_num > 8) {
+						fprintf(stderr, "Invalid register %s\n", val);
+						return 1;
+					}
+					int_val = (val[0] == 'd' ? context->dregs : context->aregs)[reg_num];
+					break;
+				case '$':
+					int_val = strtol(val+1, NULL, 16);
+					break;
+				case '0':
+					if (val[1] == 'x') {
+						int_val = strtol(val+2, NULL, 16);
+						break;
+					}
+				default:
+					int_val = strtol(val, NULL, 10);
+				}
+				switch(param[0])
+				{
+				case 'd':
+				case 'a':
+					reg_num = param[1] - '0';
+					if (reg_num < 0 || reg_num > 8) {
+						fprintf(stderr, "Invalid register %s\n", param);
+						return 1;
+					}
+					(param[0] == 'd' ? context->dregs : context->aregs)[reg_num] = int_val;
+					break;
+				default:
+					fprintf(stderr, "Invalid destinatino %s\n", param);
+				}
+				break;
+			} else {
+				if (inst.op == M68K_RTS) {
+					after = m68k_read_long(context->aregs[7], context);
+				} else if (inst.op == M68K_RTE || inst.op == M68K_RTR) {
+					after = m68k_read_long(context->aregs[7] + 2, context);
+				} else if(m68k_is_branch(&inst)) {
+					if (inst.op == M68K_BCC && inst.extra.cond != COND_TRUE) {
+						branch_f = after;
+						branch_t = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
+						insert_breakpoint(context, branch_t, debugger);
+					} else if(inst.op == M68K_DBCC) {
+						if (inst.extra.cond == COND_FALSE) {
+							if (context->dregs[inst.dst.params.regs.pri] & 0xFFFF) {
+								after = m68k_branch_target(&inst, context->dregs, context->aregs);
+							}
+						} else {
+							branch_t = after;
+							branch_f = m68k_branch_target(&inst, context->dregs, context->aregs);
+							insert_breakpoint(context, branch_f, debugger);
 						}
 					} else {
-						branch_t = after;
-						branch_f = m68k_branch_target(&inst, context->dregs, context->aregs);
-						insert_breakpoint(context, branch_f, debugger);
+						after = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
 					}
-				} else {
-					after = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF;
 				}
+				insert_breakpoint(context, after, debugger);
+				return 0;
 			}
-			insert_breakpoint(context, after, debugger);
-			return 0;
 		case 'v': {
 			genesis_context * gen = context->system;
 			//VDP debug commands
@@ -900,6 +950,8 @@
 	init_terminal();
 
 	sync_components(context, 0);
+	genesis_context *gen = context->system;
+	vdp_force_update_framebuffer(gen->vdp);
 	//probably not necessary, but let's play it safe
 	address &= 0xFFFFFF;
 	if (address == branch_t) {
--- a/default.cfg	Sun Dec 31 10:11:16 2017 -0800
+++ b/default.cfg	Tue Dec 25 11:12:26 2018 -0800
@@ -19,6 +19,10 @@
 		] ui.vdp_debug_pal
 		u ui.enter_debugger
 		p ui.screenshot
+		b ui.plane_debug
+		v ui.vram_debug
+		c ui.cram_debug
+		n ui.compositing_debug
 		esc ui.exit
 		` ui.save_state
 		0 ui.set_speed.0
@@ -38,67 +42,36 @@
 		rctrl ui.toggle_keyboard_captured
 	}
 	pads {
-		0 {
+		default {
 			dpads {
 				0 {
-					up gamepads.1.up
-					down gamepads.1.down
-					left gamepads.1.left
-					right gamepads.1.right
+					up gamepads.n.up
+					down gamepads.n.down
+					left gamepads.n.left
+					right gamepads.n.right
 				}
 			}
 			buttons {
-				a gamepads.1.a
-				b gamepads.1.b
-				rightshoulder gamepads.1.c
-				x gamepads.1.x
-				y gamepads.1.y
-				leftshoulder gamepads.1.z
-				back gamepads.1.mode
-				start gamepads.1.start
+				a gamepads.n.a
+				b gamepads.n.b
+				rightshoulder gamepads.n.c
+				x gamepads.n.x
+				y gamepads.n.y
+				leftshoulder gamepads.n.z
+				back gamepads.n.mode
+				start gamepads.n.start
 				guide ui.exit
 				leftstick ui.save_state
 			}
 			axes {
-				lefty.positive gamepads.1.down
-				lefty.negative gamepads.1.up
-				leftx.positive gamepads.1.right
-				leftx.negative gamepads.1.left
+				lefty.positive gamepads.n.down
+				lefty.negative gamepads.n.up
+				leftx.positive gamepads.n.right
+				leftx.negative gamepads.n.left
 				lefttrigger ui.prev_speed
 				righttrigger ui.next_speed
 			}
 		}
-		1 {
-			dpads {
-				0 {
-					up gamepads.2.up
-					down gamepads.2.down
-					left gamepads.2.left
-					right gamepads.2.right
-				}
-			}
-			buttons {
-				#this is exactly the same mapping as above, but with PS4 style names
-				cross gamepads.2.a
-				circle gamepads.2.b
-				r1 gamepads.2.c
-				square gamepads.2.x
-				triangle gamepads.2.y
-				l1 gamepads.2.z
-				share gamepads.2.mode
-				options gamepads.2.start
-				guide ui.exit
-				l3 ui.save_state
-			}
-			axes {
-				lefty.positive gamepads.2.down
-				lefty.negative gamepads.2.up
-				leftx.positive gamepads.2.right
-				leftx.negative gamepads.2.left
-				l2 ui.prev_speed
-				r2 ui.next_speed
-			}
-		}
 	}
 	mice {
 		0 {
@@ -217,19 +190,28 @@
 	#path for storing internal screenshots, accepts the same variables as initial_path
 	screenshot_path $HOME
 	#see strftime for the format specifiers valid in screenshot_template
-	screenshot_template blastem_%Y%m%d_%H%M%S.ppm
+	screenshot_template blastem_%Y%m%d_%H%M%S.png
 	#path template for saving SRAM, EEPROM and savestates
 	#accepts special variables $HOME, $EXEDIR, $USERDATA, $ROMNAME
 	save_path $USERDATA/blastem/$ROMNAME
 	#space delimited list of file extensions to filter against in menu
-	extensions bin gen md smd sms gg
+	extensions bin gen md smd sms gg zip gz
 	#specifies the preferred save-state format, set to gst for Genecyst compatible states
 	state_format native
 }
 
 system {
+	#controls how the emulated system is synced to the host
+	#video provides the smoothest experience when the host and emulated system have similar refresh rates
+	#audio provides lower audio latency, especially when there is a refresh rate mismatch
+	sync_source video
+	#set this to random to debug initialization bugs
 	ram_init zero
 	default_region U
+	#controls whether MegaWiFi support is enabled or not
+	#MegaWiFi allows ROMs to make connections to the internet
+	#so it should only be enabled for ROMs you trust
+	megawifi off
 }
 
 
--- a/dis.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/dis.c	Tue Dec 25 11:12:26 2018 -0800
@@ -206,6 +206,7 @@
 						}
 					}
 				}
+				fclose(address_log);
 			}
 		} else {
 			char *end;
--- a/fib.s68	Sun Dec 31 10:11:16 2017 -0800
+++ b/fib.s68	Tue Dec 25 11:12:26 2018 -0800
@@ -1,6 +1,6 @@
     dc.l $0, start
 start:
-	moveq #36, d0
+	moveq #42, d0
 	bsr fib
 	reset
 fib:
--- a/gen_x86.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/gen_x86.c	Tue Dec 25 11:12:26 2018 -0800
@@ -2152,6 +2152,7 @@
 	{
 		push_r(code, arg_arr[i]);
 	}
+	free(arg_arr);
 	
 	return stack_args * sizeof(void *) + adjust;
 }
--- a/genesis.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/genesis.c	Tue Dec 25 11:12:26 2018 -0800
@@ -15,6 +15,9 @@
 #include "util.h"
 #include "debug.h"
 #include "gdb_remote.h"
+#include "saves.h"
+#include "bindings.h"
+#include "jcart.h"
 #define MCLKS_NTSC 53693175
 #define MCLKS_PAL  53203395
 
@@ -28,7 +31,7 @@
 
 //TODO: Figure out the exact value for this
 #define LINES_NTSC 262
-#define LINES_PAL 312
+#define LINES_PAL 313
 
 #define MAX_SOUND_CYCLES 100000	
 
@@ -109,7 +112,7 @@
 
 static void update_z80_bank_pointer(genesis_context *gen)
 {
-	if (gen->z80->bank_reg < 0x100) {
+	if (gen->z80->bank_reg < 0x140) {
 		gen->z80->mem_pointers[1] = get_native_pointer(gen->z80->bank_reg << 15, (void **)gen->m68k->mem_pointers, &gen->m68k->options->gen);
 	} else {
 		gen->z80->mem_pointers[1] = NULL;
@@ -124,6 +127,7 @@
 	gen->z80->bank_reg = load_int16(buf) & 0x1FF;
 }
 
+static void adjust_int_cycle(m68k_context * context, vdp_context * v_context);
 void genesis_deserialize(deserialize_buffer *buf, genesis_context *gen)
 {
 	register_section_handler(buf, (section_handler){.fun = m68k_deserialize, .data = gen->m68k}, SECTION_68000);
@@ -143,6 +147,7 @@
 		load_section(buf);
 	}
 	update_z80_bank_pointer(gen);
+	adjust_int_cycle(gen->m68k, gen->vdp);
 }
 
 uint16_t read_dma_value(uint32_t address)
@@ -308,6 +313,11 @@
 	sync_z80(z_context, mclks);
 	sync_sound(gen, mclks);
 	vdp_run_context(v_context, mclks);
+	if (mclks >= gen->reset_cycle) {
+		gen->reset_requested = 1;
+		context->should_return = 1;
+		gen->reset_cycle = CYCLE_NEVER;
+	}
 	if (v_context->frame != last_frame_num) {
 		//printf("reached frame end %d | MCLK Cycles: %d, Target: %d, VDP cycles: %d, vcounter: %d, hslot: %d\n", last_frame_num, mclks, gen->frame_end, v_context->cycles, v_context->vcounter, v_context->hslot);
 		last_frame_num = v_context->frame;
@@ -324,6 +334,9 @@
 			io_adjust_cycles(gen->io.ports, context->current_cycle, deduction);
 			io_adjust_cycles(gen->io.ports+1, context->current_cycle, deduction);
 			io_adjust_cycles(gen->io.ports+2, context->current_cycle, deduction);
+			if (gen->mapper_type == MAPPER_JCART) {
+				jcart_adjust_cycles(gen, deduction);
+			}
 			context->current_cycle -= deduction;
 			z80_adjust_cycles(z_context, deduction);
 			gen->ym->current_cycle -= deduction;
@@ -331,6 +344,9 @@
 			if (gen->ym->write_cycle != CYCLE_NEVER) {
 				gen->ym->write_cycle = gen->ym->write_cycle >= deduction ? gen->ym->write_cycle - deduction : 0;
 			}
+			if (gen->reset_cycle != CYCLE_NEVER) {
+				gen->reset_cycle -= deduction;
+			}
 		}
 	}
 	gen->frame_end = vdp_cycles_to_frame_end(v_context);
@@ -345,6 +361,9 @@
 		context->sync_cycle = context->current_cycle + 1;
 	}
 	adjust_int_cycle(context, v_context);
+	if (gen->reset_cycle < context->target_cycle) {
+		context->target_cycle = gen->reset_cycle;
+	}
 #ifdef USE_NATIVE
 	if (address) {
 		if (gen->header.enter_debugger) {
@@ -361,18 +380,7 @@
 					sync_z80(z_context, z_context->current_cycle + MCLKS_PER_Z80);
 				}
 			}
-			char *save_path;
-			if (slot == QUICK_SAVE_SLOT) {
-				save_path = save_state_path;
-			} else {
-				char slotname[] = "slot_0.state";
-				slotname[5] = '0' + slot;
-				if (!use_native_states) {
-					strcpy(slotname + 7, "gst");
-				}
-				char const *parts[] = {gen->header.save_dir, PATH_SEP, slotname};
-				save_path = alloc_concat_m(3, parts);
-			}
+			char *save_path = get_slot_name(&gen->header, slot, use_native_states ? "state" : "gst");
 			if (use_native_states) {
 				serialize_buffer state;
 				init_serialize(&state);
@@ -383,9 +391,7 @@
 				save_gst(gen, save_path, address);
 			}
 			printf("Saved state to %s\n", save_path);
-			if (slot != QUICK_SAVE_SLOT) {
-				free(save_path);
-			}
+			free(save_path);
 		} else if(gen->header.save_state) {
 			context->sync_cycle = context->current_cycle + 1;
 		}
@@ -1029,37 +1035,24 @@
 	gen->master_clock = gen->normal_clock;
 }
 
-static void handle_reset_requests(genesis_context *gen)
-{
-	while (gen->reset_requested)
-	{
-		gen->reset_requested = 0;
-		z80_assert_reset(gen->z80, gen->m68k->current_cycle);
-		z80_clear_busreq(gen->z80, gen->m68k->current_cycle);
-		ym_reset(gen->ym);
-		//Is there any sort of VDP reset?
-		m68k_reset(gen->m68k);
-	}
-	vdp_release_framebuffer(gen->vdp);
-}
-
 #include "m68k_internal.h" //needed for get_native_address_trans, should be eliminated once handling of PC is cleaned up
 static uint8_t load_state(system_header *system, uint8_t slot)
 {
 	genesis_context *gen = (genesis_context *)system;
-	char numslotname[] = "slot_0.state";
-	char *slotname;
-	if (slot == QUICK_SAVE_SLOT) {
-		slotname = "quicksave.state";
-	} else {
-		numslotname[5] = '0' + slot;
-		slotname = numslotname;
-	}
-	char const *parts[] = {gen->header.save_dir, PATH_SEP, slotname};
-	char *statepath = alloc_concat_m(3, parts);
+	char *statepath = get_slot_name(system, slot, "state");
 	deserialize_buffer state;
 	uint32_t pc = 0;
 	uint8_t ret = 0;
+	if (!gen->m68k->resume_pc) {
+		system->delayed_load_slot = slot + 1;
+		gen->m68k->should_return = 1;
+		ret = get_modification_time(statepath) != 0;
+		if (!ret) {
+			strcpy(statepath + strlen(statepath)-strlen("state"), "gst");
+			ret = get_modification_time(statepath) != 0;
+		}
+		goto done;
+	}
 	if (load_from_file(&state, statepath)) {
 		genesis_deserialize(&state, gen);
 		free(state.data);
@@ -1078,15 +1071,39 @@
 		gen->m68k->resume_pc = get_native_address_trans(gen->m68k, pc);
 	}
 #endif
+done:
 	free(statepath);
 	return ret;
 }
 
+static void handle_reset_requests(genesis_context *gen)
+{
+	while (gen->reset_requested || gen->header.delayed_load_slot)
+	{
+		if (gen->reset_requested) {
+			gen->reset_requested = 0;
+			gen->m68k->should_return = 0;
+			z80_assert_reset(gen->z80, gen->m68k->current_cycle);
+			z80_clear_busreq(gen->z80, gen->m68k->current_cycle);
+			ym_reset(gen->ym);
+			//Is there any sort of VDP reset?
+			m68k_reset(gen->m68k);
+		}
+		if (gen->header.delayed_load_slot) {
+			load_state(&gen->header, gen->header.delayed_load_slot - 1);
+			gen->header.delayed_load_slot = 0;
+			resume_68k(gen->m68k);
+		}
+	}
+	bindings_release_capture();
+	vdp_release_framebuffer(gen->vdp);
+	render_pause_source(gen->ym->audio);
+	render_pause_source(gen->psg->audio);
+}
+
 static void start_genesis(system_header *system, char *statefile)
 {
 	genesis_context *gen = (genesis_context *)system;
-	set_keybindings(&gen->io);
-	render_set_video_standard((gen->version_reg & HZ50) ? VID_PAL : VID_NTSC);
 	if (statefile) {
 		//first try loading as a native format savestate
 		deserialize_buffer state;
@@ -1130,9 +1147,11 @@
 static void resume_genesis(system_header *system)
 {
 	genesis_context *gen = (genesis_context *)system;
-	map_all_bindings(&gen->io);
 	render_set_video_standard((gen->version_reg & HZ50) ? VID_PAL : VID_NTSC);
+	bindings_reacquire_capture();
 	vdp_reacquire_framebuffer(gen->vdp);
+	render_resume_source(gen->ym->audio);
+	render_resume_source(gen->psg->audio);
 	resume_68k(gen->m68k);
 	handle_reset_requests(gen);
 }
@@ -1140,19 +1159,7 @@
 static void inc_debug_mode(system_header *system)
 {
 	genesis_context *gen = (genesis_context *)system;
-	gen->vdp->debug++;
-	if (gen->vdp->debug == 7) {
-		gen->vdp->debug = 0;
-	}
-}
-
-static void inc_debug_pal(system_header *system)
-{
-	genesis_context *gen = (genesis_context *)system;
-	gen->vdp->debug_pal++;
-	if (gen->vdp->debug_pal == 4) {
-		gen->vdp->debug_pal = 0;
-	}
+	vdp_inc_debug_mode(gen->vdp);
 }
 
 static void request_exit(system_header *system)
@@ -1193,14 +1200,20 @@
 static void soft_reset(system_header *system)
 {
 	genesis_context *gen = (genesis_context *)system;
-	gen->m68k->should_return = 1;
-	gen->reset_requested = 1;
+	if (gen->reset_cycle == CYCLE_NEVER) {
+		double random = (double)rand()/(double)RAND_MAX;
+		gen->reset_cycle = gen->m68k->current_cycle + random * MCLKS_LINE * (gen->version_reg & HZ50 ? LINES_PAL : LINES_NTSC);
+		if (gen->reset_cycle < gen->m68k->target_cycle) {
+			gen->m68k->target_cycle = gen->reset_cycle;
+		}
+	}
 }
 
 static void free_genesis(system_header *system)
 {
 	genesis_context *gen = (genesis_context *)system;
 	vdp_free(gen->vdp);
+	memmap_chunk *map = (memmap_chunk *)gen->m68k->options->gen.memmap;
 	m68k_options_free(gen->m68k->options);
 	free(gen->cart);
 	free(gen->m68k);
@@ -1210,12 +1223,72 @@
 	free(gen->zram);
 	ym_free(gen->ym);
 	psg_free(gen->psg);
-	free(gen->save_storage);
 	free(gen->header.save_dir);
+	free_rom_info(&gen->header.info);
 	free(gen->lock_on);
 	free(gen);
 }
 
+static void gamepad_down(system_header *system, uint8_t gamepad_num, uint8_t button)
+{
+	genesis_context *gen = (genesis_context *)system;
+	io_gamepad_down(&gen->io, gamepad_num, button);
+	if (gen->mapper_type == MAPPER_JCART) {
+		jcart_gamepad_down(gen, gamepad_num, button);
+	}
+}
+
+static void gamepad_up(system_header *system, uint8_t gamepad_num, uint8_t button)
+{
+	genesis_context *gen = (genesis_context *)system;
+	io_gamepad_up(&gen->io, gamepad_num, button);
+	if (gen->mapper_type == MAPPER_JCART) {
+		jcart_gamepad_up(gen, gamepad_num, button);
+	}
+}
+
+static void mouse_down(system_header *system, uint8_t mouse_num, uint8_t button)
+{
+	genesis_context *gen = (genesis_context *)system;
+	io_mouse_down(&gen->io, mouse_num, button);
+}
+
+static void mouse_up(system_header *system, uint8_t mouse_num, uint8_t button)
+{
+	genesis_context *gen = (genesis_context *)system;
+	io_mouse_up(&gen->io, mouse_num, button);
+}
+
+static void mouse_motion_absolute(system_header *system, uint8_t mouse_num, uint16_t x, uint16_t y)
+{
+	genesis_context *gen = (genesis_context *)system;
+	io_mouse_motion_absolute(&gen->io, mouse_num, x, y);
+}
+
+static void mouse_motion_relative(system_header *system, uint8_t mouse_num, int32_t x, int32_t y)
+{
+	genesis_context *gen = (genesis_context *)system;
+	io_mouse_motion_relative(&gen->io, mouse_num, x, y);
+}
+
+static void keyboard_down(system_header *system, uint8_t scancode)
+{
+	genesis_context *gen = (genesis_context *)system;
+	io_keyboard_down(&gen->io, scancode);
+}
+
+static void keyboard_up(system_header *system, uint8_t scancode)
+{
+	genesis_context *gen = (genesis_context *)system;
+	io_keyboard_up(&gen->io, scancode);
+}
+
+static void config_updated(system_header *system)
+{
+	genesis_context *gen = (genesis_context *)system;
+	setup_io_devices(config, &system->info, &gen->io);
+}
+
 genesis_context *alloc_init_genesis(rom_info *rom, void *main_rom, void *lock_on, uint32_t system_opts, uint8_t force_region)
 {
 	static memmap_chunk z80_map[] = {
@@ -1237,29 +1310,35 @@
 	gen->header.get_open_bus_value = get_open_bus_value;
 	gen->header.request_exit = request_exit;
 	gen->header.inc_debug_mode = inc_debug_mode;
-	gen->header.inc_debug_pal = inc_debug_pal;
+	gen->header.gamepad_down = gamepad_down;
+	gen->header.gamepad_up = gamepad_up;
+	gen->header.mouse_down = mouse_down;
+	gen->header.mouse_up = mouse_up;
+	gen->header.mouse_motion_absolute = mouse_motion_absolute;
+	gen->header.mouse_motion_relative = mouse_motion_relative;
+	gen->header.keyboard_down = keyboard_down;
+	gen->header.keyboard_up = keyboard_up;
+	gen->header.config_updated = config_updated;
 	gen->header.type = SYSTEM_GENESIS;
+	gen->header.info = *rom;
 	set_region(gen, rom, force_region);
 
-	gen->vdp = malloc(sizeof(vdp_context));
-	init_vdp_context(gen->vdp, gen->version_reg & 0x40);
+	gen->vdp = init_vdp_context(gen->version_reg & 0x40);
 	gen->vdp->system = &gen->header;
 	gen->frame_end = vdp_cycles_to_frame_end(gen->vdp);
 	char * config_cycles = tern_find_path(config, "clocks\0max_cycles\0", TVAL_PTR).ptrval;
 	gen->max_cycles = config_cycles ? atoi(config_cycles) : DEFAULT_SYNC_INTERVAL;
 	gen->int_latency_prev1 = MCLKS_PER_68K * 32;
 	gen->int_latency_prev2 = MCLKS_PER_68K * 16;
-
-	char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0", TVAL_PTR).ptrval;
-	uint32_t lowpass_cutoff = lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : DEFAULT_LOWPASS_CUTOFF;
+	
+	render_set_video_standard((gen->version_reg & HZ50) ? VID_PAL : VID_NTSC);
 	
 	gen->ym = malloc(sizeof(ym2612_context));
-	ym_init(gen->ym, render_sample_rate(), gen->master_clock, MCLKS_PER_YM, render_audio_buffer(), system_opts, lowpass_cutoff);
+	ym_init(gen->ym, gen->master_clock, MCLKS_PER_YM, system_opts);
 
 	gen->psg = malloc(sizeof(psg_context));
-	psg_init(gen->psg, render_sample_rate(), gen->master_clock, MCLKS_PER_PSG, render_audio_buffer(), lowpass_cutoff);
+	psg_init(gen->psg, gen->master_clock, MCLKS_PER_PSG);
 
-	gen->zram = calloc(1, Z80_RAM_BYTES);
 	z80_map[0].buffer = gen->zram = calloc(1, Z80_RAM_BYTES);
 #ifndef NO_Z80
 	z80_options *z_opts = malloc(sizeof(z80_options));
@@ -1307,6 +1386,7 @@
 		}
 	}
 	setup_io_devices(config, rom, &gen->io);
+	gen->header.has_keyboard = io_has_keyboard(&gen->io);
 
 	gen->mapper_type = rom->mapper_type;
 	gen->save_type = rom->save_type;
@@ -1319,7 +1399,8 @@
 		if (gen->save_type == SAVE_I2C) {
 			eeprom_init(&gen->eeprom, gen->save_storage, gen->save_size);
 		} else if (gen->save_type == SAVE_NOR) {
-			nor_flash_init(&gen->nor, gen->save_storage, gen->save_size, rom->save_page_size, rom->save_product_id, rom->save_bus);
+			memcpy(&gen->nor, rom->nor, sizeof(gen->nor));
+			//nor_flash_init(&gen->nor, gen->save_storage, gen->save_size, rom->save_page_size, rom->save_product_id, rom->save_bus);
 		}
 	} else {
 		gen->save_storage = NULL;
@@ -1361,7 +1442,7 @@
 	return gen;
 }
 
-genesis_context *alloc_config_genesis(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t ym_opts, uint8_t force_region, rom_info *info_out)
+genesis_context *alloc_config_genesis(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t ym_opts, uint8_t force_region)
 {
 	static memmap_chunk base_map[] = {
 		{0xE00000, 0x1000000, 0xFFFF,   0, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, NULL,
@@ -1377,9 +1458,9 @@
 	if (!rom_db) {
 		rom_db = load_rom_db();
 	}
-	*info_out = configure_rom(rom_db, rom, rom_size, lock_on, lock_on_size, base_map, sizeof(base_map)/sizeof(base_map[0]));
-	rom = info_out->rom;
-	rom_size = info_out->rom_size;
+	rom_info info = configure_rom(rom_db, rom, rom_size, lock_on, lock_on_size, base_map, sizeof(base_map)/sizeof(base_map[0]));
+	rom = info.rom;
+	rom_size = info.rom_size;
 #ifndef BLASTEM_BIG_ENDIAN
 	byteswap_rom(rom_size, rom);
 	if (lock_on) {
@@ -1394,5 +1475,5 @@
 	if (!MCLKS_PER_68K) {
 		MCLKS_PER_68K = 7;
 	}
-	return alloc_init_genesis(info_out, rom, lock_on, ym_opts, force_region);
+	return alloc_init_genesis(&info, rom, lock_on, ym_opts, force_region);
 }
--- a/genesis.h	Sun Dec 31 10:11:16 2017 -0800
+++ b/genesis.h	Tue Dec 25 11:12:26 2018 -0800
@@ -48,6 +48,7 @@
 	uint32_t        max_cycles;
 	uint32_t        int_latency_prev1;
 	uint32_t        int_latency_prev2;
+	uint32_t        reset_cycle;
 	uint8_t         bank_regs[8];
 	uint16_t        mapper_start_index;
 	uint8_t         mapper_type;
@@ -65,7 +66,7 @@
 
 uint16_t read_dma_value(uint32_t address);
 m68k_context * sync_components(m68k_context *context, uint32_t address);
-genesis_context *alloc_config_genesis(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t system_opts, uint8_t force_region, rom_info *info_out);
+genesis_context *alloc_config_genesis(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t system_opts, uint8_t force_region);
 void genesis_serialize(genesis_context *gen, serialize_buffer *buf, uint32_t m68k_pc);
 void genesis_deserialize(deserialize_buffer *buf, genesis_context *gen);
 
Binary file images/360.png has changed
Binary file images/ps4.png has changed
Binary file images/ps4_6b.png has changed
--- a/io.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/io.c	Tue Dec 25 11:12:26 2018 -0800
@@ -18,11 +18,9 @@
 #include "serialize.h"
 #include "io.h"
 #include "blastem.h"
-#include "genesis.h"
-#include "sms.h"
 #include "render.h"
 #include "util.h"
-#include "menu.h"
+#include "bindings.h"
 
 #define CYCLE_NEVER 0xFFFFFFFF
 #define MIN_POLL_INTERVAL 6840
@@ -44,304 +42,147 @@
 	"None"
 };
 
-enum {
-	BIND_NONE,
-	BIND_UI,
-	BIND_GAMEPAD1,
-	BIND_GAMEPAD2,
-	BIND_GAMEPAD3,
-	BIND_GAMEPAD4,
-	BIND_GAMEPAD5,
-	BIND_GAMEPAD6,
-	BIND_GAMEPAD7,
-	BIND_GAMEPAD8,
-	BIND_MOUSE1,
-	BIND_MOUSE2,
-	BIND_MOUSE3,
-	BIND_MOUSE4,
-	BIND_MOUSE5,
-	BIND_MOUSE6,
-	BIND_MOUSE7,
-	BIND_MOUSE8
-};
+#define GAMEPAD_TH0 0
+#define GAMEPAD_TH1 1
+#define GAMEPAD_EXTRA 2
+#define GAMEPAD_NONE 0xF
 
-typedef enum {
-	UI_DEBUG_MODE_INC,
-	UI_DEBUG_PAL_INC,
-	UI_ENTER_DEBUGGER,
-	UI_SAVE_STATE,
-	UI_SET_SPEED,
-	UI_NEXT_SPEED,
-	UI_PREV_SPEED,
-	UI_RELEASE_MOUSE,
-	UI_TOGGLE_KEYBOARD_CAPTURE,
-	UI_TOGGLE_FULLSCREEN,
-	UI_SOFT_RESET,
-	UI_RELOAD,
-	UI_SMS_PAUSE,
-	UI_SCREENSHOT,
-	UI_EXIT
-} ui_action;
+#define IO_TH0 0
+#define IO_TH1 1
+#define IO_STATE 2
 
-typedef enum {
-	MOUSE_NONE,     //mouse is ignored
-	MOUSE_ABSOLUTE, //really only useful for menu ROM
-	MOUSE_RELATIVE, //for full screen
-	MOUSE_CAPTURE   //for windowed mode
-} mouse_modes;
-
-
-typedef struct {
-	io_port *port;
-	uint8_t bind_type;
-	uint8_t subtype_a;
-	uint8_t subtype_b;
-	uint8_t value;
-} keybinding;
-
-typedef struct {
-	keybinding bindings[4];
-	uint8_t    state;
-} joydpad;
-
-typedef struct {
-	keybinding positive;
-	keybinding negative;
-	int16_t    value;
-} joyaxis;
+enum {
+	IO_WRITE_PENDING,
+	IO_WRITTEN,
+	IO_READ_PENDING,
+	IO_READ
+};
 
 typedef struct {
-	keybinding *buttons;
-	joydpad    *dpads;
-	joyaxis    *axes;
-	uint32_t   num_buttons; //number of entries in the buttons array, not necessarily the number of buttons on the device
-	uint32_t   num_dpads;   //number of entries in the dpads array, not necessarily the number of dpads on the device
-	uint32_t   num_axes;    //number of entries in the axes array, not necessarily the number of dpads on the device
-} joystick;
+	uint8_t states[2], value;
+} gp_button_def;
 
-typedef struct {
-	io_port    *motion_port;
-	keybinding buttons[MAX_MOUSE_BUTTONS];
-	uint8_t    bind_type;
-} mousebinding;
 
-#define DEFAULT_JOYBUTTON_ALLOC 12
+static gp_button_def button_defs[NUM_GAMEPAD_BUTTONS] = {
+	[DPAD_UP] = {.states = {GAMEPAD_TH0, GAMEPAD_TH1}, .value = 0x1},
+	[DPAD_DOWN] = {.states = {GAMEPAD_TH0, GAMEPAD_TH1}, .value = 0x2},
+	[DPAD_LEFT] = {.states = {GAMEPAD_TH1, GAMEPAD_NONE}, .value = 0x4},
+	[DPAD_RIGHT] = {.states = {GAMEPAD_TH1, GAMEPAD_NONE}, .value = 0x8},
+	[BUTTON_A] = {.states = {GAMEPAD_TH0, GAMEPAD_NONE}, .value = 0x10},
+	[BUTTON_B] = {.states = {GAMEPAD_TH1, GAMEPAD_NONE}, .value = 0x10},
+	[BUTTON_C] = {.states = {GAMEPAD_TH1, GAMEPAD_NONE}, .value = 0x20},
+	[BUTTON_START] = {.states = {GAMEPAD_TH0, GAMEPAD_NONE}, .value = 0x20},
+	[BUTTON_X] = {.states = {GAMEPAD_EXTRA, GAMEPAD_NONE}, .value = 0x4},
+	[BUTTON_Y] = {.states = {GAMEPAD_EXTRA, GAMEPAD_NONE}, .value = 0x2},
+	[BUTTON_Z] = {.states = {GAMEPAD_EXTRA, GAMEPAD_NONE}, .value = 0x1},
+	[BUTTON_MODE] = {.states = {GAMEPAD_EXTRA, GAMEPAD_NONE}, .value = 0x8},
+};
 
-static sega_io *current_io;
-static keybinding *bindings[0x10000];
-static joystick joysticks[MAX_JOYSTICKS];
-static mousebinding mice[MAX_MICE];
-static io_port *keyboard_port;
-const uint8_t dpadbits[] = {RENDER_DPAD_UP, RENDER_DPAD_DOWN, RENDER_DPAD_LEFT, RENDER_DPAD_RIGHT};
-
-static void do_bind(keybinding *binding, uint8_t bind_type, uint8_t subtype_a, uint8_t subtype_b, uint8_t value)
+static io_port *find_gamepad(sega_io *io, uint8_t gamepad_num)
 {
-	binding->bind_type = bind_type;
-	binding->subtype_a = subtype_a;
-	binding->subtype_b = subtype_b;
-	binding->value = value;
+	for (int i = 0; i < 3; i++)
+	{
+		io_port *port = io->ports + i;
+		if (port->device_type < IO_MOUSE && port->device.pad.gamepad_num == gamepad_num) {
+			return port;
+		}
+	}
+	return NULL;
 }
 
-void bind_key(int keycode, uint8_t bind_type, uint8_t subtype_a, uint8_t subtype_b, uint8_t value)
+static io_port *find_mouse(sega_io *io, uint8_t mouse_num)
 {
-	int bucket = keycode >> 15 & 0xFFFF;
-	if (!bindings[bucket]) {
-		bindings[bucket] = malloc(sizeof(keybinding) * 0x8000);
-		memset(bindings[bucket], 0, sizeof(keybinding) * 0x8000);
+	for (int i = 0; i < 3; i++)
+	{
+		io_port *port = io->ports + i;
+		if (port->device_type == IO_MOUSE && port->device.mouse.mouse_num == mouse_num) {
+			return port;
+		}
 	}
-	int idx = keycode & 0x7FFF;
-	do_bind(bindings[bucket] + idx, bind_type, subtype_a, subtype_b, value);
+	return NULL;
 }
 
-void bind_button(int joystick, int button, uint8_t bind_type, uint8_t subtype_a, uint8_t subtype_b, uint8_t value)
+static io_port *find_keyboard(sega_io *io)
 {
-	if (joystick >= MAX_JOYSTICKS) {
-		return;
+	for (int i = 0; i < 3; i++)
+	{
+		io_port *port = io->ports + i;
+		if (port->device_type == IO_SATURN_KEYBOARD || port->device_type == IO_XBAND_KEYBOARD) {
+			return port;
+		}
 	}
-	if (!joysticks[joystick].buttons) {
-		joysticks[joystick].num_buttons = button < DEFAULT_JOYBUTTON_ALLOC ? DEFAULT_JOYBUTTON_ALLOC : button + 1;
-		joysticks[joystick].buttons = calloc(joysticks[joystick].num_buttons, sizeof(keybinding));
-	} else if (joysticks[joystick].num_buttons <= button) {
-		uint32_t old_capacity = joysticks[joystick].num_buttons;
-		joysticks[joystick].num_buttons *= 2;
-		joysticks[joystick].buttons = realloc(joysticks[joystick].buttons, sizeof(keybinding) * joysticks[joystick].num_buttons);
-		memset(joysticks[joystick].buttons + old_capacity, 0, joysticks[joystick].num_buttons - old_capacity);
-	}
-	do_bind(joysticks[joystick].buttons + button, bind_type, subtype_a, subtype_b, value);
+	return NULL;
 }
 
-void bind_dpad(int joystick, int dpad, int direction, uint8_t bind_type, uint8_t subtype_a, uint8_t subtype_b, uint8_t value)
+void io_port_gamepad_down(io_port *port, uint8_t button)
 {
-	if (joystick >= MAX_JOYSTICKS) {
-		return;
-	}
-	if (!joysticks[joystick].dpads) {
-		//multiple D-pads/hats are not common, so don't allocate any extra space
-		joysticks[joystick].dpads = calloc(dpad+1, sizeof(joydpad));
-		joysticks[joystick].num_dpads = dpad+1;
-	} else if (joysticks[joystick].num_dpads <= dpad) {
-		uint32_t old_capacity = joysticks[joystick].num_dpads;
-		joysticks[joystick].num_dpads *= 2;
-		joysticks[joystick].dpads = realloc(joysticks[joystick].dpads, sizeof(joydpad) * joysticks[joystick].num_dpads);
-		memset(joysticks[joystick].dpads + old_capacity, 0, (joysticks[joystick].num_dpads - old_capacity) * sizeof(joydpad));
-	}
-	for (int i = 0; i < 4; i ++) {
-		if (dpadbits[i] & direction) {
-			do_bind(joysticks[joystick].dpads[dpad].bindings + i, bind_type, subtype_a, subtype_b, value);
-			break;
-		}
+	gp_button_def *def = button_defs + button;
+	port->input[def->states[0]] |= def->value;
+	if (def->states[1] != GAMEPAD_NONE) {
+		port->input[def->states[1]] |= def->value;
 	}
 }
 
-void bind_axis(int joystick, int axis, int positive, uint8_t bind_type, uint8_t subtype_a, uint8_t subtype_b, uint8_t value)
+void io_port_gamepad_up(io_port *port, uint8_t button)
 {
-	if (joystick >= MAX_JOYSTICKS) {
-		return;
-	}
-	if (!joysticks[joystick].axes) {
-		//typical gamepad has 4 axes
-		joysticks[joystick].num_axes = axis+1 > 4 ? axis+1 : 4;
-		joysticks[joystick].axes = calloc(joysticks[joystick].num_axes, sizeof(joyaxis));
-	} else if (joysticks[joystick].num_axes <= axis) {
-		uint32_t old_capacity = joysticks[joystick].num_axes;
-		joysticks[joystick].num_axes *= 2;
-		joysticks[joystick].axes = realloc(joysticks[joystick].axes, sizeof(joyaxis) * joysticks[joystick].num_axes);
-		memset(joysticks[joystick].axes + old_capacity, 0, (joysticks[joystick].num_axes - old_capacity) * sizeof(joyaxis));
-	}
-	if (positive) {
-		do_bind(&joysticks[joystick].axes[axis].positive, bind_type, subtype_a, subtype_b, value);
-	} else {
-		do_bind(&joysticks[joystick].axes[axis].negative, bind_type, subtype_a, subtype_b, value);
+	gp_button_def *def = button_defs + button;
+	port->input[def->states[0]] &= ~def->value;
+	if (def->states[1] != GAMEPAD_NONE) {
+		port->input[def->states[1]] &= ~def->value;
 	}
 }
 
-void reset_joystick_bindings(int joystick)
+void io_gamepad_down(sega_io *io, uint8_t gamepad_num, uint8_t button)
 {
-	if (joystick >= MAX_JOYSTICKS) {
-		return;
-	}
-	if (joysticks[joystick].buttons) {
-		for (int i = 0; i < joysticks[joystick].num_buttons; i++)
-		{
-			joysticks[joystick].buttons[i].bind_type = BIND_NONE;
-		}
+	io_port *port = find_gamepad(io, gamepad_num);
+	if (port) {
+		io_port_gamepad_down(port, button);
 	}
-	if (joysticks[joystick].dpads) {
-		for (int i = 0; i < joysticks[joystick].num_dpads; i++)
-		{
-			for (int dir = 0; dir < 4; dir++)
-			{
-				joysticks[joystick].dpads[i].bindings[dir].bind_type = BIND_NONE;
-			}
-		}
-	}
-	if (joysticks[joystick].axes) {
-		for (int i = 0; i < joysticks[joystick].num_axes; i++)
-		{
-			joysticks[joystick].axes[i].positive.bind_type = BIND_NONE;
-			joysticks[joystick].axes[i].negative.bind_type = BIND_NONE;
-		}
+}
+
+void io_gamepad_up(sega_io *io, uint8_t gamepad_num, uint8_t button)
+{
+	io_port *port = find_gamepad(io, gamepad_num);
+	if (port) {
+		io_port_gamepad_up(port, button);
 	}
 }
 
-#define GAMEPAD_BUTTON(PRI_SLOT, SEC_SLOT, VALUE)  (PRI_SLOT << 12 | SEC_SLOT << 8 | VALUE)
-
-#define DPAD_UP      GAMEPAD_BUTTON(GAMEPAD_TH0, GAMEPAD_TH1, 0x01)
-#define BUTTON_Z     GAMEPAD_BUTTON(GAMEPAD_EXTRA, GAMEPAD_NONE, 0x01)
-#define DPAD_DOWN    GAMEPAD_BUTTON(GAMEPAD_TH0, GAMEPAD_TH1, 0x02)
-#define BUTTON_Y     GAMEPAD_BUTTON(GAMEPAD_EXTRA, GAMEPAD_NONE, 0x02)
-#define DPAD_LEFT    GAMEPAD_BUTTON(GAMEPAD_TH1, GAMEPAD_NONE, 0x04)
-#define BUTTON_X     GAMEPAD_BUTTON(GAMEPAD_EXTRA, GAMEPAD_NONE, 0x04)
-#define DPAD_RIGHT   GAMEPAD_BUTTON(GAMEPAD_TH1, GAMEPAD_NONE, 0x08)
-#define BUTTON_MODE  GAMEPAD_BUTTON(GAMEPAD_EXTRA, GAMEPAD_NONE, 0x08)
-#define BUTTON_A     GAMEPAD_BUTTON(GAMEPAD_TH0, GAMEPAD_NONE, 0x10)
-#define BUTTON_B     GAMEPAD_BUTTON(GAMEPAD_TH1, GAMEPAD_NONE, 0x10)
-#define BUTTON_START GAMEPAD_BUTTON(GAMEPAD_TH0, GAMEPAD_NONE, 0x20)
-#define BUTTON_C     GAMEPAD_BUTTON(GAMEPAD_TH1, GAMEPAD_NONE, 0x20)
-
-#define PSEUDO_BUTTON_MOTION 0xFFFF
-#define MOUSE_LEFT           1
-#define MOUSE_RIGHT          2
-#define MOUSE_MIDDLE         4
-#define MOUSE_START          8
-
-void bind_gamepad(int keycode, int gamepadnum, int button)
+void io_mouse_down(sega_io *io, uint8_t mouse_num, uint8_t button)
 {
-
-	if (gamepadnum < 1 || gamepadnum > 8) {
-		return;
+	io_port *port = find_mouse(io, mouse_num);
+	if (port) {
+		port->input[0] |= button;
 	}
-	uint8_t bind_type = gamepadnum - 1 + BIND_GAMEPAD1;
-	bind_key(keycode, bind_type, button >> 12, button >> 8 & 0xF, button & 0xFF);
-}
-
-void bind_button_gamepad(int joystick, int joybutton, int gamepadnum, int padbutton)
-{
-	if (gamepadnum < 1 || gamepadnum > 8) {
-		return;
-	}
-	uint8_t bind_type = gamepadnum - 1 + BIND_GAMEPAD1;
-	bind_button(joystick, joybutton, bind_type, padbutton >> 12, padbutton >> 8 & 0xF, padbutton & 0xFF);
 }
 
-void bind_dpad_gamepad(int joystick, int dpad, uint8_t direction, int gamepadnum, int button)
-{
-	if (gamepadnum < 1 || gamepadnum > 8) {
-		return;
-	}
-	uint8_t bind_type = gamepadnum - 1 + BIND_GAMEPAD1;
-	bind_dpad(joystick, dpad, direction, bind_type, button >> 12, button >> 8 & 0xF, button & 0xFF);
-}
-
-void bind_axis_gamepad(int joystick, int axis, uint8_t positive, int gamepadnum, int button)
-{
-	if (gamepadnum < 1 || gamepadnum > 8) {
-		return;
-	}
-	uint8_t bind_type = gamepadnum - 1 + BIND_GAMEPAD1;
-	bind_axis(joystick, axis, positive, bind_type, button >> 12, button >> 8 & 0xF, button & 0xFF);
-}
-
-void bind_ui(int keycode, ui_action action, uint8_t param)
-{
-	bind_key(keycode, BIND_UI, action, 0, param);
-}
-
-void bind_button_ui(int joystick, int joybutton, ui_action action, uint8_t param)
+void io_mouse_up(sega_io *io, uint8_t mouse_num, uint8_t button)
 {
-	bind_button(joystick, joybutton, BIND_UI, action, 0, param);
-}
-
-void bind_dpad_ui(int joystick, int dpad, uint8_t direction, ui_action action, uint8_t param)
-{
-	bind_dpad(joystick, dpad, direction, BIND_UI, action, 0, param);
-}
-
-void bind_axis_ui(int joystick, int axis, uint8_t positive, ui_action action, uint8_t param)
-{
-	bind_axis(joystick, axis, positive, BIND_UI, action, 0, param);
-}
-
-void handle_binding_down(keybinding * binding)
-{
-	if (binding->bind_type >= BIND_GAMEPAD1 && binding->bind_type <= BIND_GAMEPAD8)
-	{
-		if (binding->subtype_a <= GAMEPAD_EXTRA && binding->port) {
-			binding->port->input[binding->subtype_a] |= binding->value;
-		}
-		if (binding->subtype_b <= GAMEPAD_EXTRA && binding->port) {
-			binding->port->input[binding->subtype_b] |= binding->value;
-		}
-	}
-	else if (binding->bind_type >= BIND_MOUSE1 && binding->bind_type <= BIND_MOUSE8)
-	{
-		if (binding->port) {
-			binding->port->input[0] |= binding->value;
-		}
+	io_port *port = find_mouse(io, mouse_num);
+	if (port) {
+		port->input[0] &= ~button;
 	}
 }
 
-void store_key_event(uint16_t code)
+void io_mouse_motion_absolute(sega_io *io, uint8_t mouse_num, uint16_t x, uint16_t y)
+{
+	io_port *port = find_mouse(io, mouse_num);
+	if (port) {
+		port->device.mouse.cur_x = x;
+		port->device.mouse.cur_y = y;
+	}
+}
+
+void io_mouse_motion_relative(sega_io *io, uint8_t mouse_num, int32_t x, int32_t y)
+{
+	io_port *port = find_mouse(io, mouse_num);
+	if (port) {
+		port->device.mouse.cur_x += x;
+		port->device.mouse.cur_y += y;
+	}
+}
+
+void store_key_event(io_port *keyboard_port, uint16_t code)
 {
 	if (keyboard_port && keyboard_port->device.keyboard.write_pos != keyboard_port->device.keyboard.read_pos) {
 		//there's room in the buffer, record this event
@@ -354,482 +195,24 @@
 	}
 }
 
-void handle_keydown(int keycode, uint8_t scancode)
-{
-	int bucket = keycode >> 15 & 0xFFFF;
-	int idx = keycode & 0x7FFF;
-	keybinding * binding = bindings[bucket] ? bindings[bucket] + idx : NULL;
-	if (binding && (!current_io->keyboard_captured || (binding->bind_type == BIND_UI && binding->subtype_a == UI_TOGGLE_KEYBOARD_CAPTURE))) {
-		handle_binding_down(binding);
-	} else if (current_io->keyboard_captured) {
-		store_key_event(scancode);
-	}
-}
-
-void handle_joydown(int joystick, int button)
-{
-	if (joystick >= MAX_JOYSTICKS || button >= joysticks[joystick].num_buttons) {
-		return;
-	}
-	keybinding * binding = joysticks[joystick].buttons + button;
-	handle_binding_down(binding);
-}
-
-void handle_mousedown(int mouse, int button)
-{
-	if (current_io->mouse_mode == MOUSE_CAPTURE && !current_io->mouse_captured) {
-		current_io->mouse_captured = 1;
-		render_relative_mouse(1);
-		return;
-	}
-	if (mouse >= MAX_MICE || button > MAX_MOUSE_BUTTONS || button <= 0) {
-		return;
-	}
-	keybinding * binding = mice[mouse].buttons + button - 1;
-	handle_binding_down(binding);
-}
-
-uint8_t ui_debug_mode = 0;
-uint8_t ui_debug_pal = 0;
-
-int current_speed = 0;
-int num_speeds = 1;
-uint32_t * speeds = NULL;
-
-uint8_t is_keyboard(io_port *port)
-{
-	return port->device_type == IO_SATURN_KEYBOARD || port->device_type == IO_XBAND_KEYBOARD;
-}
-
-uint8_t keyboard_connected(sega_io *io)
-{
-	return is_keyboard(io->ports) || is_keyboard(io->ports+1) || is_keyboard(io->ports+2);
-}
-
-#ifdef _WIN32
-#define localtime_r(a,b) localtime(a)
-#endif
-
-void handle_binding_up(keybinding * binding)
+void io_keyboard_down(sega_io *io, uint8_t scancode)
 {
-	switch(binding->bind_type)
-	{
-	case BIND_GAMEPAD1:
-	case BIND_GAMEPAD2:
-	case BIND_GAMEPAD3:
-	case BIND_GAMEPAD4:
-	case BIND_GAMEPAD5:
-	case BIND_GAMEPAD6:
-	case BIND_GAMEPAD7:
-	case BIND_GAMEPAD8:
-		if (binding->subtype_a <= GAMEPAD_EXTRA && binding->port) {
-			binding->port->input[binding->subtype_a] &= ~binding->value;
-		}
-		if (binding->subtype_b <= GAMEPAD_EXTRA && binding->port) {
-			binding->port->input[binding->subtype_b] &= ~binding->value;
-		}
-		break;
-	case BIND_MOUSE1:
-	case BIND_MOUSE2:
-	case BIND_MOUSE3:
-	case BIND_MOUSE4:
-	case BIND_MOUSE5:
-	case BIND_MOUSE6:
-	case BIND_MOUSE7:
-	case BIND_MOUSE8:
-		if (binding->port) {
-			binding->port->input[0] &= ~binding->value;
-		}
-		break;
-	case BIND_UI:
-		switch (binding->subtype_a)
-		{
-		case UI_DEBUG_MODE_INC:
-			current_system->inc_debug_mode(current_system);
-			break;
-		case UI_DEBUG_PAL_INC:
-			current_system->inc_debug_pal(current_system);
-			break;
-		case UI_ENTER_DEBUGGER:
-			current_system->enter_debugger = 1;
-			break;
-		case UI_SAVE_STATE:
-			current_system->save_state = QUICK_SAVE_SLOT+1;
-			break;
-		case UI_NEXT_SPEED:
-			current_speed++;
-			if (current_speed >= num_speeds) {
-				current_speed = 0;
-			}
-			printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]);
-			current_system->set_speed_percent(current_system, speeds[current_speed]);
-			break;
-		case UI_PREV_SPEED:
-			current_speed--;
-			if (current_speed < 0) {
-				current_speed = num_speeds - 1;
-			}
-			printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]);
-			current_system->set_speed_percent(current_system, speeds[current_speed]);
-			break;
-		case UI_SET_SPEED:
-			if (binding->value < num_speeds) {
-				current_speed = binding->value;
-				printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]);
-				current_system->set_speed_percent(current_system, speeds[current_speed]);
-			} else {
-				printf("Setting speed to %d\n", speeds[current_speed]);
-				current_system->set_speed_percent(current_system, speeds[current_speed]);
-			}
-			break;
-		case UI_RELEASE_MOUSE:
-			if (current_io->mouse_captured) {
-				current_io->mouse_captured = 0;
-				render_relative_mouse(0);
-			}
-			break;
-		case UI_TOGGLE_KEYBOARD_CAPTURE:
-			if (keyboard_connected(current_io)) {
-				current_io->keyboard_captured = !current_io->keyboard_captured;
-			}
-			break;
-		case UI_TOGGLE_FULLSCREEN:
-			render_toggle_fullscreen();
-			break;
-		case UI_SOFT_RESET:
-			current_system->soft_reset(current_system);
-			break;
-		case UI_RELOAD:
-			reload_media();
-			break;
-		case UI_SMS_PAUSE:
-			if (current_system->type == SYSTEM_SMS) {
-				sms_context *sms = (sms_context *)current_system;
-				vdp_pbc_pause(sms->vdp);
-			}
-			break;
-		case UI_SCREENSHOT: {
-			char *screenshot_base = tern_find_path(config, "ui\0screenshot_path\0", TVAL_PTR).ptrval;
-			if (!screenshot_base) {
-				screenshot_base = "$HOME";
-			}
-			tern_node *vars = tern_insert_ptr(NULL, "HOME", get_home_dir());
-			vars = tern_insert_ptr(vars, "EXEDIR", get_exe_dir());
-			screenshot_base = replace_vars(screenshot_base, vars, 1);
-			tern_free(vars);
-			time_t now = time(NULL);
-			struct tm local_store;
-			char fname_part[256];
-			char *template = tern_find_path(config, "ui\0screenshot_template\0", TVAL_PTR).ptrval;
-			if (!template) {
-				template = "blastem_%c.ppm";
-			}
-			strftime(fname_part, sizeof(fname_part), template, localtime_r(&now, &local_store));
-			char const *parts[] = {screenshot_base, PATH_SEP, fname_part};
-			char *path = alloc_concat_m(3, parts);
-			free(screenshot_base);
-			render_save_screenshot(path);
-			break;
-		}
-		case UI_EXIT:
-			current_system->request_exit(current_system);
-			if (current_system->type == SYSTEM_GENESIS) {
-				genesis_context *gen = (genesis_context *)current_system;
-				if (gen->extra) {
-					//TODO: More robust mechanism for detecting menu
-					menu_context *menu = gen->extra;
-					menu->external_game_load = 1;
-				}
-			}
-			break;
-		}
-		break;
-	}
-}
-
-void handle_keyup(int keycode, uint8_t scancode)
-{
-	int bucket = keycode >> 15 & 0xFFFF;
-	int idx = keycode & 0x7FFF;
-	keybinding * binding = bindings[bucket] ? bindings[bucket] + idx : NULL;
-	if (binding && (!current_io->keyboard_captured || (binding->bind_type == BIND_UI && binding->subtype_a == UI_TOGGLE_KEYBOARD_CAPTURE))) {
-		handle_binding_up(binding);
-	} else if (current_io->keyboard_captured) {
-		store_key_event(0xF000 | scancode);
-	}
-}
-
-void handle_joyup(int joystick, int button)
-{
-	if (joystick >= MAX_JOYSTICKS  || button >= joysticks[joystick].num_buttons) {
-		return;
-	}
-	keybinding * binding = joysticks[joystick].buttons + button;
-	handle_binding_up(binding);
-}
-
-void handle_joy_dpad(int joystick, int dpadnum, uint8_t value)
-{
-	if (joystick >= MAX_JOYSTICKS  || dpadnum >= joysticks[joystick].num_dpads) {
-		return;
-	}
-	joydpad * dpad = joysticks[joystick].dpads + dpadnum;
-	uint8_t newdown = (value ^ dpad->state) & value;
-	uint8_t newup = ((~value) ^ (~dpad->state)) & (~value);
-	dpad->state = value;
-	for (int i = 0; i < 4; i++) {
-		if (newdown & dpadbits[i]) {
-			handle_binding_down(dpad->bindings + i);
-		} else if(newup & dpadbits[i]) {
-			handle_binding_up(dpad->bindings + i);
-		}
-	}
+	store_key_event(find_keyboard(io), scancode);
 }
 
-#define JOY_AXIS_THRESHOLD 2000
-
-void handle_joy_axis(int joystick, int axis, int16_t value)
-{
-	if (joystick >= MAX_JOYSTICKS  || axis >= joysticks[joystick].num_axes) {
-		return;
-	}
-	joyaxis *jaxis = joysticks[joystick].axes + axis;
-	int old_active = abs(jaxis->value) > JOY_AXIS_THRESHOLD;
-	int new_active = abs(value) > JOY_AXIS_THRESHOLD;
-	int old_pos = jaxis->value > 0;
-	int new_pos = value > 0;
-	jaxis->value = value;
-	if (old_active && (!new_active || old_pos != new_pos)) {
-		//previously activated direction is no longer active
-		handle_binding_up(old_pos ? &jaxis->positive : &jaxis->negative);
-	}
-	if (new_active && (!old_active || old_pos != new_pos)) {
-		//previously unactivated direction is now active
-		handle_binding_down(new_pos ? &jaxis->positive : &jaxis->negative);
-	}
-}
-
-void handle_mouseup(int mouse, int button)
+void io_keyboard_up(sega_io *io, uint8_t scancode)
 {
-	if (mouse >= MAX_MICE || button > MAX_MOUSE_BUTTONS || button <= 0) {
-		return;
-	}
-	keybinding * binding = mice[mouse].buttons + button - 1;
-	handle_binding_up(binding);
-}
-
-void handle_mouse_moved(int mouse, uint16_t x, uint16_t y, int16_t deltax, int16_t deltay)
-{
-	if (mouse >= MAX_MICE || !mice[mouse].motion_port) {
-		return;
-	}
-	switch(current_io->mouse_mode)
-	{
-	case MOUSE_NONE:
-		break;
-	case MOUSE_ABSOLUTE: {
-		float scale_x = (render_emulated_width() * 2.0f) / ((float)render_width());
-		float scale_y = (render_emulated_height() * 2.0f) / ((float)render_height());
-		int32_t adj_x = x * scale_x + 2 * render_overscan_left() - 2 * BORDER_LEFT;
-		int32_t adj_y = y * scale_y + 2 * render_overscan_top() - 4;
-		if (adj_x >= 0 && adj_y >= 0) {
-			mice[mouse].motion_port->device.mouse.cur_x = adj_x;
-			mice[mouse].motion_port->device.mouse.cur_y = adj_y;
-		}
-		break;
-	}
-	case MOUSE_RELATIVE: {
-		mice[mouse].motion_port->device.mouse.cur_x += deltax;
-		mice[mouse].motion_port->device.mouse.cur_y += deltay;
-		break;
-	}
-	case MOUSE_CAPTURE: {
-		if (current_io->mouse_captured) {
-			mice[mouse].motion_port->device.mouse.cur_x += deltax;
-			mice[mouse].motion_port->device.mouse.cur_y += deltay;
-		}
-		break;
-	}
-	}
+	store_key_event(find_keyboard(io), 0xF000 | scancode);
 }
 
-int parse_binding_target(char * target, tern_node * padbuttons, tern_node *mousebuttons, int * ui_out, int * padnum_out, int * padbutton_out)
-{
-	const int gpadslen = strlen("gamepads.");
-	const int mouselen = strlen("mouse.");
-	if (!strncmp(target, "gamepads.", gpadslen)) {
-		if (target[gpadslen] >= '1' && target[gpadslen] <= '8') {
-			int padnum = target[gpadslen] - '0';
-			int button = tern_find_int(padbuttons, target + gpadslen + 1, 0);
-			if (button) {
-				*padnum_out = padnum;
-				*padbutton_out = button;
-				return BIND_GAMEPAD1;
-			} else {
-				if (target[gpadslen+1]) {
-					warning("Gamepad mapping string '%s' refers to an invalid button '%s'\n", target, target + gpadslen + 1);
-				} else {
-					warning("Gamepad mapping string '%s' has no button component\n", target);
-				}
-			}
-		} else {
-			warning("Gamepad mapping string '%s' refers to an invalid gamepad number %c\n", target, target[gpadslen]);
-		}
-	} else if(!strncmp(target, "mouse.", mouselen)) {
-		if (target[mouselen] >= '1' && target[mouselen] <= '8') {
-			int mousenum = target[mouselen] - '0';
-			int button = tern_find_int(mousebuttons, target + mouselen + 1, 0);
-			if (button) {
-				*padnum_out = mousenum;
-				*padbutton_out = button;
-				return BIND_MOUSE1;
-			} else {
-				if (target[mouselen+1]) {
-					warning("Mouse mapping string '%s' refers to an invalid button '%s'\n", target, target + mouselen + 1);
-				} else {
-					warning("Mouse mapping string '%s' has no button component\n", target);
-				}
-			}
-		} else {
-			warning("Gamepad mapping string '%s' refers to an invalid mouse number %c\n", target, target[mouselen]);
-		}
-	} else if(!strncmp(target, "ui.", strlen("ui."))) {
-		*padbutton_out = 0;
-		if (!strcmp(target + 3, "vdp_debug_mode")) {
-			*ui_out = UI_DEBUG_MODE_INC;
-		} else if(!strcmp(target + 3, "vdp_debug_pal")) {
-			*ui_out = UI_DEBUG_PAL_INC;
-		} else if(!strcmp(target + 3, "enter_debugger")) {
-			*ui_out = UI_ENTER_DEBUGGER;
-		} else if(!strcmp(target + 3, "save_state")) {
-			*ui_out = UI_SAVE_STATE;
-		} else if(!strncmp(target + 3, "set_speed.", strlen("set_speed."))) {
-			*ui_out = UI_SET_SPEED;
-			*padbutton_out = atoi(target + 3 + strlen("set_speed."));
-		} else if(!strcmp(target + 3, "next_speed")) {
-			*ui_out = UI_NEXT_SPEED;
-		} else if(!strcmp(target + 3, "prev_speed")) {
-			*ui_out = UI_PREV_SPEED;
-		} else if(!strcmp(target + 3, "release_mouse")) {
-			*ui_out = UI_RELEASE_MOUSE;
-		} else if(!strcmp(target + 3, "toggle_keyboard_captured")) {
-			*ui_out = UI_TOGGLE_KEYBOARD_CAPTURE;
-		} else if (!strcmp(target + 3, "toggle_fullscreen")) {
-			*ui_out = UI_TOGGLE_FULLSCREEN;
-		} else if (!strcmp(target + 3, "soft_reset")) {
-			*ui_out = UI_SOFT_RESET;
-		} else if (!strcmp(target + 3, "reload")) {
-			*ui_out = UI_RELOAD;
-		} else if (!strcmp(target + 3, "sms_pause")) {
-			*ui_out = UI_SMS_PAUSE;
-		} else if (!strcmp(target + 3, "screenshot")) {
-			*ui_out = UI_SCREENSHOT;
-		} else if(!strcmp(target + 3, "exit")) {
-			*ui_out = UI_EXIT;
-		} else {
-			warning("Unreconized UI binding type %s\n", target);
-			return 0;
-		}
-		return BIND_UI;
-	} else {
-		warning("Unrecognized binding type %s\n", target);
-	}
-	return 0;
-}
-
-void process_keys(tern_node * cur, tern_node * special, tern_node * padbuttons, tern_node *mousebuttons, char * prefix)
+uint8_t io_has_keyboard(sega_io *io)
 {
-	char * curstr = NULL;
-	int len;
-	if (!cur) {
-		return;
-	}
-	char onec[2];
-	if (prefix) {
-		len = strlen(prefix);
-		curstr = malloc(len + 2);
-		memcpy(curstr, prefix, len);
-	} else {
-		curstr = onec;
-		len = 0;
-	}
-	curstr[len] = cur->el;
-	curstr[len+1] = 0;
-	if (cur->el) {
-		process_keys(cur->straight.next, special, padbuttons, mousebuttons, curstr);
-	} else {
-		int keycode = tern_find_int(special, curstr, 0);
-		if (!keycode) {
-			keycode = curstr[0];
-			if (curstr[1] != 0) {
-				warning("%s is not recognized as a key identifier, truncating to %c\n", curstr, curstr[0]);
-			}
-		}
-		char * target = cur->straight.value.ptrval;
-		int ui_func, padnum, button;
-		int bindtype = parse_binding_target(target, padbuttons, mousebuttons, &ui_func, &padnum, &button);
-		if (bindtype == BIND_GAMEPAD1) {
-			bind_gamepad(keycode, padnum, button);
-		} else if(bindtype == BIND_UI) {
-			bind_ui(keycode, ui_func, button);
-		}
-	}
-	process_keys(cur->left, special, padbuttons, mousebuttons, prefix);
-	process_keys(cur->right, special, padbuttons, mousebuttons, prefix);
-	if (curstr && len) {
-		free(curstr);
-	}
-}
-
-void process_speeds(tern_node * cur, char * prefix)
-{
-	char * curstr = NULL;
-	int len;
-	if (!cur) {
-		return;
-	}
-	char onec[2];
-	if (prefix) {
-		len = strlen(prefix);
-		curstr = malloc(len + 2);
-		memcpy(curstr, prefix, len);
-	} else {
-		curstr = onec;
-		len = 0;
-	}
-	curstr[len] = cur->el;
-	curstr[len+1] = 0;
-	if (cur->el) {
-		process_speeds(cur->straight.next, curstr);
-	} else {
-		char *end;
-		long speed_index = strtol(curstr, &end, 10);
-		if (speed_index < 0 || end == curstr || *end) {
-			warning("%s is not a valid speed index", curstr);
-		} else {
-			if (speed_index >= num_speeds) {
-				speeds = realloc(speeds, sizeof(uint32_t) * (speed_index+1));
-				for(; num_speeds < speed_index + 1; num_speeds++) {
-					speeds[num_speeds] = 0;
-				}
-			}
-			speeds[speed_index] = atoi(cur->straight.value.ptrval);
-			if (speeds[speed_index] < 1) {
-				warning("%s is not a valid speed percentage, setting speed %d to 100", cur->straight.value.ptrval, speed_index);
-				speeds[speed_index] = 100;
-			}
-		}
-	}
-	process_speeds(cur->left, prefix);
-	process_speeds(cur->right, prefix);
-	if (curstr && len) {
-		free(curstr);
-	}
+	return find_keyboard(io) != NULL;
 }
 
 void process_device(char * device_type, io_port * port)
 {
-	port->device_type = IO_NONE;
+	//assuming that the io_port struct has been zeroed if this is the first time this has been called
 	if (!device_type)
 	{
 		return;
@@ -852,34 +235,44 @@
 		} else {
 			port->device_type = IO_GAMEPAD6;
 		}
-		port->device.pad.gamepad_num = device_type[gamepad_len+2] - '1';
+		port->device.pad.gamepad_num = device_type[gamepad_len+2] - '0';
 	} else if(!strncmp(device_type, "mouse", mouse_len)) {
-		port->device_type = IO_MOUSE;
-		port->device.mouse.mouse_num = device_type[mouse_len+1] - '1';
-		port->device.mouse.last_read_x = 0;
-		port->device.mouse.last_read_y = 0;
-		port->device.mouse.cur_x = 0;
-		port->device.mouse.cur_y = 0;
-		port->device.mouse.latched_x = 0;
-		port->device.mouse.latched_y = 0;
-		port->device.mouse.ready_cycle = CYCLE_NEVER;
-		port->device.mouse.tr_counter = 0;
+		if (port->device_type != IO_MOUSE) {
+			port->device_type = IO_MOUSE;
+			port->device.mouse.mouse_num = device_type[mouse_len+1] - '0';
+			port->device.mouse.last_read_x = 0;
+			port->device.mouse.last_read_y = 0;
+			port->device.mouse.cur_x = 0;
+			port->device.mouse.cur_y = 0;
+			port->device.mouse.latched_x = 0;
+			port->device.mouse.latched_y = 0;
+			port->device.mouse.ready_cycle = CYCLE_NEVER;
+			port->device.mouse.tr_counter = 0;
+		}
 	} else if(!strcmp(device_type, "saturn keyboard")) {
-		port->device_type = IO_SATURN_KEYBOARD;
-		port->device.keyboard.read_pos = 0xFF;
-		port->device.keyboard.write_pos = 0;
+		if (port->device_type != IO_SATURN_KEYBOARD) {
+			port->device_type = IO_SATURN_KEYBOARD;
+			port->device.keyboard.read_pos = 0xFF;
+			port->device.keyboard.write_pos = 0;
+		}
 	} else if(!strcmp(device_type, "xband keyboard")) {
-		port->device_type = IO_XBAND_KEYBOARD;
-		port->device.keyboard.read_pos = 0xFF;
-		port->device.keyboard.write_pos = 0;
+		if (port->device_type != IO_XBAND_KEYBOARD) {
+			port->device_type = IO_XBAND_KEYBOARD;
+			port->device.keyboard.read_pos = 0xFF;
+			port->device.keyboard.write_pos = 0;
+		}
 	} else if(!strcmp(device_type, "sega_parallel")) {
-		port->device_type = IO_SEGA_PARALLEL;
-		port->device.stream.data_fd = -1;
-		port->device.stream.listen_fd = -1;
+		if (port->device_type != IO_SEGA_PARALLEL) {
+			port->device_type = IO_SEGA_PARALLEL;
+			port->device.stream.data_fd = -1;
+			port->device.stream.listen_fd = -1;
+		}
 	} else if(!strcmp(device_type, "generic")) {
-		port->device_type = IO_GENERIC;
-		port->device.stream.data_fd = -1;
-		port->device.stream.listen_fd = -1;
+		if (port->device_type != IO_GENERIC) {
+			port->device_type = IO_GENERIC;
+			port->device.stream.data_fd = -1;
+			port->device.stream.listen_fd = -1;
+		}
 	}
 }
 
@@ -906,8 +299,7 @@
 
 void setup_io_devices(tern_node * config, rom_info *rom, sega_io *io)
 {
-	current_io = io;
-	io_port * ports = current_io->ports;
+	io_port * ports = io->ports;
 	tern_node *io_nodes = tern_find_path(config, "io\0devices\0", TVAL_NODE).ptrval;
 	char * io_1 = rom->port1_override ? rom->port1_override : io_nodes ? tern_find_ptr(io_nodes, "1") : NULL;
 	char * io_2 = rom->port2_override ? rom->port2_override : io_nodes ? tern_find_ptr(io_nodes, "2") : NULL;
@@ -917,25 +309,26 @@
 	process_device(io_2, ports+1);
 	process_device(io_ext, ports+2);
 
+	uint8_t mouse_mode;
 	if (ports[0].device_type == IO_MOUSE || ports[1].device_type == IO_MOUSE || ports[2].device_type == IO_MOUSE) {
 		if (render_fullscreen()) {
-				current_io->mouse_mode = MOUSE_RELATIVE;
-				render_relative_mouse(1);
+				mouse_mode = MOUSE_RELATIVE;
 		} else {
 			if (rom->mouse_mode && !strcmp(rom->mouse_mode, "absolute")) {
-				current_io->mouse_mode = MOUSE_ABSOLUTE;
+				mouse_mode = MOUSE_ABSOLUTE;
 			} else {
-				current_io->mouse_mode = MOUSE_CAPTURE;
+				mouse_mode = MOUSE_CAPTURE;
 			}
 		}
 	} else {
-		current_io->mouse_mode = MOUSE_NONE;
+		mouse_mode = MOUSE_NONE;
 	}
+	bindings_set_mouse_mode(mouse_mode);
 
 	for (int i = 0; i < 3; i++)
 	{
 #ifndef _WIN32
-		if (ports[i].device_type == IO_SEGA_PARALLEL)
+		if (ports[i].device_type == IO_SEGA_PARALLEL && ports[i].device.stream.data_fd == -1)
 		{
 			char *pipe_name = tern_find_path(config, "io\0parallel_pipe\0", TVAL_PTR).ptrval;
 			if (!pipe_name)
@@ -962,7 +355,7 @@
 					}
 				}
 			}
-		} else if (ports[i].device_type == IO_GENERIC) {
+		} else if (ports[i].device_type == IO_GENERIC && ports[i].device.stream.data_fd == -1) {
 			char *sock_name = tern_find_path(config, "io\0socket\0", TVAL_PTR).ptrval;
 			if (!sock_name)
 			{
@@ -1006,496 +399,6 @@
 	}
 }
 
-void map_bindings(io_port *ports, keybinding *bindings, int numbindings)
-{
-	for (int i = 0; i < numbindings; i++)
-	{
-		if (bindings[i].bind_type >= BIND_GAMEPAD1 && bindings[i].bind_type <= BIND_GAMEPAD8)
-		{
-			int num = bindings[i].bind_type - BIND_GAMEPAD1;
-			for (int j = 0; j < 3; j++)
-			{
-				if ((ports[j].device_type == IO_GAMEPAD3
-					 || ports[j].device_type == IO_GAMEPAD6
-					 || ports[j].device_type == IO_GAMEPAD2)
-					 && ports[j].device.pad.gamepad_num == num
-				)
-				{
-					memset(ports[j].input, 0, sizeof(ports[j].input));
-					bindings[i].port = ports + j;
-					break;
-				}
-			}
-		}
-		else if (bindings[i].bind_type >= BIND_MOUSE1 && bindings[i].bind_type <= BIND_MOUSE8)
-		{
-			int num = bindings[i].bind_type - BIND_MOUSE1;
-			for (int j = 0; j < 3; j++)
-			{
-				if (ports[j].device_type == IO_MOUSE && ports[j].device.mouse.mouse_num == num)
-				{
-					memset(ports[j].input, 0, sizeof(ports[j].input));
-					bindings[i].port = ports + j;
-					break;
-				}
-			}
-		}
-	}
-}
-
-typedef struct {
-	tern_node *padbuttons;
-	tern_node *mousebuttons;
-	int       mouseidx;
-} pmb_state;
-
-void process_mouse_button(char *buttonstr, tern_val value, uint8_t valtype, void *data)
-{
-	pmb_state *state = data;
-	int buttonnum = atoi(buttonstr);
-	if (buttonnum < 1 || buttonnum > MAX_MOUSE_BUTTONS) {
-		warning("Mouse button %s is out of the supported range of 1-8\n", buttonstr);
-		return;
-	}
-	if (valtype != TVAL_PTR) {
-		warning("Mouse button %s is not a scalar value!\n", buttonstr);
-		return;
-	}
-	buttonnum--;
-	int ui_func, devicenum, button;
-	int bindtype = parse_binding_target(value.ptrval, state->padbuttons, state->mousebuttons, &ui_func, &devicenum, &button);
-	switch (bindtype)
-	{
-	case BIND_UI:
-		mice[state->mouseidx].buttons[buttonnum].subtype_a = ui_func;
-		break;
-	case BIND_GAMEPAD1:
-		mice[state->mouseidx].buttons[buttonnum].subtype_a = button >> 12;
-		mice[state->mouseidx].buttons[buttonnum].subtype_b = button >> 8 & 0xF;
-		mice[state->mouseidx].buttons[buttonnum].value = button & 0xFF;
-		break;
-	case BIND_MOUSE1:
-		mice[state->mouseidx].buttons[buttonnum].value = button & 0xFF;
-		break;
-	}
-	if (bindtype != BIND_UI) {
-		bindtype += devicenum-1;
-	}
-	mice[state->mouseidx].buttons[buttonnum].bind_type = bindtype;
-
-}
-
-void process_mouse(char *mousenum, tern_val value, uint8_t valtype, void *data)
-{
-	tern_node **buttonmaps = data;
-	if (valtype != TVAL_NODE) {
-		warning("Binding for mouse %s is a scalar!\n", mousenum);
-		return;
-	}
-	tern_node *mousedef = value.ptrval;
-	tern_node *padbuttons = buttonmaps[0];
-	tern_node *mousebuttons = buttonmaps[1];
-
-	int mouseidx = atoi(mousenum);
-	if (mouseidx < 0 || mouseidx >= MAX_MICE) {
-		warning("Mouse numbers must be between 0 and %d, but %d is not\n", MAX_MICE, mouseidx);
-		return;
-	}
-	char *motion = tern_find_ptr(mousedef, "motion");
-	if (motion) {
-		int ui_func,devicenum,button;
-		int bindtype = parse_binding_target(motion, padbuttons, mousebuttons, &ui_func, &devicenum, &button);
-		if (bindtype != BIND_UI) {
-			bindtype += devicenum-1;
-		}
-		if (button == PSEUDO_BUTTON_MOTION) {
-			mice[mouseidx].bind_type = bindtype;
-		} else {
-			warning("Mouse motion can't be bound to target %s\n", motion);
-		}
-	}
-	tern_node *buttons = tern_find_path(mousedef, "buttons\0\0", TVAL_NODE).ptrval;
-	if (buttons) {
-		pmb_state state = {padbuttons, mousebuttons, mouseidx};
-		tern_foreach(buttons, process_mouse_button, &state);
-	}
-}
-
-typedef struct {
-	int       padnum;
-	tern_node *padbuttons;
-	tern_node *mousebuttons;
-} pad_button_state;
-
-
-static long map_warning_pad = -1;
-void process_pad_button(char *key, tern_val val, uint8_t valtype, void *data)
-{
-	pad_button_state *state = data;
-	int hostpadnum = state->padnum;
-	int ui_func, padnum, button;
-	if (valtype != TVAL_PTR) {
-		warning("Pad button %s has a non-scalar value\n", key);
-		return;
-	}
-	int bindtype = parse_binding_target(val.ptrval, state->padbuttons, state->mousebuttons, &ui_func, &padnum, &button);
-	char *end;
-	long hostbutton = strtol(key, &end, 10);
-	if (*end) {
-		//key is not a valid base 10 integer
-		hostbutton = render_translate_input_name(hostpadnum, key, 0);
-		if (hostbutton < 0) {
-			if (hostbutton == RENDER_INVALID_NAME) {
-				warning("%s is not a valid gamepad input name\n", key);
-			} else if (hostbutton == RENDER_NOT_MAPPED && hostpadnum != map_warning_pad) {
-				warning("No SDL 2 mapping exists for input %s on gamepad %d\n", key, hostpadnum);
-				map_warning_pad = hostpadnum;
-			}
-			return;
-		}
-		if (hostbutton & RENDER_DPAD_BIT) {
-			if (bindtype == BIND_GAMEPAD1) {
-				bind_dpad_gamepad(hostpadnum, render_dpad_part(hostbutton), render_direction_part(hostbutton), padnum, button);
-			} else {
-				bind_dpad_ui(hostpadnum, render_dpad_part(hostbutton), render_direction_part(hostbutton), ui_func, button);
-			}
-			return;
-		} else if (hostbutton & RENDER_AXIS_BIT) {
-			if (bindtype == BIND_GAMEPAD1) {
-				bind_axis_gamepad(hostpadnum, render_axis_part(hostbutton), 1, padnum, button);
-			} else {
-				bind_axis_ui(hostpadnum, render_axis_part(hostbutton), 1, padnum, button);
-			}
-			return;
-		}
-	}
-	if (bindtype == BIND_GAMEPAD1) {
-		bind_button_gamepad(hostpadnum, hostbutton, padnum, button);
-	} else if (bindtype == BIND_UI) {
-		bind_button_ui(hostpadnum, hostbutton, ui_func, button);
-	}
-}
-
-void process_pad_axis(char *key, tern_val val, uint8_t valtype, void *data)
-{
-	key = strdup(key);
-	pad_button_state *state = data;
-	int hostpadnum = state->padnum;
-	int ui_func, padnum, button;
-	if (valtype != TVAL_PTR) {
-		warning("Mapping for axis %s has a non-scalar value", key);
-		return;
-	}
-	int bindtype = parse_binding_target(val.ptrval, state->padbuttons, state->mousebuttons, &ui_func, &padnum, &button);
-	char *modifier = strchr(key, '.');
-	int positive = 1;
-	if (modifier) {
-		*modifier = 0;
-		modifier++;
-		if (!strcmp("negative", modifier)) {
-			positive = 0;
-		} else if(strcmp("positive", modifier)) {
-			warning("Invalid axis modifier %s for axis %s on pad %d\n", modifier, key, hostpadnum);
-		}
-	}
-	char *end;
-	long axis = strtol(key, &end, 10);
-	if (*end) {
-		//key is not a valid base 10 integer
-		axis = render_translate_input_name(hostpadnum, key, 1);
-		if (axis < 0) {
-			if (axis == RENDER_INVALID_NAME) {
-				warning("%s is not a valid gamepad input name\n", key);
-			} else if (axis == RENDER_NOT_MAPPED && hostpadnum != map_warning_pad) {
-				warning("No SDL 2 mapping exists for input %s on gamepad %d\n", key, hostpadnum);
-				map_warning_pad = hostpadnum;
-			}
-			goto done;
-		}
-		if (axis & RENDER_DPAD_BIT) {
-			if (bindtype == BIND_GAMEPAD1) {
-				bind_dpad_gamepad(hostpadnum, render_dpad_part(axis), render_direction_part(axis), padnum, button);
-			} else {
-				bind_dpad_ui(hostpadnum, render_dpad_part(axis), render_direction_part(axis), ui_func, button);
-			}
-			goto done;
-		} else if (axis & RENDER_AXIS_BIT) {
-			axis = render_axis_part(axis);
-		} else {
-			if (bindtype == BIND_GAMEPAD1) {
-				bind_button_gamepad(hostpadnum, axis, padnum, button);
-			} else if (bindtype == BIND_UI) {
-				bind_button_ui(hostpadnum, axis, ui_func, button);
-			}
-			goto done;
-		}
-	}
-	if (bindtype == BIND_GAMEPAD1) {
-		bind_axis_gamepad(hostpadnum, axis, positive, padnum, button);
-	} else {
-		bind_axis_ui(hostpadnum, axis, positive, ui_func, button);
-	}
-done:
-	free(key);
-	return;
-}
-
-static tern_node *get_pad_buttons()
-{
-	static tern_node *padbuttons;
-	if (!padbuttons) {
-		padbuttons = tern_insert_int(NULL, ".up", DPAD_UP);
-		padbuttons = tern_insert_int(padbuttons, ".down", DPAD_DOWN);
-		padbuttons = tern_insert_int(padbuttons, ".left", DPAD_LEFT);
-		padbuttons = tern_insert_int(padbuttons, ".right", DPAD_RIGHT);
-		padbuttons = tern_insert_int(padbuttons, ".a", BUTTON_A);
-		padbuttons = tern_insert_int(padbuttons, ".b", BUTTON_B);
-		padbuttons = tern_insert_int(padbuttons, ".c", BUTTON_C);
-		padbuttons = tern_insert_int(padbuttons, ".x", BUTTON_X);
-		padbuttons = tern_insert_int(padbuttons, ".y", BUTTON_Y);
-		padbuttons = tern_insert_int(padbuttons, ".z", BUTTON_Z);
-		padbuttons = tern_insert_int(padbuttons, ".start", BUTTON_START);
-		padbuttons = tern_insert_int(padbuttons, ".mode", BUTTON_MODE);
-	}
-	return padbuttons;
-}
-
-static tern_node *get_mouse_buttons()
-{
-	static tern_node *mousebuttons;
-	if (!mousebuttons) {
-		mousebuttons = tern_insert_int(NULL, ".left", MOUSE_LEFT);
-		mousebuttons = tern_insert_int(mousebuttons, ".middle", MOUSE_MIDDLE);
-		mousebuttons = tern_insert_int(mousebuttons, ".right", MOUSE_RIGHT);
-		mousebuttons = tern_insert_int(mousebuttons, ".start", MOUSE_START);
-		mousebuttons = tern_insert_int(mousebuttons, ".motion", PSEUDO_BUTTON_MOTION);
-	}
-	return mousebuttons;
-}
-
-void handle_joy_added(int joystick)
-{
-	if (joystick > MAX_JOYSTICKS) {
-		return;
-	}
-	tern_node * pads = tern_find_path(config, "bindings\0pads\0", TVAL_NODE).ptrval;
-	if (pads) {
-		char numstr[11];
-		sprintf(numstr, "%d", joystick);
-		tern_node * pad = tern_find_node(pads, numstr);
-		if (pad) {
-			tern_node * dpad_node = tern_find_node(pad, "dpads");
-			if (dpad_node) {
-				for (int dpad = 0; dpad < 10; dpad++)
-				{
-					numstr[0] = dpad + '0';
-					numstr[1] = 0;
-					tern_node * pad_dpad = tern_find_node(dpad_node, numstr);
-					char * dirs[] = {"up", "down", "left", "right"};
-					int dirnums[] = {RENDER_DPAD_UP, RENDER_DPAD_DOWN, RENDER_DPAD_LEFT, RENDER_DPAD_RIGHT};
-					for (int dir = 0; dir < sizeof(dirs)/sizeof(dirs[0]); dir++) {
-						char * target = tern_find_ptr(pad_dpad, dirs[dir]);
-						if (target) {
-							int ui_func, padnum, button;
-							int bindtype = parse_binding_target(target, get_pad_buttons(), get_mouse_buttons(), &ui_func, &padnum, &button);
-							if (bindtype == BIND_GAMEPAD1) {
-								bind_dpad_gamepad(joystick, dpad, dirnums[dir], padnum, button);
-							} else if (bindtype == BIND_UI) {
-								bind_dpad_ui(joystick, dpad, dirnums[dir], ui_func, button);
-							}
-						}
-					}
-				}
-			}
-			tern_node *button_node = tern_find_node(pad, "buttons");
-			if (button_node) {
-				pad_button_state state = {
-					.padnum = joystick,
-					.padbuttons = get_pad_buttons(),
-					.mousebuttons = get_mouse_buttons()
-				};
-				tern_foreach(button_node, process_pad_button, &state);
-			}
-			tern_node *axes_node = tern_find_node(pad, "axes");
-			if (axes_node) {
-				pad_button_state state = {
-					.padnum = joystick,
-					.padbuttons = get_pad_buttons(),
-					.mousebuttons = get_mouse_buttons()
-				};
-				tern_foreach(axes_node, process_pad_axis, &state);
-			}
-			if (current_io) {
-				if (joysticks[joystick].buttons) {
-					map_bindings(current_io->ports, joysticks[joystick].buttons, joysticks[joystick].num_buttons);
-				}
-				if (joysticks[joystick].dpads)
-				{
-					for (uint32_t i = 0; i < joysticks[joystick].num_dpads; i++)
-					{
-						map_bindings(current_io->ports, joysticks[joystick].dpads[i].bindings, 4);
-					}
-				}
-				if (joysticks[joystick].axes) {
-					for (uint32_t i = 0; i < joysticks[joystick].num_axes; i++)
-					{
-						map_bindings(current_io->ports, &joysticks[joystick].axes[i].positive, 1);
-						map_bindings(current_io->ports, &joysticks[joystick].axes[i].negative, 1);
-					}
-				}
-			}
-		}
-	}
-	
-}
-
-void set_keybindings(sega_io *io)
-{
-	static uint8_t already_done;
-	if (already_done) {
-		map_all_bindings(io);
-		return;
-	}
-	already_done = 1;
-	io_port *ports = io->ports;
-	tern_node * special = tern_insert_int(NULL, "up", RENDERKEY_UP);
-	special = tern_insert_int(special, "down", RENDERKEY_DOWN);
-	special = tern_insert_int(special, "left", RENDERKEY_LEFT);
-	special = tern_insert_int(special, "right", RENDERKEY_RIGHT);
-	special = tern_insert_int(special, "enter", '\r');
-	special = tern_insert_int(special, "space", ' ');
-	special = tern_insert_int(special, "tab", '\t');
-	special = tern_insert_int(special, "backspace", '\b');
-	special = tern_insert_int(special, "esc", RENDERKEY_ESC);
-	special = tern_insert_int(special, "delete", RENDERKEY_DEL);
-	special = tern_insert_int(special, "lshift", RENDERKEY_LSHIFT);
-	special = tern_insert_int(special, "rshift", RENDERKEY_RSHIFT);
-	special = tern_insert_int(special, "lctrl", RENDERKEY_LCTRL);
-	special = tern_insert_int(special, "rctrl", RENDERKEY_RCTRL);
-	special = tern_insert_int(special, "lalt", RENDERKEY_LALT);
-	special = tern_insert_int(special, "ralt", RENDERKEY_RALT);
-	special = tern_insert_int(special, "home", RENDERKEY_HOME);
-	special = tern_insert_int(special, "end", RENDERKEY_END);
-	special = tern_insert_int(special, "pageup", RENDERKEY_PAGEUP);
-	special = tern_insert_int(special, "pagedown", RENDERKEY_PAGEDOWN);
-	special = tern_insert_int(special, "f1", RENDERKEY_F1);
-	special = tern_insert_int(special, "f2", RENDERKEY_F2);
-	special = tern_insert_int(special, "f3", RENDERKEY_F3);
-	special = tern_insert_int(special, "f4", RENDERKEY_F4);
-	special = tern_insert_int(special, "f5", RENDERKEY_F5);
-	special = tern_insert_int(special, "f6", RENDERKEY_F6);
-	special = tern_insert_int(special, "f7", RENDERKEY_F7);
-	special = tern_insert_int(special, "f8", RENDERKEY_F8);
-	special = tern_insert_int(special, "f9", RENDERKEY_F9);
-	special = tern_insert_int(special, "f10", RENDERKEY_F10);
-	special = tern_insert_int(special, "f11", RENDERKEY_F11);
-	special = tern_insert_int(special, "f12", RENDERKEY_F12);
-	special = tern_insert_int(special, "select", RENDERKEY_SELECT);
-	special = tern_insert_int(special, "play", RENDERKEY_PLAY);
-	special = tern_insert_int(special, "search", RENDERKEY_SEARCH);
-	special = tern_insert_int(special, "back", RENDERKEY_BACK);
-
-	tern_node *padbuttons = get_pad_buttons();
-
-	tern_node *mousebuttons = get_mouse_buttons();
-	
-	tern_node * keys = tern_find_path(config, "bindings\0keys\0", TVAL_NODE).ptrval;
-	process_keys(keys, special, padbuttons, mousebuttons, NULL);
-	char numstr[] = "00";
-	tern_node * pads = tern_find_path(config, "bindings\0pads\0", TVAL_NODE).ptrval;
-	if (pads) {
-		for (int i = 0; i < MAX_JOYSTICKS; i++)
-		{
-
-			if (i < 10) {
-				numstr[0] = i + '0';
-				numstr[1] = 0;
-			} else {
-				numstr[0] = i/10 + '0';
-				numstr[1] = i%10 + '0';
-			}
-			
-		}
-	}
-	memset(mice, 0, sizeof(mice));
-	tern_node * mice = tern_find_path(config, "bindings\0mice\0", TVAL_NODE).ptrval;
-	if (mice) {
-		tern_node *buttonmaps[2] = {padbuttons, mousebuttons};
-		tern_foreach(mice, process_mouse, buttonmaps);
-	}
-	tern_node * speed_nodes = tern_find_path(config, "clocks\0speeds\0", TVAL_NODE).ptrval;
-	speeds = malloc(sizeof(uint32_t));
-	speeds[0] = 100;
-	process_speeds(speed_nodes, NULL);
-	for (int i = 0; i < num_speeds; i++)
-	{
-		if (!speeds[i]) {
-			warning("Speed index %d was not set to a valid percentage!", i);
-			speeds[i] = 100;
-		}
-	}
-	map_all_bindings(io);
-}
-
-void map_all_bindings(sega_io *io)
-{
-	current_io = io;
-	io_port *ports = io->ports;
-	
-	for (int bucket = 0; bucket < 0x10000; bucket++)
-	{
-		if (bindings[bucket])
-		{
-			map_bindings(ports, bindings[bucket], 0x8000);
-		}
-	}
-	for (int stick = 0; stick < MAX_JOYSTICKS; stick++)
-	{
-		if (joysticks[stick].buttons) {
-			map_bindings(ports, joysticks[stick].buttons, joysticks[stick].num_buttons);
-		}
-		if (joysticks[stick].dpads)
-		{
-			for (uint32_t i = 0; i < joysticks[stick].num_dpads; i++)
-			{
-				map_bindings(ports, joysticks[stick].dpads[i].bindings, 4);
-			}
-		}
-		for (uint32_t i = 0; i < joysticks[stick].num_axes; i++)
-		{
-			map_bindings(current_io->ports, &joysticks[stick].axes[i].positive, 1);
-			map_bindings(current_io->ports, &joysticks[stick].axes[i].negative, 1);
-		}
-	}
-	for (int mouse = 0; mouse < MAX_MICE; mouse++)
-	{
-		if (mice[mouse].bind_type >= BIND_MOUSE1 && mice[mouse].bind_type <= BIND_MOUSE8) {
-			int num = mice[mouse].bind_type - BIND_MOUSE1;
-			for (int j = 0; j < 3; j++)
-			{
-				if (ports[j].device_type == IO_MOUSE && ports[j].device.mouse.mouse_num == num)
-				{
-					memset(ports[j].input, 0, sizeof(ports[j].input));
-					mice[mouse].motion_port = ports + j;
-					break;
-				}
-			}
-		}
-		map_bindings(ports, mice[mouse].buttons, MAX_MOUSE_BUTTONS);
-	}
-	keyboard_port = NULL;
-	for (int i = 0; i < 3; i++)
-	{
-		if (ports[i].device_type == IO_SATURN_KEYBOARD || ports[i].device_type == IO_XBAND_KEYBOARD) {
-			keyboard_port = ports + i;
-			break;
-		}
-	}
-	//not really related to the intention of this function, but the best place to do this currently
-	if (speeds[0] != 100) {
-		current_system->set_speed_percent(current_system, speeds[0]);
-	}
-}
 
 #define TH 0x40
 #define TR 0x20
@@ -1509,6 +412,7 @@
 		if (port->device.mouse.tr_counter == 3) {
 			port->device.mouse.latched_x = port->device.mouse.cur_x;
 			port->device.mouse.latched_y = port->device.mouse.cur_y;
+			/* FIXME mouse mode owned by bindings now
 			if (current_io->mouse_mode == MOUSE_ABSOLUTE) {
 				//avoid overflow in absolute mode
 				int deltax = port->device.mouse.latched_x - port->device.mouse.last_read_x;
@@ -1519,7 +423,7 @@
 				if (abs(deltay) > 255) {
 					port->device.mouse.latched_y = port->device.mouse.last_read_y + (deltay > 0 ? 255 : -255);
 				}
-			}
+			}*/
 		}
 	}
 }
--- a/io.h	Sun Dec 31 10:11:16 2017 -0800
+++ b/io.h	Tue Dec 25 11:12:26 2018 -0800
@@ -70,48 +70,61 @@
 
 typedef struct {
 	io_port	ports[3];
-	uint8_t mouse_mode;
-	uint8_t mouse_captured;
-	uint8_t keyboard_captured;
 } sega_io;
 
-#define GAMEPAD_TH0 0
-#define GAMEPAD_TH1 1
-#define GAMEPAD_EXTRA 2
-#define GAMEPAD_NONE 0xF
-
-#define IO_TH0 0
-#define IO_TH1 1
-#define IO_STATE 2
+//pseudo gamepad for buttons on main console unit
+#define GAMEPAD_MAIN_UNIT 255
 
 enum {
-	IO_WRITE_PENDING,
-	IO_WRITTEN,
-	IO_READ_PENDING,
-	IO_READ
+	BUTTON_INVALID,
+	DPAD_UP,
+	DPAD_DOWN,
+	DPAD_LEFT,
+	DPAD_RIGHT,
+	BUTTON_A,
+	BUTTON_B,
+	BUTTON_C,
+	BUTTON_START,
+	BUTTON_X,
+	BUTTON_Y,
+	BUTTON_Z,
+	BUTTON_MODE,
+	NUM_GAMEPAD_BUTTONS
 };
 
-typedef struct genesis_context genesis_context;
+enum {
+	MAIN_UNIT_PAUSE
+};
 
-void set_keybindings(sega_io *io);
-void map_all_bindings(sega_io *io);
+enum {
+	MOUSE_LEFT = 1,
+	MOUSE_RIGHT = 2,
+	MOUSE_MIDDLE = 4,
+	MOUSE_START = 8,
+	PSEUDO_BUTTON_MOTION=0xFF
+};
+
 void setup_io_devices(tern_node * config, rom_info *rom, sega_io *io);
 void io_adjust_cycles(io_port * pad, uint32_t current_cycle, uint32_t deduction);
 void io_control_write(io_port *port, uint8_t value, uint32_t current_cycle);
 void io_data_write(io_port * pad, uint8_t value, uint32_t current_cycle);
 uint8_t io_data_read(io_port * pad, uint32_t current_cycle);
-void handle_keydown(int keycode, uint8_t scancode);
-void handle_keyup(int keycode, uint8_t scancode);
-void handle_joydown(int joystick, int button);
-void handle_joyup(int joystick, int button);
-void handle_joy_dpad(int joystick, int dpad, uint8_t state);
-void handle_joy_axis(int joystick, int axis, int16_t value);
-void handle_joy_added(int joystick);
-void handle_mouse_moved(int mouse, uint16_t x, uint16_t y, int16_t deltax, int16_t deltay);
-void handle_mousedown(int mouse, int button);
-void handle_mouseup(int mouse, int button);
 void io_serialize(io_port *port, serialize_buffer *buf);
 void io_deserialize(deserialize_buffer *buf, void *vport);
 
+void io_port_gamepad_down(io_port *port, uint8_t button);
+void io_port_gamepad_up(io_port *port, uint8_t button);
+void io_gamepad_down(sega_io *io, uint8_t gamepad_num, uint8_t button);
+void io_gamepad_up(sega_io *io, uint8_t gamepad_num, uint8_t button);
+void io_mouse_down(sega_io *io, uint8_t mouse_num, uint8_t button);
+void io_mouse_up(sega_io *io, uint8_t mouse_num, uint8_t button);
+void io_mouse_motion_absolute(sega_io *io, uint8_t mouse_num, uint16_t x, uint16_t y);
+void io_mouse_motion_relative(sega_io *io, uint8_t mouse_num, int32_t x, int32_t y);
+void io_keyboard_down(sega_io *io, uint8_t scancode);
+void io_keyboard_up(sega_io *io, uint8_t scancode);
+uint8_t io_has_keyboard(sega_io *io);
+
+extern const char * device_type_names[];
+
 #endif //IO_H_
 
--- a/jagcpu_x86.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/jagcpu_x86.c	Tue Dec 25 11:12:26 2018 -0800
@@ -102,7 +102,7 @@
 	code_ptr no_delay = code-.cur + 1;
 	jcc(code, CC_NZ, no_delay + 1);
 	ccylces(code, 1);
-	*no_delay = code->cur - (no_delay = 1);
+	*no_delay = code->cur - (no_delay + 1);
 	*no_result = code->cur - (no_result + 1);
 	mov_rr(code, opts->resultreg, opts->writeback, SZ_B);
 }
@@ -122,7 +122,7 @@
 
 uint16_t *translate_jag_inst(uint16_t *stream, jag_cpu_options *opts, uint32_t address)
 {
-	uint16_t inst = *stream
+	uint16_t inst = *stream;
 	++stream;
 	uint16_t opcode = jag_opcode(inst, opts->is_gpu);
 	check_cycles_int(&opts->gen, address);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jcart.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,87 @@
+#include "genesis.h"
+
+static io_port *get_ports(m68k_context *m68k)
+{
+	genesis_context *gen = m68k->system;
+	if (!gen->extra) {
+		io_port *ports = calloc(2, sizeof(io_port));
+		ports[0].device_type = IO_GAMEPAD3;
+		ports[0].device.pad.gamepad_num = 3;
+		ports[1].device_type = IO_GAMEPAD3;
+		ports[1].device.pad.gamepad_num = 4;
+		io_control_write(ports, 0x40, 0);
+		io_control_write(ports + 1, 0x40, 0);
+		gen->extra = ports;
+	}
+		
+	return gen->extra;
+}
+
+void *jcart_write_w(uint32_t address, void *context, uint16_t value)
+{
+	m68k_context *m68k= context;
+	io_port *ports = get_ports(m68k);
+	value = value << 6 & 0x40;
+	io_data_write(ports, value, m68k->current_cycle);
+	io_data_write(ports + 1, value, m68k->current_cycle);
+	return context;
+}
+
+void *jcart_write_b(uint32_t address, void *context, uint8_t value)
+{
+	if (address & 1) {
+		return jcart_write_w(address, context, value);
+	}
+	return context;
+}
+
+uint16_t jcart_read_w(uint32_t address, void *context)
+{
+	m68k_context *m68k= context;
+	io_port *ports = get_ports(m68k);
+	//according to Eke, bit 14 is forced low, at least on the Micro Machines 2 cart
+	//TODO: Test behavior of actual cart
+	uint16_t value = io_data_read(ports, m68k->current_cycle) << 8;
+	value |= io_data_read(ports + 1, m68k->current_cycle);
+	return value;
+}
+
+uint8_t jcart_read_b(uint32_t address, void *context)
+{
+	m68k_context *m68k= context;
+	io_port *ports = get_ports(m68k);
+	return io_data_read(ports + (address & 1), m68k->current_cycle);
+}
+
+void jcart_adjust_cycles(genesis_context *context, uint32_t deduction)
+{
+	io_port *ports = get_ports(context->m68k);
+	io_adjust_cycles(ports, context->m68k->current_cycle, deduction);
+	io_adjust_cycles(ports + 1, context->m68k->current_cycle, deduction);
+}
+
+void jcart_gamepad_down(genesis_context *context, uint8_t gamepad_num, uint8_t button)
+{
+	io_port *ports = get_ports(context->m68k);
+	if (gamepad_num == ports[1].device.pad.gamepad_num) {
+		ports++;
+	} else if (gamepad_num != ports[0].device.pad.gamepad_num) {
+		ports = NULL;
+	}
+	if (ports) {
+		io_port_gamepad_down(ports, button);
+	}
+}
+
+void jcart_gamepad_up(genesis_context *context, uint8_t gamepad_num, uint8_t button)
+{
+	io_port *ports = get_ports(context->m68k);
+	if (gamepad_num == ports[1].device.pad.gamepad_num) {
+		ports++;
+	} else if (gamepad_num != ports[0].device.pad.gamepad_num) {
+		ports = NULL;
+	}
+	if (ports) {
+		io_port_gamepad_up(ports, button);
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jcart.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,12 @@
+#ifndef JCART_H_
+#define JCART_H_
+
+void *jcart_write_w(uint32_t address, void *context, uint16_t value);
+void *jcart_write_b(uint32_t address, void *context, uint8_t value);
+uint16_t jcart_read_w(uint32_t address, void *context);
+uint8_t jcart_read_b(uint32_t address, void *context);
+void jcart_adjust_cycles(genesis_context *context, uint32_t deduction);
+void jcart_gamepad_down(genesis_context *context, uint8_t gamepad_num, uint8_t button);
+void jcart_gamepad_up(genesis_context *context, uint8_t gamepad_num, uint8_t button);
+
+#endif //JCART_H_
--- a/m68k_core.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/m68k_core.c	Tue Dec 25 11:12:26 2018 -0800
@@ -475,7 +475,11 @@
 		}
 	}
 	if (opts->num_movem == opts->movem_storage) {
-		opts->movem_storage *= 2;
+		if (!opts->movem_storage) {
+			opts->movem_storage = 4;
+		} else {
+			opts->movem_storage *= 2;
+		}
 		opts->big_movem = realloc(opts->big_movem, sizeof(movem_fun) * opts->movem_storage);
 	}
 	if (!opts->extra_code.cur) {
@@ -919,7 +923,7 @@
 	RAW_IMPL(M68K_MOVE_USP, translate_m68k_move_usp),
 	RAW_IMPL(M68K_LEA, translate_m68k_lea_pea),
 	RAW_IMPL(M68K_PEA, translate_m68k_lea_pea),
-	RAW_IMPL(M68K_CLR, translate_m68k_clr),
+	UNARY_IMPL(M68K_CLR, N0|V0|C0|Z1),
 	OP_IMPL(M68K_EXG, translate_m68k_exg),
 	RAW_IMPL(M68K_SCC, translate_m68k_scc),
 
@@ -1188,7 +1192,6 @@
 void start_68k_context(m68k_context * context, uint32_t address)
 {
 	m68k_options * options = context->options;
-	context->should_return = 0;
 #ifdef USE_NATIVE
 	code_ptr addr = get_native_address_trans(context, address);
 	options->start_context(addr, context);
@@ -1235,8 +1238,21 @@
 void m68k_options_free(m68k_options *opts)
 {
 #ifdef USE_NATIVE
+	for (uint32_t address = 0; address < opts->gen.address_mask; address += NATIVE_CHUNK_SIZE)
+	{
+		uint32_t chunk = address / NATIVE_CHUNK_SIZE;
+		if (opts->gen.native_code_map[chunk].base) {
+			free(opts->gen.native_code_map[chunk].offsets);
+		}
+	}
 	free(opts->gen.native_code_map);
+	uint32_t ram_inst_slots = ram_size(&opts->gen) / 1024;
+	for (uint32_t i = 0; i < ram_inst_slots; i++)
+	{
+		free(opts->gen.ram_inst_sizes[i]);
+	}
 	free(opts->gen.ram_inst_sizes);
+	free(opts->big_movem);
 #endif
 	free(opts);
 }
--- a/m68k_core_x86.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/m68k_core_x86.c	Tue Dec 25 11:12:26 2018 -0800
@@ -742,27 +742,6 @@
 	cycles(&opts->gen, BUS);
 }
 
-void translate_m68k_clr(m68k_options * opts, m68kinst * inst)
-{
-	code_info *code = &opts->gen.code;
-	update_flags(opts, N0|V0|C0|Z1);
-	int8_t reg = native_reg(&(inst->dst), opts);
-	if (reg >= 0) {
-		cycles(&opts->gen, (inst->extra.size == OPSIZE_LONG ? 6 : 4));
-		xor_rr(code, reg, reg, inst->extra.size);
-		return;
-	}
-	host_ea dst_op;
-	//TODO: fix timing
-	translate_m68k_op(inst, &dst_op, opts, 1);
-	if (dst_op.mode == MODE_REG_DIRECT) {
-		xor_rr(code, dst_op.base, dst_op.base, inst->extra.size);
-	} else {
-		mov_irdisp(code, 0, dst_op.base, dst_op.disp, inst->extra.size);
-	}
-	m68k_save_result(inst, opts);
-}
-
 void translate_m68k_ext(m68k_options * opts, m68kinst * inst)
 {
 	code_info *code = &opts->gen.code;
@@ -780,6 +759,7 @@
 	}
 	inst->extra.size = dst_size;
 	update_flags(opts, N|V0|C0|Z);
+	cycles(&opts->gen, BUS);
 	//M68K EXT only operates on registers so no need for a call to save result here
 }
 
@@ -1210,7 +1190,8 @@
 void translate_m68k_reset(m68k_options *opts, m68kinst *inst)
 {
 	code_info *code = &opts->gen.code;
-	cycles(&opts->gen, BUS);
+	//RESET instructions take a long time to give peripherals time to reset themselves
+	cycles(&opts->gen, 132);
 	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, reset_handler), opts->gen.scratch1, SZ_PTR);
 	cmp_ir(code, 0, opts->gen.scratch1, SZ_PTR);
 	code_ptr no_reset_handler = code->cur + 1;
@@ -1437,6 +1418,7 @@
 {
 	switch(inst->op)
 	{
+	case M68K_CLR:   xor_rr(code, dst, dst, size); break;
 	case M68K_NEG:   neg_r(code, dst, size); break;
 	case M68K_NOT:   not_r(code, dst, size); cmp_ir(code, 0, dst, size); break;
 	case M68K_ROL:   rol_clr(code, dst, size); break;
@@ -1452,6 +1434,7 @@
 {
 	switch(inst->op)
 	{
+	case M68K_CLR:   mov_irdisp(code, 0, dst, disp, size); break;
 	case M68K_NEG:   neg_rdisp(code, dst, disp, size); break;
 	case M68K_NOT:   not_rdisp(code, dst, disp, size); cmp_irdisp(code, 0, dst, disp, size); break;
 	case M68K_ROL:   rol_clrdisp(code, dst, disp, size); break;
@@ -1466,7 +1449,11 @@
 void translate_m68k_unary(m68k_options *opts, m68kinst *inst, uint32_t flag_mask, host_ea *dst_op)
 {
 	code_info *code = &opts->gen.code;
-	cycles(&opts->gen, BUS);
+	uint32_t num_cycles = BUS;
+	if (inst->extra.size == OPSIZE_LONG && (inst->dst.addr_mode == MODE_REG || inst->dst.addr_mode == MODE_AREG)) {
+		num_cycles += 2;
+	}
+	cycles(&opts->gen, num_cycles);
 	if (dst_op->mode == MODE_REG_DIRECT) {
 		op_r(code, inst, dst_op->base, inst->extra.size);
 	} else {
@@ -1508,6 +1495,8 @@
 		//destination is in memory so we need to preserve scratch2 for the write at the end
 		push_r(code, opts->gen.scratch2);
 	}
+	//MC68000 User's Manual suggests NBCD hides the 2 cycle penalty during the write cycle somehow
+	cycles(&opts->gen, inst->op == M68K_NBCD && inst->dst.addr_mode != MODE_REG_DIRECT ? BUS : BUS + 2);
 	uint8_t other_reg;
 	//WARNING: This may need adjustment if register assignments change
 	if (opts->gen.scratch2 > RBX) {
@@ -2365,7 +2354,7 @@
 	}
 	code_ptr loop_top = code->cur;
 		call(code, opts->do_sync);
-		cmp_rr(code, opts->gen.limit, opts->gen.cycles, SZ_D);
+		cmp_rr(code, opts->gen.cycles, opts->gen.limit, SZ_D);
 		code_ptr normal_cycle_up = code->cur + 1;
 		jcc(code, CC_A, code->cur + 2);
 			cycles(&opts->gen, BUS);
@@ -2454,6 +2443,7 @@
 void translate_m68k_move_from_sr(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op)
 {
 	code_info *code = &opts->gen.code;
+	cycles(&opts->gen, inst->dst.addr_mode == MODE_REG_DIRECT ? BUS+2 : BUS);
 	call(code, opts->get_sr);
 	if (dst_op->mode == MODE_REG_DIRECT) {
 		mov_rr(code, opts->gen.scratch1, dst_op->base, SZ_W);
--- a/m68k_internal.h	Sun Dec 31 10:11:16 2017 -0800
+++ b/m68k_internal.h	Tue Dec 25 11:12:26 2018 -0800
@@ -66,7 +66,6 @@
 void translate_m68k_unary(m68k_options *opts, m68kinst *inst, uint32_t flag_mask, host_ea *dst_op);
 void translate_m68k_cmp(m68k_options * opts, m68kinst * inst);
 void translate_m68k_tas(m68k_options * opts, m68kinst * inst);
-void translate_m68k_clr(m68k_options * opts, m68kinst * inst);
 void translate_m68k_ext(m68k_options * opts, m68kinst * inst);
 void translate_m68k_abcd_sbcd(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op);
 void translate_m68k_sl(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/megawifi.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,439 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#ifdef _WIN32
+#define WINVER 0x501
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include "genesis.h"
+#include "net.h"
+
+enum {
+	TX_IDLE,
+	TX_LEN1,
+	TX_LEN2,
+	TX_PAYLOAD,
+	TX_WAIT_ETX
+};
+#define STX 0x7E
+#define ETX 0x7E
+#define MAX_RECV_SIZE 1440
+
+#define E(N) N
+enum {
+#include "mw_commands.c"
+	CMD_ERROR = 255
+};
+#undef E
+#define E(N) #N
+static const char *cmd_names[] = {
+#include "mw_commands.c"
+	[255] = "CMD_ERROR"
+};
+
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+
+enum {
+	STATE_IDLE=1,
+	STATE_AP_JOIN,
+	STATE_SCAN,
+	STATE_READY,
+	STATE_TRANSPARENT
+};
+
+#define FLAG_ONLINE 
+
+typedef struct {
+	uint32_t transmit_bytes;
+	uint32_t expected_bytes;
+	uint32_t receive_bytes;
+	uint32_t receive_read;
+	int      sock_fds[15];
+	uint16_t channel_flags;
+	uint8_t  channel_state[15];
+	uint8_t  scratchpad;
+	uint8_t  transmit_channel;
+	uint8_t  transmit_state;
+	uint8_t  module_state;
+	uint8_t  flags;
+	uint8_t  transmit_buffer[4096];
+	uint8_t  receive_buffer[4096];
+} megawifi;
+
+static megawifi *get_megawifi(void *context)
+{
+	m68k_context *m68k = context;
+	genesis_context *gen = m68k->system;
+	if (!gen->extra) {
+		gen->extra = calloc(1, sizeof(megawifi));
+		megawifi *mw = gen->extra;
+		mw->module_state = STATE_IDLE;
+		for (int i = 0; i < 15; i++)
+		{
+			mw->sock_fds[i] = -1;
+		}
+	}
+	return gen->extra;
+}
+
+static void mw_putc(megawifi *mw, uint8_t v)
+{
+	if (mw->receive_bytes == sizeof(mw->receive_buffer)) {
+		return;
+	}
+	mw->receive_buffer[mw->receive_bytes++] = v;
+}
+
+static void mw_set(megawifi *mw, uint8_t val, uint32_t count)
+{
+	if (count + mw->receive_bytes > sizeof(mw->receive_buffer)) {
+		count = sizeof(mw->receive_buffer) - mw->receive_bytes;
+	}
+	memset(mw->receive_buffer + mw->receive_bytes, val, count);
+	mw->receive_bytes += count;
+}
+
+static void mw_copy(megawifi *mw, const uint8_t *src, uint32_t count)
+{
+	if (count + mw->receive_bytes > sizeof(mw->receive_buffer)) {
+		count = sizeof(mw->receive_buffer) - mw->receive_bytes;
+	}
+	memcpy(mw->receive_buffer + mw->receive_bytes, src, count);
+	mw->receive_bytes += count;
+}
+
+static void mw_puts(megawifi *mw, char *s)
+{
+	uint32_t len = strlen(s);
+	if ((mw->receive_bytes + len) > sizeof(mw->receive_buffer)) {
+		return;
+	}
+	memcpy(mw->receive_buffer + mw->receive_bytes, s, len);
+	mw->receive_bytes += len;
+}
+
+static void poll_socket(megawifi *mw, uint8_t channel)
+{
+	if (mw->sock_fds[channel] < 0) {
+		return;
+	}
+	if (mw->channel_state[channel] == 1) {
+		int res = accept(mw->sock_fds[channel], NULL, NULL);
+		if (res >= 0) {
+			close(mw->sock_fds[channel]);
+#ifndef _WIN32
+//FIXME: Set nonblocking on Windows too
+			fcntl(res, F_SETFL, O_NONBLOCK);
+#endif
+			mw->sock_fds[channel] = res;
+			mw->channel_state[channel] = 2;
+			mw->channel_flags |= 1 << (channel + 1);
+		} else if (errno != EAGAIN && errno != EWOULDBLOCK) {
+			close(mw->sock_fds[channel]);
+			mw->channel_state[channel] = 0;
+			mw->channel_flags |= 1 << (channel + 1);
+		}
+	} else if (mw->channel_state[channel] == 2 && mw->receive_bytes < sizeof(mw->receive_buffer) - 4) {
+		size_t max = sizeof(mw->receive_buffer) - 4 - mw->receive_bytes;
+		if (max > MAX_RECV_SIZE) {
+			max = MAX_RECV_SIZE;
+		}
+		int bytes = recv(mw->sock_fds[channel], mw->receive_buffer + mw->receive_bytes + 3, max, 0);
+		if (bytes > 0) {
+			mw_putc(mw, STX);
+			mw_putc(mw, bytes >> 8 | (channel+1) << 4);
+			mw_putc(mw, bytes);
+			mw->receive_bytes += bytes;
+			mw_putc(mw, ETX);
+			//should this set the channel flag?
+		} else if (bytes < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
+			close(mw->sock_fds[channel]);
+			mw->channel_state[channel] = 0;
+			mw->channel_flags |= 1 << (channel + 1);
+		}
+	}
+}
+
+static void poll_all_sockets(megawifi *mw)
+{
+	for (int i = 0; i < 15; i++)
+	{
+		poll_socket(mw, i);
+	}
+}
+
+static void start_reply(megawifi *mw, uint8_t cmd)
+{
+	mw_putc(mw, STX);
+	//reserve space for length
+	mw->receive_bytes += 2;
+	//cmd
+	mw_putc(mw, 0);
+	mw_putc(mw, cmd);
+	//reserve space for length
+	mw->receive_bytes += 2;
+}
+
+static void end_reply(megawifi *mw)
+{
+	uint32_t len = mw->receive_bytes - 3;
+	//LSD packet length
+	mw->receive_buffer[1] = len >> 8;
+	mw->receive_buffer[2] = len;
+	//command length
+	len -= 4;
+	mw->receive_buffer[5] = len >> 8;
+	mw->receive_buffer[6] = len;
+	mw_putc(mw, ETX);
+}
+
+static void process_packet(megawifi *mw)
+{
+	if (mw->transmit_channel == 0) {
+		uint32_t command = mw->transmit_buffer[0] << 8 | mw->transmit_buffer[1];
+		uint32_t size = mw->transmit_buffer[2] << 8 | mw->transmit_buffer[3];
+		if (size > mw->transmit_bytes - 4) {
+			size = mw->transmit_bytes - 4;
+		}
+		int orig_receive_bytes = mw->receive_bytes;
+		switch (command)
+		{
+		case CMD_VERSION:
+			start_reply(mw, CMD_OK);
+			mw_putc(mw, 1);
+			mw_putc(mw, 0);
+			mw_puts(mw, "blastem");
+			end_reply(mw);
+			break;
+		case CMD_ECHO:
+			mw->receive_bytes = mw->transmit_bytes;
+			memcpy(mw->receive_buffer, mw->transmit_buffer, mw->transmit_bytes);
+			break;
+		case CMD_IP_CURRENT: {
+			iface_info i;
+			if (get_host_address(&i)) {
+				start_reply(mw, CMD_OK);
+				//config number and reserved bytes
+				mw_set(mw, 0, 4);
+				//ip
+				mw_copy(mw, i.ip, sizeof(i.ip));
+				//net mask
+				mw_copy(mw, i.net_mask, sizeof(i.net_mask));
+				//gateway guess
+				mw_putc(mw, i.ip[0] & i.net_mask[0]);
+				mw_putc(mw, i.ip[1] & i.net_mask[1]);
+				mw_putc(mw, i.ip[2] & i.net_mask[2]);
+				mw_putc(mw, (i.ip[3] & i.net_mask[3]) + 1);
+				//dns
+				static const uint8_t localhost[] = {127,0,0,1};
+				mw_copy(mw, localhost, sizeof(localhost));
+				mw_copy(mw, localhost, sizeof(localhost));
+				
+			} else {
+				start_reply(mw, CMD_ERROR);
+			}
+			end_reply(mw);
+			break;
+		}
+		case CMD_AP_JOIN:
+			mw->module_state = STATE_READY;
+			start_reply(mw, CMD_OK);
+			end_reply(mw);
+			break;
+		case CMD_TCP_BIND:{
+			if (size < 7){
+				start_reply(mw, CMD_ERROR);
+				end_reply(mw);
+				break;
+			}
+			uint8_t channel = mw->transmit_buffer[10];
+			if (!channel || channel > 15) {
+				start_reply(mw, CMD_ERROR);
+				end_reply(mw);
+				break;
+			}
+			channel--;
+			if (mw->sock_fds[channel] >= 0) {
+				close(mw->sock_fds[channel]);
+			}
+			mw->sock_fds[channel] = socket(AF_INET, SOCK_STREAM, 0);
+			if (mw->sock_fds[channel] < 0) {
+				start_reply(mw, CMD_ERROR);
+				end_reply(mw);
+				break;
+			}
+			int value = 1;
+			setsockopt(mw->sock_fds[channel], SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
+			struct sockaddr_in bind_addr;
+			memset(&bind_addr, 0, sizeof(bind_addr));
+			bind_addr.sin_family = AF_INET;
+			bind_addr.sin_port = htons(mw->transmit_buffer[8] << 8 | mw->transmit_buffer[9]);
+			if (bind(mw->sock_fds[channel], (struct sockaddr *)&bind_addr, sizeof(bind_addr)) != 0) {
+				close(mw->sock_fds[channel]);
+				mw->sock_fds[channel] = -1;
+				start_reply(mw, CMD_ERROR);
+				end_reply(mw);
+				break;
+			}
+			int res = listen(mw->sock_fds[channel], 2);
+			start_reply(mw, res ? CMD_ERROR : CMD_OK);
+			if (res) {
+				close(mw->sock_fds[channel]);
+				mw->sock_fds[channel] = -1;
+			} else {
+				mw->channel_flags |= 1 << (channel + 1);
+				mw->channel_state[channel] = 1;
+#ifndef _WIN32
+//FIXME: Set nonblocking on Windows too
+				fcntl(mw->sock_fds[channel], F_SETFL, O_NONBLOCK);
+#endif
+			}
+			end_reply(mw);
+			break;
+		}
+		case CMD_SOCK_STAT: {
+			uint8_t channel = mw->transmit_buffer[4];
+			if (!channel || channel > 15) {
+				start_reply(mw, CMD_ERROR);
+				end_reply(mw);
+				break;
+			}
+			mw->channel_flags &= ~(1 << channel);
+			channel--;
+			poll_socket(mw, channel);
+			start_reply(mw, CMD_OK);
+			mw_putc(mw, mw->channel_state[channel]);
+			end_reply(mw);
+			break;
+		}
+		case CMD_SYS_STAT:
+			poll_all_sockets(mw);
+			start_reply(mw, CMD_OK);
+			mw_putc(mw, mw->module_state);
+			mw_putc(mw, mw->flags);
+			mw_putc(mw, mw->channel_flags >> 8);
+			mw_putc(mw, mw->channel_flags);
+			end_reply(mw);
+			break;
+		default:
+			printf("Unhandled MegaWiFi command %s(%d) with length %X\n", cmd_names[command], command, size);
+			break;
+		}
+	} else if (mw->sock_fds[mw->transmit_channel - 1] >= 0 && mw->channel_state[mw->transmit_channel - 1] == 2) {
+		uint8_t channel = mw->transmit_channel - 1;
+		int sent = send(mw->sock_fds[channel], mw->transmit_buffer, mw->transmit_bytes, MSG_NOSIGNAL);
+		if (sent < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
+			close(mw->sock_fds[channel]);
+			mw->sock_fds[channel] = -1;
+			mw->channel_state[channel] = 0;
+			mw->channel_flags |= 1 << mw->transmit_channel;
+		} else if (sent < mw->transmit_bytes) {
+			//TODO: save this data somewhere so it can be sent in poll_socket
+			printf("Sent %d bytes on channel %d, but %d were requested\n", sent, mw->transmit_channel, mw->transmit_bytes);
+		}
+	} else {
+		printf("Unhandled receive of MegaWiFi data on channel %d\n", mw->transmit_channel);
+	}
+	mw->transmit_bytes = mw->expected_bytes = 0;
+}
+
+void *megawifi_write_b(uint32_t address, void *context, uint8_t value)
+{
+	if (!(address & 1)) {
+		return context;
+	}
+	megawifi *mw = get_megawifi(context);
+	address = address >> 1 & 7;
+	switch (address)
+	{
+	case 0:
+		switch (mw->transmit_state)
+		{
+		case TX_IDLE:
+			if (value == STX) {
+				mw->transmit_state = TX_LEN1;
+			}
+			break;
+		case TX_LEN1:
+			mw->transmit_channel = value >> 4;
+			mw->expected_bytes = value << 8 & 0xF00;
+			mw->transmit_state = TX_LEN2;
+			break;
+		case TX_LEN2:
+			mw->expected_bytes |= value;
+			mw->transmit_state = TX_PAYLOAD;
+			break;
+		case TX_PAYLOAD:
+			mw->transmit_buffer[mw->transmit_bytes++] = value;
+			if (mw->transmit_bytes == mw->expected_bytes) {
+				mw->transmit_state = TX_WAIT_ETX;
+			}
+			break;
+		case TX_WAIT_ETX:
+			if (value == ETX) {
+				mw->transmit_state = TX_IDLE;
+				process_packet(mw);
+			}
+			break;
+		}
+		break;
+	case 7:
+		mw->scratchpad = value;
+		break;
+	default:
+		printf("Unhandled write to MegaWiFi UART register %X: %X\n", address, value);
+	}
+	return context;
+}
+
+void *megawifi_write_w(uint32_t address, void *context, uint16_t value)
+{
+	return megawifi_write_b(address | 1, context, value);
+}
+
+uint8_t megawifi_read_b(uint32_t address, void *context)
+{
+	
+	if (!(address & 1)) {
+		return 0xFF;
+	}
+	megawifi *mw = get_megawifi(context);
+	address = address >> 1 & 7;
+	switch (address)
+	{
+	case 0:
+		poll_all_sockets(mw);
+		if (mw->receive_read < mw->receive_bytes) {
+			uint8_t ret = mw->receive_buffer[mw->receive_read++];
+			if (mw->receive_read == mw->receive_bytes) {
+				mw->receive_read = mw->receive_bytes = 0;
+			}
+			return ret;
+		}
+		return 0xFF;
+	case 5:
+		poll_all_sockets(mw);
+		//line status
+		return 0x60 | (mw->receive_read < mw->receive_bytes);
+	case 7:
+		return mw->scratchpad;
+	default:
+		printf("Unhandled read from MegaWiFi UART register %X\n", address);
+		return 0xFF;
+	}
+}
+
+uint16_t megawifi_read_w(uint32_t address, void *context)
+{
+	return 0xFF00 | megawifi_read_b(address | 1, context);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/megawifi.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,9 @@
+#ifndef MEGAWIFI_H_
+#define MEGAWIFI_H_
+
+void *megawifi_write_w(uint32_t address, void *context, uint16_t value);
+void *megawifi_write_b(uint32_t address, void *context, uint8_t value);
+uint16_t megawifi_read_w(uint32_t address, void *context);
+uint8_t megawifi_read_b(uint32_t address, void *context);
+
+#endif //MEGAWIFI_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/memmap.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,41 @@
+#ifndef MEMMAP_H_
+#define MEMMAP_H_
+
+typedef enum {
+	READ_16,
+	READ_8,
+	WRITE_16,
+	WRITE_8
+} ftype;
+
+#define MMAP_READ      0x01
+#define MMAP_WRITE     0x02
+#define MMAP_CODE      0x04
+#define MMAP_PTR_IDX   0x08
+#define MMAP_ONLY_ODD  0x10
+#define MMAP_ONLY_EVEN 0x20
+#define MMAP_FUNC_NULL 0x40
+#define MMAP_BYTESWAP  0x80
+#define MMAP_AUX_BUFF  0x100
+#define MMAP_READ_CODE 0x200
+
+typedef uint16_t (*read_16_fun)(uint32_t address, void * context);
+typedef uint8_t (*read_8_fun)(uint32_t address, void * context);
+typedef void * (*write_16_fun)(uint32_t address, void * context, uint16_t value);
+typedef void * (*write_8_fun)(uint32_t address, void * context, uint8_t value);
+
+typedef struct {
+	uint32_t     start;
+	uint32_t     end;
+	uint32_t     mask;
+	uint32_t     aux_mask;
+	uint16_t     ptr_index;
+	uint16_t     flags;
+	void *       buffer;
+	read_16_fun  read_16;
+	write_16_fun write_16;
+	read_8_fun   read_8;
+	write_8_fun  write_8;
+} memmap_chunk;
+
+#endif //MEMMAP_H_
--- a/menu.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/menu.c	Tue Dec 25 11:12:26 2018 -0800
@@ -8,71 +8,16 @@
 #include "backend.h"
 #include "util.h"
 #include "gst.h"
-#include "m68k_internal.h" //needed for get_native_address_trans, should be eliminated once handling of PC is cleaned up
-
-static menu_context *persist_path_menu;
-static void persist_path(void)
-{
-	char const *parts[] = {get_userdata_dir(), PATH_SEP, "sticky_path"};
-	char *pathfname = alloc_concat_m(3, parts);
-	FILE *f = fopen(pathfname, "wb");
-	if (f) {
-		if (fwrite(persist_path_menu->curpath, 1, strlen(persist_path_menu->curpath), f) != strlen(persist_path_menu->curpath)) {
-			warning("Failed to save menu path");
-		}
-		fclose(f);
-	} else {
-		warning("Failed to save menu path: Could not open %s for writing\n", pathfname);
-		
-	}
-	free(pathfname);
-}
+#include "paths.h"
+#include "saves.h"
+#include "config.h"
 
 static menu_context *get_menu(genesis_context *gen)
 {
 	menu_context *menu = gen->extra;
 	if (!menu) {
 		gen->extra = menu = calloc(1, sizeof(menu_context));
-		menu->curpath = NULL;
-		char *remember_path = tern_find_path(config, "ui\0remember_path\0", TVAL_PTR).ptrval;
-		if (!remember_path || !strcmp("on", remember_path)) {
-			char const *parts[] = {get_userdata_dir(), PATH_SEP, "sticky_path"};
-			char *pathfname = alloc_concat_m(3, parts);
-			FILE *f = fopen(pathfname, "rb");
-			if (f) {
-				long pathsize = file_size(f);
-				if (pathsize > 0) {
-					menu->curpath = malloc(pathsize + 1);
-					if (fread(menu->curpath, 1, pathsize, f) != pathsize) {
-						warning("Error restoring saved menu path");
-						free(menu->curpath);
-						menu->curpath = NULL;
-					} else {
-						menu->curpath[pathsize] = 0;
-					}
-				}
-				fclose(f);
-			}
-			free(pathfname);
-			if (!persist_path_menu) {
-				atexit(persist_path);
-			}
-			persist_path_menu = menu;
-		}
-		if (!menu->curpath) {
-			menu->curpath = tern_find_path(config, "ui\0initial_path\0", TVAL_PTR).ptrval;
-		}
-		if (!menu->curpath){
-#ifdef __ANDROID__
-			menu->curpath = get_external_storage_path();
-#else
-			menu->curpath = "$HOME";
-#endif
-		}
-		tern_node *vars = tern_insert_ptr(NULL, "HOME", get_home_dir());
-		vars = tern_insert_ptr(vars, "EXEDIR", get_exe_dir());
-		menu->curpath = replace_vars(menu->curpath, vars, 1);
-		tern_free(vars);
+		get_initial_browse_path(&menu->curpath);
 	}
 	return menu;
 }
@@ -97,17 +42,6 @@
 	}
 }
 
-int menu_dir_sort(const void *a, const void *b)
-{
-	const dir_entry *da, *db;
-	da = a;
-	db = b;
-	if (da->is_dir != db->is_dir) {
-		return db->is_dir - da->is_dir;
-	}
-	return strcasecmp(((dir_entry *)a)->name, ((dir_entry *)b)->name);
-}
-
 void copy_string_from_guest(m68k_context *m68k, uint32_t guest_addr, char *buf, size_t maxchars)
 {
 	char *cur;
@@ -150,54 +84,6 @@
 
 #define SAVE_INFO_BUFFER_SIZE (11*40)
 
-#ifdef __ANDROID__
-#include <SDL.h>
-#include <jni.h>
-char *get_external_storage_path()
-{
-	static char *ret;
-	if (ret) {
-		return ret;
-	}
-	JNIEnv *env = SDL_AndroidGetJNIEnv();
-	if ((*env)->PushLocalFrame(env, 8) < 0) {
-		return NULL;
-	}
-
-	jclass Environment = (*env)->FindClass(env, "android/os/Environment");
-	jmethodID getExternalStorageDirectory =
-		(*env)->GetStaticMethodID(env, Environment, "getExternalStorageDirectory", "()Ljava/io/File;");
-	jobject file = (*env)->CallStaticObjectMethod(env, Environment, getExternalStorageDirectory);
-	if (!file) {
-		goto cleanup;
-	}
-
-	jmethodID getAbsolutePath = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, file),
-		"getAbsolutePath", "()Ljava/lang/String;");
-	jstring path = (*env)->CallObjectMethod(env, file, getAbsolutePath);
-
-	char const *tmp = (*env)->GetStringUTFChars(env, path, NULL);
-	ret = strdup(tmp);
-	(*env)->ReleaseStringUTFChars(env, path, tmp);
-
-cleanup:
-	(*env)->PopLocalFrame(env, NULL);
-	return ret;
-}
-#endif
-
-#ifdef _WIN32
-#define localtime_r(a,b) localtime(a)
-//windows inclues seem not to like certain single letter defines from m68k_internal.h
-//get rid of them here
-#undef X
-#undef N
-#undef Z
-#undef V
-#undef C
-#include <windows.h>
-#endif
-
 uint32_t copy_dir_entry_to_guest(uint32_t dst, m68k_context *m68k, char *name, uint8_t is_dir)
 {
 	uint8_t *dest = get_native_pointer(dst, (void **)m68k->mem_pointers, &m68k->options->gen);
@@ -235,7 +121,9 @@
 	}
 	return dst;
 }
-
+#ifdef _WIN32
+#include <windows.h>
+#endif
 void * menu_write_w(uint32_t address, void * context, uint16_t value)
 {
 	m68k_context *m68k = context;
@@ -246,7 +134,7 @@
 		switch (address >> 2)
 		{
 		case 0: {
-#ifdef _WIN32
+#if _WIN32
 			//handle virtual "drives" directory
 			if (menu->curpath[0] == PATH_SEP[0]) {
 				char drivestrings[4096];
@@ -267,7 +155,7 @@
 			size_t num_entries;
 			dir_entry *entries = get_dir_list(menu->curpath, &num_entries);
 			if (entries) {
-				qsort(entries, num_entries, sizeof(dir_entry), menu_dir_sort);
+				sort_dir_list(entries, num_entries);
 			} else {
 				warning("Failed to open directory %s: %s\n", menu->curpath, strerror(errno));
 				entries = malloc(sizeof(dir_entry));
@@ -281,40 +169,17 @@
 				dst = copy_dir_entry_to_guest(dst, m68k, "..", 1);
 			}
 #endif
-			char *ext_filter = strdup(tern_find_path_default(config, "ui\0extensions\0", (tern_val){.ptrval = "bin gen md smd sms gg"}, TVAL_PTR).ptrval);
-			uint32_t num_exts = 0, ext_storage = 5;
-			char **ext_list = malloc(sizeof(char *) * ext_storage);
-			char *cur_filter = ext_filter;
-			while (*cur_filter)
-			{
-				if (num_exts == ext_storage) {
-					ext_storage *= 2;
-					ext_list = realloc(ext_list, sizeof(char *) * ext_storage);
-				}
-				ext_list[num_exts++] = cur_filter;
-				cur_filter = split_keyval(cur_filter);
-			}
+			uint32_t num_exts;
+			char **ext_list = get_extension_list(config, &num_exts);
 			for (size_t i = 0; dst && i < num_entries; i++)
 			{
 				if (num_exts && !entries[i].is_dir) {
-					char *ext = path_extension(entries[i].name);
-					if (!ext) {
-						continue;
-					}
-					uint32_t extidx;
-					for (extidx = 0; extidx < num_exts; extidx++)
-					{
-						if (!strcasecmp(ext, ext_list[extidx])) {
-							break;
-						}
-					}
-					if (extidx == num_exts) {
+					if (!path_matches_extensions(entries[i].name, ext_list, num_exts)) {
 						continue;
 					}
 				}
 				dst = copy_dir_entry_to_guest(dst,  m68k, entries[i].name, entries[i].is_dir);
 			}
-			free(ext_filter);
 			free(ext_list);
 			//terminate list
 			uint8_t *dest = get_native_pointer(dst, (void **)m68k->mem_pointers, &m68k->options->gen);
@@ -327,42 +192,10 @@
 		case 1: {
 			char buf[4096];
 			copy_string_from_guest(m68k, dst, buf, sizeof(buf));
-			if (!strcmp(buf, "..")) {
-#ifdef _WIN32
-				if (menu->curpath[1] == ':' && !menu->curpath[2]) {
-					menu->curpath[0] = PATH_SEP[0];
-					menu->curpath[1] = 0;
-					break;
-				}
-#endif
-				size_t len = strlen(menu->curpath);
-				while (len > 0) {
-					--len;
-					if (is_path_sep(menu->curpath[len])) {
-						if (!len) {
-							//special handling for /
-							menu->curpath[len+1] = 0;
-						} else {
-							menu->curpath[len] = 0;
-						}
-						break;
-					}
-				}
-			} else {
-				char *tmp = menu->curpath;
-#ifdef _WIN32
-				if (menu->curpath[0] == PATH_SEP[0] && !menu->curpath[1]) {
-					menu->curpath = strdup(buf);
-				} else
-#endif
-				if (is_path_sep(menu->curpath[strlen(menu->curpath) - 1])) {
-					menu->curpath = alloc_concat(menu->curpath, buf);
-				} else {
-					char const *pieces[] = {menu->curpath, PATH_SEP, buf};
-					menu->curpath = alloc_concat_m(3, pieces);
-				}
-				free(tmp);
-			}
+			buf[sizeof(buf)-1] = 0;
+			char *tmp = menu->curpath;
+			menu->curpath = path_append(tmp, buf);
+			free(tmp);
 			break;
 		}
 		case 2:
@@ -399,63 +232,19 @@
 			char *cur = buffer;
 			if (gen->header.next_context && gen->header.next_context->save_dir) {
 				char *end = buffer + SAVE_INFO_BUFFER_SIZE;
-				char slotfile[] = "slot_0.state";
-				char slotfilegst[] = "slot_0.gst";
-				char const * parts[3] = {gen->header.next_context->save_dir, PATH_SEP, slotfile};
-				char const * partsgst[3] = {gen->header.next_context->save_dir, PATH_SEP, slotfilegst};
-				struct tm ltime;
-				char *fname;
-				time_t modtime;
-				for (int i = 0; i < 10 && cur < end; i++)
+				uint32_t num_slots;
+				save_slot_info *slots = get_slot_info(gen->header.next_context, &num_slots);
+				for (uint32_t i = 0; i < num_slots; i++)
 				{
-					slotfile[5] = i + '0';
-					fname = alloc_concat_m(3, parts);
-					modtime = get_modification_time(fname);
-					free(fname);
-					if (modtime) {
-						cur += snprintf(cur, end-cur, "Slot %d - ", i);
-						cur += strftime(cur, end-cur, "%c", localtime_r(&modtime, &ltime));
-						
-					} else {
-						slotfilegst[5] = i + '0';
-						fname = alloc_concat_m(3, partsgst);
-						modtime = get_modification_time(fname);
-						free(fname);
-						if (modtime) {
-							cur += snprintf(cur, end-cur, "Slot %d - ", i);
-							cur += strftime(cur, end-cur, "%c", localtime_r(&modtime, &ltime));
-						} else {
-							cur += snprintf(cur, end-cur, "Slot %d - EMPTY", i);
-						}
+					size_t desc_len = strlen(slots[i].desc) + 1;//+1 for string terminator
+					char *after = cur + desc_len + 1;//+1 for list terminator
+					if (after > cur) {
+						desc_len -= after - cur;
 					}
-					//advance past the null terminator for this entry
-					cur++;
+					memcpy(cur, slots[i].desc, desc_len);
+					cur = after;
 				}
-				if (cur < end) {
-					parts[2] = "quicksave.state";
-					fname = alloc_concat_m(3, parts);
-					modtime = get_modification_time(fname);
-					free(fname);
-					if (modtime) {
-						cur += strftime(cur, end-cur, "Quick  - %c", localtime_r(&modtime, &ltime));
-					} else {
-						parts[2] = "quicksave.gst";
-						fname = alloc_concat_m(3, parts);
-						modtime = get_modification_time(fname);
-						free(fname);
-						if (modtime) {
-							cur += strftime(cur, end-cur, "Quick  - %c", localtime_r(&modtime, &ltime));
-						} else if ((end-cur) > strlen("Quick  - EMPTY")){
-							cur += strlen(strcpy(cur, "Quick  - EMPTY"));
-						}
-					}
-					//advance past the null terminator for this entry
-					cur++;
-					if (cur < end) {
-						//terminate the list
-						*(cur++) = 0;
-					}
-				}
+				*cur = 0;//terminate list
 			} else {
 				*(cur++) = 0;
 				*(cur++) = 0;
@@ -475,36 +264,7 @@
 			if (gen->header.next_context && gen->header.next_context->save_dir) {
 				if (!gen->header.next_context->load_state(gen->header.next_context, dst)) {
 					break;
-				}/*
-				char numslotname[] = "slot_0.state";
-				char *slotname;
-				if (dst == QUICK_SAVE_SLOT) {
-					slotname = "quicksave.state";
-				} else {
-					numslotname[5] = '0' + dst;
-					slotname = numslotname;
 				}
-				char const *parts[] = {gen->header.next_context->save_dir, PATH_SEP, slotname};
-				char *statepath = alloc_concat_m(3, parts);
-				gen->header.next_context->load_state
-				genesis_context *next = (genesis_context *)gen->header.next_context;
-				deserialize_buffer state;
-				uint32_t pc = 0;
-				if (load_from_file(&state, statepath)) {
-					genesis_deserialize(&state, next);
-					free(state.data);
-					//HACK
-					pc = next->m68k->last_prefetch_address;
-				} else {
-					strcpy(statepath + strlen(statepath)-strlen("state"), "gst");
-					pc = load_gst(next, statepath);
-				}
-				free(statepath);
-				if (!pc) {
-					break;
-				}
-				next->m68k->resume_pc = get_native_address_trans(next->m68k, pc);
-				*/
 			}
 			m68k->should_return = 1;
 			break;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mw_commands.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,33 @@
+	E(CMD_OK),
+	E(CMD_VERSION),
+	E(CMD_ECHO),
+	E(CMD_AP_SCAN),
+	E(CMD_AP_CFG),
+	E(CMD_AP_CFG_GET),
+	E(CMD_IP_CURRENT),
+	E(CMD_RESERVED),
+	E(CMD_IP_CFG),
+	E(CMD_IP_CFG_GET),
+	E(CMD_DEF_AP_CFG),
+	E(CMD_DEF_AP_CFG_GET),
+	E(CMD_AP_JOIN),
+	E(CMD_AP_LEAVE),
+	E(CMD_TCP_CON),
+	E(CMD_TCP_BIND),
+	E(CMD_TCP_ACCEPT),
+	E(CMD_TCP_DISC),
+	E(CMD_UDP_SET),
+	E(CMD_UDP_CLR),
+	E(CMD_SOCK_STAT),
+	E(CMD_PING),
+	E(CMD_SNTP_CFG),
+	E(CMD_SNTP_CFG_GET),
+	E(CMD_DATETIME),
+	E(CMD_DT_SET),
+	E(CMD_FLASH_WRITE),
+	E(CMD_FLASH_READ),
+	E(CMD_FLASH_ERASE),
+	E(CMD_FLASH_ID),
+	E(CMD_SYS_STAT),
+	E(CMD_DEF_CFG_SET),
+	E(CMD_HRNG_GET),
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,49 @@
+#include <sys/types.h>
+#include <ifaddrs.h>
+#include <netinet/in.h>
+#include "net.h"
+
+static uint8_t is_loopback(struct sockaddr_in *addr)
+{
+	return (addr->sin_addr.s_addr & 0xFF) == 127;
+}
+
+static void format_address(uint8_t *dst, struct sockaddr_in *addr)
+{
+	long ip = addr->sin_addr.s_addr;
+	dst[0] = ip;
+	dst[1] = ip >> 8;
+	dst[2] = ip >> 16;
+	dst[3] = ip >> 24;
+}
+
+uint8_t get_host_address(iface_info *out)
+{
+	struct ifaddrs *entries, *current, *localhost;
+	if (getifaddrs(&entries)) {
+		return 0;
+	}
+	
+	for (current = entries; current; current = current->ifa_next)
+	{
+		if (current->ifa_addr && current->ifa_addr->sa_family == AF_INET) {
+			struct sockaddr_in *addr = (struct sockaddr_in *)current->ifa_addr;
+			if (is_loopback(addr)) {
+				localhost = current;
+			} else {
+				break;
+			}
+		}
+	}
+	if (!current && localhost) {
+		current = localhost;
+	}
+	uint8_t ret = 0;
+	if (current) {
+		ret = 1;
+		format_address(out->ip, (struct sockaddr_in *)current->ifa_addr);
+		format_address(out->net_mask, (struct sockaddr_in *)current->ifa_netmask);
+	}
+	freeifaddrs(entries);
+	return ret;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,12 @@
+#ifndef NET_H_
+#define NET_H_
+#include <stdint.h>
+
+typedef struct {
+	uint8_t ip[4];
+	uint8_t net_mask[4];
+} iface_info;
+
+uint8_t get_host_address(iface_info *out);
+
+#endif //NET_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net_win.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,14 @@
+#include "net.h"
+
+uint8_t get_host_address(iface_info *out)
+{
+	out->ip[0] = 127;
+	out->ip[1] = 0;
+	out->ip[2] = 0;
+	out->ip[3] = 1;
+	out->net_mask[0] = 255;
+	out->net_mask[0] = 255;
+	out->net_mask[0] = 255;
+	out->net_mask[0] = 0;
+	return 1;
+}
--- a/nor.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/nor.c	Tue Dec 25 11:12:26 2018 -0800
@@ -1,6 +1,7 @@
 #include "genesis.h"
 #include <stdlib.h>
 #include <string.h>
+#include "util.h"
 
 enum {
 	NOR_NORMAL,
@@ -31,9 +32,11 @@
 	state->cmd_state = NOR_CMD_IDLE;
 	state->alt_cmd = 0;
 	state->bus_flags = bus_flags;
+	state->cmd_address1 = 0x5555;
+	state->cmd_address2 = 0x2AAA;
 }
 
-void nor_run(nor_state *state, uint32_t cycle)
+void nor_run(nor_state *state, m68k_context *m68k, uint32_t cycle)
 {
 	if (state->last_write_cycle == 0xFFFFFFFF) {
 		return;
@@ -44,6 +47,10 @@
 			state->buffer[state->current_page + i] = state->page_buffer[i];
 		}
 		memset(state->page_buffer, 0xFF, state->page_size);
+		if (state->bus_flags == RAM_FLAG_BOTH) {
+			//TODO: add base address of NOR device to start and end addresses
+			m68k_invalidate_code_range(m68k, state->current_page, state->current_page + state->page_size);
+		}
 	}
 }
 
@@ -62,10 +69,13 @@
 		address = address >> 1;
 	}
 	
-	nor_run(state, m68k->current_cycle);
+	nor_run(state, m68k, m68k->current_cycle);
 	switch (state->mode)
 	{
 	case NOR_NORMAL:
+		if (state->bus_flags == RAM_FLAG_BOTH) {
+			address ^= 1;
+		}
 		return state->buffer[address & (state->size-1)];
 		break;
 	case NOR_PRODUCTID:
@@ -80,7 +90,7 @@
 			return 0xFE;
 		default:
 			return 0xFE;
-		}
+		}			//HERE
 		break;
 	case NOR_BOOTBLOCK:
 		break;
@@ -103,6 +113,9 @@
 		if (state->last_write_cycle != 0xFFFFFFFF) {
 			state->current_page = address & (state->size - 1) & ~(state->page_size - 1);
 		}
+		if (state->bus_flags == RAM_FLAG_BOTH) {
+			address ^= 1;
+		}
 		state->page_buffer[address & (state->page_size - 1)] = value;
 		break;
 	case NOR_PRODUCTID:
@@ -129,11 +142,11 @@
 		address = address >> 1;
 	}
 	
-	nor_run(state, m68k->current_cycle);
+	nor_run(state, m68k, m68k->current_cycle);
 	switch (state->cmd_state)
 	{
 	case NOR_CMD_IDLE:
-		if (value == 0xAA && (address & (state->size - 1)) == 0x5555) {
+		if (value == 0xAA && (address & (state->size - 1)) == state->cmd_address1) {
 			state->cmd_state = NOR_CMD_AA;
 		} else {
 			nor_write_byte(state, address, value, m68k->current_cycle);
@@ -141,16 +154,16 @@
 		}
 		break;
 	case NOR_CMD_AA:
-		if (value == 0x55 && (address & (state->size - 1)) == 0x2AAA) {
+		if (value == 0x55 && (address & (state->size - 1)) == state->cmd_address2) {
 			state->cmd_state = NOR_CMD_55;
 		} else {
-			nor_write_byte(state, 0x5555, 0xAA, m68k->current_cycle);
+			nor_write_byte(state, state->cmd_address1, 0xAA, m68k->current_cycle);
 			nor_write_byte(state, address, value, m68k->current_cycle);
 			state->cmd_state = NOR_CMD_IDLE;
 		}
 		break;
 	case NOR_CMD_55:
-		if ((address & (state->size - 1)) == 0x5555) {
+		if ((address & (state->size - 1)) == state->cmd_address1) {
 			if (state->alt_cmd) {
 				switch(value)
 				{
@@ -187,8 +200,8 @@
 				}
 			}
 		} else {
-			nor_write_byte(state, 0x5555, 0xAA, m68k->current_cycle);
-			nor_write_byte(state, 0x2AAA, 0x55, m68k->current_cycle);
+			nor_write_byte(state, state->cmd_address1, 0xAA, m68k->current_cycle);
+			nor_write_byte(state, state->cmd_address2, 0x55, m68k->current_cycle);
 			nor_write_byte(state, address, value, m68k->current_cycle);
 		}
 		state->cmd_state = NOR_CMD_IDLE;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nuklear_ui/blastem_nuklear.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,1963 @@
+#define NK_IMPLEMENTATION
+#define NK_SDL_GLES2_IMPLEMENTATION
+
+#include <stdlib.h>
+#include <limits.h>
+#include "blastem_nuklear.h"
+#include "font.h"
+#include "../render.h"
+#include "../render_sdl.h"
+#include "../util.h"
+#include "../paths.h"
+#include "../saves.h"
+#include "../blastem.h"
+#include "../config.h"
+#include "../io.h"
+#include "../png.h"
+#include "../controller_info.h"
+#include "../bindings.h"
+
+static struct nk_context *context;
+
+typedef struct
+{
+	uint32_t         *image_data;
+	uint32_t         width, height;
+	struct nk_image  ui;
+} ui_image;
+
+static ui_image **ui_images, *controller_360, *controller_ps4, *controller_ps4_6b;
+static uint32_t num_ui_images, ui_image_storage;
+
+typedef void (*view_fun)(struct nk_context *);
+static view_fun current_view;
+static view_fun *previous_views;
+static uint32_t view_storage;
+static uint32_t num_prev;
+static struct nk_font *def_font;
+static uint8_t config_dirty;
+
+static void push_view(view_fun new_view)
+{
+	if (num_prev == view_storage) {
+		view_storage = view_storage ? 2*view_storage : 2;
+		previous_views = realloc(previous_views, view_storage*sizeof(view_fun));
+	}
+	previous_views[num_prev++] = current_view;
+	current_view = new_view;
+}
+
+static void pop_view()
+{
+	if (num_prev) {
+		current_view = previous_views[--num_prev];
+	}
+}
+
+static void clear_view_stack()
+{
+	num_prev = 0;
+}
+
+void view_play(struct nk_context *context)
+{
+	
+}
+
+void view_file_browser(struct nk_context *context, uint8_t normal_open)
+{
+	static char *current_path;
+	static dir_entry *entries;
+	static size_t num_entries;
+	static int32_t selected_entry = -1;
+	static char **ext_list;
+	static uint32_t num_exts;
+	static uint8_t got_ext_list;
+	if (!current_path) {
+		get_initial_browse_path(&current_path);
+	}
+	if (!entries) {
+		entries = get_dir_list(current_path, &num_entries);
+		if (entries) {
+			sort_dir_list(entries, num_entries);
+		}
+	}
+	if (!got_ext_list) {
+		ext_list = get_extension_list(config, &num_exts);
+		got_ext_list = 1;
+	}
+	uint32_t width = render_width();
+	uint32_t height = render_height();
+	if (nk_begin(context, "Load ROM", nk_rect(0, 0, width, height), 0)) {
+		nk_layout_row_static(context, height - context->style.font->height * 3, width - 60, 1);
+		int32_t old_selected = selected_entry;
+		if (nk_group_begin(context, "Select ROM", NK_WINDOW_BORDER | NK_WINDOW_TITLE)) {
+			nk_layout_row_static(context, context->style.font->height - 2, width-100, 1);
+			for (int32_t i = 0; i < num_entries; i++)
+			{
+				if (entries[i].name[0] == '.' && entries[i].name[1] != '.') {
+					continue;
+				}
+				if (num_exts && !entries[i].is_dir && !path_matches_extensions(entries[i].name, ext_list, num_exts)) {
+					continue;
+				}
+				int selected = i == selected_entry;
+				nk_selectable_label(context, entries[i].name, NK_TEXT_ALIGN_LEFT, &selected);
+				if (selected) {
+					selected_entry = i;
+				} else if (i == selected_entry) {
+					selected_entry = -1;
+				}
+			}
+			nk_group_end(context);
+		}
+		nk_layout_row_static(context, context->style.font->height * 1.75, width > 600 ? 300 : width / 2, 2);
+		if (nk_button_label(context, "Back")) {
+			pop_view();
+		}
+		if (nk_button_label(context, "Open") || (old_selected >= 0 && selected_entry < 0)) {
+			if (selected_entry < 0) {
+				selected_entry = old_selected;
+			}
+			char *full_path = path_append(current_path, entries[selected_entry].name);
+			if (entries[selected_entry].is_dir) {
+				free(current_path);
+				current_path = full_path;
+				free_dir_list(entries, num_entries);
+				entries = NULL;
+			} else {
+				if(normal_open) {
+					if (current_system) {
+						current_system->next_rom = full_path;
+						current_system->request_exit(current_system);
+					} else {
+						init_system_with_media(full_path, SYSTEM_UNKNOWN);
+						free(full_path);
+					}
+				} else {
+					lockon_media(full_path);
+					free(full_path);
+				}
+				clear_view_stack();
+				current_view = view_play;
+			}
+			selected_entry = -1;
+		}
+		nk_end(context);
+	}
+}
+
+void view_load(struct nk_context *context)
+{
+	view_file_browser(context, 1);
+}
+
+void view_lock_on(struct nk_context *context)
+{
+	view_file_browser(context, 0);
+}
+
+void view_about(struct nk_context *context)
+{
+	const char *lines[] = {
+		"BlastEm v0.6.0",
+		"Copyright 2012-2017 Michael Pavone",
+		"",
+		"BlastEm is a high performance open source",
+		"(GPLv3) Genesis/Megadrive emulator",
+	};
+	const uint32_t NUM_LINES = sizeof(lines)/sizeof(*lines);
+	const char *thanks[] = {
+		"Nemesis: Documentatino and test ROMs",
+		"Charles MacDonald: Documentation",
+		"Eke-Eke: Documentation",
+		"Bart Trzynadlowski: Documentation",
+		"KanedaFR: Hosting the best Sega forum",
+		"Titan: Awesome demos and documentation",
+		"micky: Testing",
+		"Sasha: Testing",
+		"lol-frank: Testing",
+		"Sik: Testing",
+		"Tim Lawrence : Testing",
+		"ComradeOj: Testing",
+		"Vladikcomper: Testing"
+	};
+	const uint32_t NUM_THANKS = sizeof(thanks)/sizeof(*thanks);
+	uint32_t width = render_width();
+	uint32_t height = render_height();
+	if (nk_begin(context, "About", nk_rect(0, 0, width, height), 0)) {
+		nk_layout_row_static(context, context->style.font->height, width-40, 1);
+		for (uint32_t i = 0; i < NUM_LINES; i++)
+		{
+			nk_label(context, lines[i], NK_TEXT_LEFT);
+		}
+		nk_layout_row_static(context, height - (context->style.font->height * 2 + 20) - (context->style.font->height +4)*NUM_LINES, width-40, 1);
+		if (nk_group_begin(context, "Special Thanks", NK_WINDOW_TITLE)) {
+			nk_layout_row_static(context, context->style.font->height, width - 80, 1);
+			for (uint32_t i = 0; i < NUM_THANKS; i++)
+			{
+				nk_label(context, thanks[i], NK_TEXT_LEFT);
+			}
+			nk_group_end(context);
+		}
+		nk_layout_row_static(context, context->style.font->height * 1.75, width/3, 1);
+		if (nk_button_label(context, "Back")) {
+			pop_view();
+		}
+		nk_end(context);
+	}
+}
+
+typedef struct {
+	const char *title;
+	view_fun   next_view;
+} menu_item;
+
+static save_slot_info *slots;
+static uint32_t num_slots, selected_slot;
+
+void view_choose_state(struct nk_context *context, uint8_t is_load)
+{
+	uint32_t width = render_width();
+	uint32_t height = render_height();
+	if (nk_begin(context, "Slot Picker", nk_rect(0, 0, width, height), 0)) {
+		nk_layout_row_static(context, height - context->style.font->height * 3, width - 60, 1);
+		if (nk_group_begin(context, "Select Save Slot", NK_WINDOW_BORDER | NK_WINDOW_TITLE)) {
+			nk_layout_row_static(context, context->style.font->height - 2, width-100, 1);
+			if (!slots) {
+				slots = get_slot_info(current_system, &num_slots);
+			}
+			for (uint32_t i = 0; i < num_slots; i++)
+			{
+				int selected = i == selected_slot;
+				nk_selectable_label(context, slots[i].desc, NK_TEXT_ALIGN_LEFT, &selected);
+				if (selected && (slots[i].modification_time || !is_load)) {
+					selected_slot = i;
+				}
+			}
+			nk_group_end(context);
+		}
+		nk_layout_row_static(context, context->style.font->height * 1.75, width > 600 ? 300 : width / 2, 2);
+		if (nk_button_label(context, "Back")) {
+			pop_view();
+		}
+		if (is_load) {
+			if (nk_button_label(context, "Load")) {
+				current_system->load_state(current_system, selected_slot);
+				current_view = view_play;
+			}
+		} else {
+			if (nk_button_label(context, "Save")) {
+				current_system->save_state = selected_slot + 1;
+				current_view = view_play;
+			}
+		}
+		nk_end(context);
+	}
+}
+
+void view_save_state(struct nk_context *context)
+{
+	view_choose_state(context, 0);
+}
+
+void view_load_state(struct nk_context *context)
+{
+	view_choose_state(context, 1);
+}
+
+typedef void (*menu_handler)(uint32_t index);
+
+static void menu(struct nk_context *context, uint32_t num_entries, const menu_item *items, menu_handler handler)
+{
+	const uint32_t button_height = context->style.font->height * 1.75;
+	const uint32_t ideal_button_width = context->style.font->height * 10;
+	const uint32_t button_space = 6;
+	
+	uint32_t width = render_width();
+	uint32_t height = render_height();
+	uint32_t top = height/2 - (button_height * num_entries)/2;
+	uint32_t button_width = width > ideal_button_width ? ideal_button_width : width;
+	uint32_t left = width/2 - button_width/2;
+	
+	nk_layout_space_begin(context, NK_STATIC, top + button_height * num_entries, num_entries);
+	for (uint32_t i = 0; i < num_entries; i++)
+	{
+		nk_layout_space_push(context, nk_rect(left, top + i * button_height, button_width, button_height-button_space));
+		if (nk_button_label(context, items[i].title)) {
+			if (items[i].next_view) {
+				push_view(items[i].next_view);
+				if (current_view == view_save_state || current_view == view_load_state) {
+					free_slot_info(slots);
+					slots = NULL;
+				}
+			} else {
+				handler(i);
+			}
+		}
+	}
+	nk_layout_space_end(context);
+}
+
+void binding_loop(char *key, tern_val val, uint8_t valtype, void *data)
+{
+	if (valtype != TVAL_PTR) {
+		return;
+	}
+	tern_node **binding_lookup = data;
+	*binding_lookup = tern_insert_ptr(*binding_lookup, val.ptrval, strdup(key));
+}
+
+static int32_t keycode;
+static const char *set_binding;
+char *set_label;
+void binding_group(struct nk_context *context, char *name, const char **binds, const char **bind_names, uint32_t num_binds, tern_node *binding_lookup)
+{
+	nk_layout_row_static(context, (context->style.font->height + 4)*num_binds+context->style.font->height+30, render_width() - 80, 1);
+	if (nk_group_begin(context, name, NK_WINDOW_TITLE)) {
+		nk_layout_row_static(context, context->style.font->height, render_width()/2 - 80, 2);
+		
+		for (int i = 0; i < num_binds; i++)
+		{
+			char *label_alloc = bind_names ? NULL : path_extension(binds[i]);
+			const char *label = label_alloc;
+			if (!label) {
+				label = bind_names ? bind_names[i] : binds[i];
+			}
+			nk_label(context, label, NK_TEXT_LEFT);
+			if (nk_button_label(context, tern_find_ptr_default(binding_lookup, binds[i], "Not Set"))) {
+				set_binding = binds[i];
+				set_label = strdup(label);
+				keycode = 0;
+			}
+			if (label_alloc) {
+				free(label_alloc);
+			}
+		}
+		nk_group_end(context);
+	}
+}
+
+static char *get_key_name(int32_t keycode)
+{
+	char *name = NULL;
+	if (keycode > ' ' && keycode < 0x80) {
+		//key corresponds to a printable non-whitespace character
+		name = malloc(2);
+		name[0] = keycode;
+		name[1] = 0;
+	} else {
+		switch (keycode)
+		{
+		case RENDERKEY_UP: name = "up"; break;
+		case RENDERKEY_DOWN: name = "down"; break;
+		case RENDERKEY_LEFT: name = "left"; break;
+		case RENDERKEY_RIGHT: name = "right"; break;
+		case '\r': name = "enter"; break;
+		case ' ': name = "space"; break;
+		case '\t': name = "tab"; break;
+		case '\b': name = "backspace"; break;
+		case RENDERKEY_ESC: name = "esc"; break;
+		case RENDERKEY_DEL: name = "delete"; break;
+		case RENDERKEY_LSHIFT: name = "lshift"; break;
+		case RENDERKEY_RSHIFT: name = "rshift"; break;
+		case RENDERKEY_LCTRL: name = "lctrl"; break;
+		case RENDERKEY_RCTRL: name = "rctrl"; break;
+		case RENDERKEY_LALT: name = "lalt"; break;
+		case RENDERKEY_RALT: name = "ralt"; break;
+		case RENDERKEY_HOME: name = "home"; break;
+		case RENDERKEY_END: name = "end"; break;
+		case RENDERKEY_PAGEUP: name = "pageup"; break;
+		case RENDERKEY_PAGEDOWN: name = "pagedown"; break;
+		case RENDERKEY_F1: name = "f1"; break;
+		case RENDERKEY_F2: name = "f2"; break;
+		case RENDERKEY_F3: name = "f3"; break;
+		case RENDERKEY_F4: name = "f4"; break;
+		case RENDERKEY_F5: name = "f5"; break;
+		case RENDERKEY_F6: name = "f6"; break;
+		case RENDERKEY_F7: name = "f7"; break;
+		case RENDERKEY_F8: name = "f8"; break;
+		case RENDERKEY_F9: name = "f9"; break;
+		case RENDERKEY_F10: name = "f10"; break;
+		case RENDERKEY_F11: name = "f11"; break;
+		case RENDERKEY_F12: name = "f12"; break;
+		case RENDERKEY_SELECT: name = "select"; break;
+		case RENDERKEY_PLAY: name = "play"; break;
+		case RENDERKEY_SEARCH: name = "search"; break;
+		case RENDERKEY_BACK: name = "back"; break;
+		case RENDERKEY_NP0: name = "np0"; break;
+		case RENDERKEY_NP1: name = "np1"; break;
+		case RENDERKEY_NP2: name = "np2"; break;
+		case RENDERKEY_NP3: name = "np3"; break;
+		case RENDERKEY_NP4: name = "np4"; break;
+		case RENDERKEY_NP5: name = "np5"; break;
+		case RENDERKEY_NP6: name = "np6"; break;
+		case RENDERKEY_NP7: name = "np7"; break;
+		case RENDERKEY_NP8: name = "np8"; break;
+		case RENDERKEY_NP9: name = "np9"; break;
+		case RENDERKEY_NP_DIV: name = "np/"; break;
+		case RENDERKEY_NP_MUL: name = "np*"; break;
+		case RENDERKEY_NP_MIN: name = "np-"; break;
+		case RENDERKEY_NP_PLUS: name = "np+"; break;
+		case RENDERKEY_NP_ENTER: name = "npenter"; break;
+		case RENDERKEY_NP_STOP: name = "np."; break;
+		}
+		if (name) {
+			name = strdup(name);
+		}
+	}
+	return name;
+}
+
+void view_key_bindings(struct nk_context *context)
+{
+	const char *controller1_binds[] = {
+		"gamepads.1.up", "gamepads.1.down", "gamepads.1.left", "gamepads.1.right",
+		"gamepads.1.a", "gamepads.1.b", "gamepads.1.c",
+		"gamepads.1.x", "gamepads.1.y", "gamepads.1.z",
+		"gamepads.1.start", "gamepads.1.mode"
+	};
+	const char *controller2_binds[] = {
+		"gamepads.2.up", "gamepads.2.down", "gamepads.2.left", "gamepads.2.right",
+		"gamepads.2.a", "gamepads.2.b", "gamepads.2.c",
+		"gamepads.2.x", "gamepads.2.y", "gamepads.2.z",
+		"gamepads.2.start", "gamepads.2.mode"
+	};
+	const char *general_binds[] = {
+		"ui.exit", "ui.save_state", "ui.toggle_fullscreen", "ui.soft_reset", "ui.reload",
+		"ui.screenshot", "ui.sms_pause", "ui.toggle_keyboard_cpatured", "ui.release_mouse"
+	};
+	const char *general_names[] = {
+		"Show Menu", "Quick Save", "Toggle Fullscreen", "Soft Reset", "Reload Media",
+		"Internal Screenshot", "SMS Pause", "Capture Keyboard", "Release Mouse"
+	};
+	const char *speed_binds[] = {
+		"ui.next_speed", "ui.prev_speed",
+		"ui.set_speed.0", "ui.set_speed.1", "ui.set_speed.2" ,"ui.set_speed.3", "ui.set_speed.4",
+		"ui.set_speed.5", "ui.set_speed.6", "ui.set_speed.7" ,"ui.set_speed.8", "ui.set_speed.9",
+	};
+	const char *speed_names[] = {
+		"Next", "Previous",
+		"Default Speed", "Set Speed 1", "Set Speed 2", "Set Speed 3", "Set Speed 4",
+		"Set Speed 5", "Set Speed 6", "Set Speed 7", "Set Speed 8", "Set Speed 9"
+	};
+	const char *debug_binds[] = {
+		"ui.enter_debugger", "ui.vdp_debug_mode", "ui.vdp_debug_pal"
+	};
+	const char *debug_names[] = {
+		"Enter Debugger", "VDP Debug Mode", "Debug Palette"
+	};
+	const uint32_t NUM_C1_BINDS = sizeof(controller1_binds)/sizeof(*controller1_binds);
+	const uint32_t NUM_C2_BINDS = sizeof(controller2_binds)/sizeof(*controller2_binds);
+	const uint32_t NUM_SPEED_BINDS = sizeof(speed_binds)/sizeof(*speed_binds);
+	const uint32_t NUM_GEN_BINDS = sizeof(general_binds)/sizeof(*general_binds);
+	const uint32_t NUM_DBG_BINDS = sizeof(debug_binds)/sizeof(*debug_binds);
+	static tern_node *binding_lookup;
+	if (!binding_lookup) {
+		tern_node *bindings = tern_find_path(config, "bindings\0keys\0", TVAL_NODE).ptrval;
+		if (bindings) {
+			tern_foreach(bindings, binding_loop, &binding_lookup);
+		}
+	}
+	uint32_t width = render_width();
+	uint32_t height = render_height();
+	if (nk_begin(context, "Keyboard Bindings", nk_rect(0, 0, width, height), 0)) {
+		binding_group(context, "Controller 1", controller1_binds, NULL, NUM_C1_BINDS, binding_lookup);
+		binding_group(context, "Controller 2", controller2_binds, NULL, NUM_C2_BINDS, binding_lookup);
+		binding_group(context, "General", general_binds, general_names, NUM_GEN_BINDS, binding_lookup);
+		binding_group(context, "Speed Control", speed_binds, speed_names, NUM_SPEED_BINDS, binding_lookup);
+		binding_group(context, "Debug", debug_binds, debug_names, NUM_DBG_BINDS, binding_lookup);
+		nk_layout_row_static(context, context->style.font->height * 1.1333, (render_width() - 80) / 2, 1);
+		if (nk_button_label(context, "Back")) {
+			pop_view();
+		}
+		nk_end(context);
+	}
+	if (set_binding && nk_begin(context, "Set Binding", nk_rect(width/4, height/4, width/2/*width*3/4*/, height/2), NK_WINDOW_TITLE | NK_WINDOW_BORDER)) {
+		nk_layout_row_static(context, 30, width/2-30, 1);
+		nk_label(context, "Press new key for", NK_TEXT_CENTERED);
+		nk_label(context, set_label, NK_TEXT_CENTERED);
+		if (nk_button_label(context, "Cancel")) {
+			free(set_label);
+			set_binding = set_label = NULL;
+		} else if (keycode) {
+			char *name = get_key_name(keycode);
+			if (name) {
+				uint32_t prefix_len = strlen("bindings") + strlen("keys") + 2;
+				char * old = tern_find_ptr(binding_lookup, set_binding);
+				if (old) {
+					uint32_t suffix_len = strlen(old) + 1;
+					char *old_path = malloc(prefix_len + suffix_len + 1);
+					memcpy(old_path, "bindings\0keys\0", prefix_len);
+					memcpy(old_path + prefix_len, old, suffix_len);
+					old_path[prefix_len + suffix_len] = 0;
+					tern_val old_val;
+					if (tern_delete_path(&config, old_path, &old_val) == TVAL_PTR) {
+						free(old_val.ptrval);
+					}
+				}
+				uint32_t suffix_len = strlen(name) + 1;
+				char *path = malloc(prefix_len + suffix_len + 1);
+				memcpy(path, "bindings\0keys\0", prefix_len);
+				memcpy(path + prefix_len, name, suffix_len);
+				path[prefix_len + suffix_len] = 0;
+				
+				config_dirty = 1;
+				config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(set_binding)}, TVAL_PTR);
+				free(path);
+				free(name);
+				tern_free(binding_lookup);
+				binding_lookup = NULL;
+			}
+			free(set_label);
+			set_binding = set_label = NULL;
+		}
+		nk_end(context);
+	}
+}
+
+static int selected_controller;
+static controller_info selected_controller_info;
+//#define MIN_BIND_BOX_WIDTH 140
+#define MAX_BIND_BOX_WIDTH 350
+
+#define AXIS       0x40000000
+#define STICKDIR   0x30000000
+#define LEFTSTICK  0x10000000
+#define RIGHTSTICK 0x20000000
+enum {
+	UP,DOWN,RIGHT,LEFT,NUM_AXIS_DIRS
+};
+
+static char * config_ps_names[] = {
+	[SDL_CONTROLLER_BUTTON_A] = "cross",
+	[SDL_CONTROLLER_BUTTON_B] = "circle",
+	[SDL_CONTROLLER_BUTTON_X] = "square",
+	[SDL_CONTROLLER_BUTTON_Y] = "triangle",
+	[SDL_CONTROLLER_BUTTON_BACK] = "share",
+	[SDL_CONTROLLER_BUTTON_START] = "options",
+	[SDL_CONTROLLER_BUTTON_LEFTSHOULDER] = "l1",
+	[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] = "r1",
+	[SDL_CONTROLLER_BUTTON_LEFTSTICK] = "l3",
+	[SDL_CONTROLLER_BUTTON_RIGHTSTICK] = "r3",
+};
+
+typedef struct {	
+	const char *button_binds[SDL_CONTROLLER_BUTTON_MAX];
+	const char *left_stick[NUM_AXIS_DIRS];
+	const char *right_stick[NUM_AXIS_DIRS];
+	const char *triggers[2];
+} pad_bind_config;
+
+static const char **current_bind_dest;
+
+const char *translate_binding_option(const char *option)
+{
+	static tern_node *conf_names;
+	if (!conf_names) {
+		conf_names = tern_insert_ptr(conf_names, "gamepads.n.up", "Pad Up");
+		conf_names = tern_insert_ptr(conf_names, "gamepads.n.down", "Pad Down");
+		conf_names = tern_insert_ptr(conf_names, "gamepads.n.left", "Pad Left");
+		conf_names = tern_insert_ptr(conf_names, "gamepads.n.right", "Pad Right");
+		conf_names = tern_insert_ptr(conf_names, "gamepads.n.a", "Pad A");
+		conf_names = tern_insert_ptr(conf_names, "gamepads.n.b", "Pad B");
+		conf_names = tern_insert_ptr(conf_names, "gamepads.n.c", "Pad C");
+		conf_names = tern_insert_ptr(conf_names, "gamepads.n.x", "Pad X");
+		conf_names = tern_insert_ptr(conf_names, "gamepads.n.y", "Pad Y");
+		conf_names = tern_insert_ptr(conf_names, "gamepads.n.z", "Pad Z");
+		conf_names = tern_insert_ptr(conf_names, "gamepads.n.start", "Pad Start");
+		conf_names = tern_insert_ptr(conf_names, "gamepads.n.mode", "Pad Mode");
+		conf_names = tern_insert_ptr(conf_names, "ui.release_mouse", "Release Mouse");
+		conf_names = tern_insert_ptr(conf_names, "ui.vdp_debug_mode", "VDP Debug Mode");
+		conf_names = tern_insert_ptr(conf_names, "ui.vdp_debug_pal", "VDP Debug Palette");
+		conf_names = tern_insert_ptr(conf_names, "ui.enter_debugger", "Enter CPU Debugger");
+		conf_names = tern_insert_ptr(conf_names, "ui.screenshot", "Take Screenshot");
+		conf_names = tern_insert_ptr(conf_names, "ui.exit", "Show Menu");
+		conf_names = tern_insert_ptr(conf_names, "ui.save_state", "Quick Save");
+		conf_names = tern_insert_ptr(conf_names, "ui.set_speed.0", "Set Speed 0");
+		conf_names = tern_insert_ptr(conf_names, "ui.set_speed.1", "Set Speed 1");
+		conf_names = tern_insert_ptr(conf_names, "ui.set_speed.2", "Set Speed 2");
+		conf_names = tern_insert_ptr(conf_names, "ui.set_speed.3", "Set Speed 3");
+		conf_names = tern_insert_ptr(conf_names, "ui.set_speed.4", "Set Speed 4");
+		conf_names = tern_insert_ptr(conf_names, "ui.set_speed.5", "Set Speed 5");
+		conf_names = tern_insert_ptr(conf_names, "ui.set_speed.6", "Set Speed 6");
+		conf_names = tern_insert_ptr(conf_names, "ui.set_speed.7", "Set Speed 7");
+		conf_names = tern_insert_ptr(conf_names, "ui.set_speed.8", "Set Speed 8");
+		conf_names = tern_insert_ptr(conf_names, "ui.set_speed.9", "Set Speed 9");
+		conf_names = tern_insert_ptr(conf_names, "ui.next_speed", "Next Speed");
+		conf_names = tern_insert_ptr(conf_names, "ui.prev_speed", "Prev. Speed");
+		conf_names = tern_insert_ptr(conf_names, "ui.toggle_fullscreen", "Toggle Fullscreen");
+		conf_names = tern_insert_ptr(conf_names, "ui.soft_reset", "Soft Reset");
+		conf_names = tern_insert_ptr(conf_names, "ui.reload", "Reload ROM");
+		conf_names = tern_insert_ptr(conf_names, "ui.sms_pause", "SMS Pause");
+		conf_names = tern_insert_ptr(conf_names, "ui.toggle_keyboard_captured", "Toggle Keyboard Capture");
+	}
+	return tern_find_ptr_default(conf_names, option, (void *)option);
+}
+
+static uint8_t controller_binding_changed;
+static void bind_option_group(struct nk_context *context, char *name, const char **options, uint32_t num_options)
+{
+	float margin = context->style.font->height * 2;
+	nk_layout_row_static(context, (context->style.font->height + 3) * ((num_options + 2) / 3) + context->style.font->height*2.1, render_width() - margin, 1);
+	if (nk_group_begin(context, name, NK_WINDOW_TITLE|NK_WINDOW_NO_SCROLLBAR)) {
+		nk_layout_row_static(context, context->style.font->height, (render_width() - margin - context->style.font->height) / 3, 3);
+		for (int i = 0; i < num_options; i++)
+		{
+			if (nk_button_label(context, translate_binding_option(options[i]))) {
+				*current_bind_dest = options[i];
+				controller_binding_changed = 1;
+				pop_view();
+			}
+		}
+		nk_group_end(context);
+	}
+}
+
+static void view_button_binding(struct nk_context *context)
+{
+	static const char *pad_opts[] = {
+		"gamepads.n.up",
+		"gamepads.n.down",
+		"gamepads.n.left",
+		"gamepads.n.right",
+		"gamepads.n.a",
+		"gamepads.n.b",
+		"gamepads.n.c",
+		"gamepads.n.x",
+		"gamepads.n.y",
+		"gamepads.n.z",
+		"gamepads.n.start",
+		"gamepads.n.mode"
+	};
+	static const char *system_buttons[] = {
+		"ui.soft_reset",
+		"ui.reload",
+		"ui.sms_pause"
+	};
+	static const char *emu_control[] = {
+		"ui.save_state",
+		"ui.exit",
+		"ui.toggle_fullscreen",
+		"ui.screenshot",
+		"ui.release_mouse",
+		"ui.toggle_keyboard_captured"
+	};
+	static const char *debugger[] = {
+		"ui.vdp_debug_mode",
+		"ui.vdp_debug_pal",
+		"ui.enter_debugger"
+	};
+	static const char *speeds[] = {
+		"ui.next_speed",
+		"ui.prev_speed",
+		"ui.set_speed.0",
+		"ui.set_speed.1",
+		"ui.set_speed.2",
+		"ui.set_speed.3",
+		"ui.set_speed.4",
+		"ui.set_speed.5",
+		"ui.set_speed.6",
+		"ui.set_speed.7",
+		"ui.set_speed.8",
+		"ui.set_speed.9"
+	};
+		
+	if (nk_begin(context, "Button Binding", nk_rect(0, 0, render_width(), render_height()), 0)) {
+		bind_option_group(context, "Controller Buttons", pad_opts, sizeof(pad_opts)/sizeof(*pad_opts));
+		bind_option_group(context, "System Buttons", system_buttons, sizeof(system_buttons)/sizeof(*system_buttons));
+		bind_option_group(context, "Emulator Control", emu_control, sizeof(emu_control)/sizeof(*emu_control));
+		bind_option_group(context, "Debugging", debugger, sizeof(debugger)/sizeof(*debugger));
+		bind_option_group(context, "Speed Control", speeds, sizeof(speeds)/sizeof(*speeds));
+		
+		nk_layout_row_static(context, context->style.font->height, (render_width() - 80)/4, 1);
+		if (nk_button_label(context, "Back")) {
+			pop_view();
+		}
+		nk_end(context);
+	}
+}
+
+static void binding_box(struct nk_context *context, pad_bind_config *bindings, char *name, float x, float y, float width, int num_binds, int *binds)
+{
+	const struct nk_user_font *font = context->style.font;
+	float row_height = font->height * 2;
+	
+	char const **labels = calloc(sizeof(char *), num_binds);
+	char const ***conf_vals = calloc(sizeof(char *), num_binds);
+	float max_width = 0.0f;
+	
+	int skipped = 0;
+	for (int i = 0; i < num_binds; i++)
+	{
+		if (binds[i] & AXIS) {
+			labels[i] = get_axis_label(&selected_controller_info, binds[i] & ~AXIS);
+			conf_vals[i] = &bindings->triggers[(binds[i] & ~AXIS) - SDL_CONTROLLER_AXIS_TRIGGERLEFT];
+		} else if (binds[i] & STICKDIR) {
+			static char const * dirs[] = {"Up", "Down", "Right", "Left"};
+			labels[i] = dirs[binds[i] & 3];
+			conf_vals[i] = &(binds[i] & LEFTSTICK ? bindings->left_stick : bindings->right_stick)[binds[i] & 3];
+		} else {
+			labels[i] = get_button_label(&selected_controller_info, binds[i]);
+			conf_vals[i] = &bindings->button_binds[binds[i]];
+		}
+		if (!labels[i]) {
+			skipped++;
+			continue;
+		}
+		float lb_width = font->width(font->userdata, font->height, labels[i], strlen(labels[i]));
+		max_width = max_width < lb_width ? lb_width : max_width;
+	}
+	nk_layout_space_push(context, nk_rect(x, y, width, (num_binds - skipped) * (row_height + 4) + 4));
+	nk_group_begin(context, name, NK_WINDOW_BORDER | NK_WINDOW_NO_SCROLLBAR);
+	
+	float widths[] = {max_width + 3, width - (max_width + 6)};
+	nk_layout_row(context, NK_STATIC, row_height, 2, widths);
+	for (int i = 0; i < num_binds; i++)
+	{
+		if (!labels[i]) {
+			continue;
+		}
+		nk_label(context, labels[i], NK_TEXT_LEFT);
+		const char *name = *conf_vals[i] ? translate_binding_option(*conf_vals[i]) : "None";
+		if (nk_button_label(context, name)) {
+			current_bind_dest = conf_vals[i];
+			push_view(view_button_binding);
+		}
+	}
+	free(labels);
+	free(conf_vals);
+	nk_group_end(context);
+}
+
+static void button_iter(char *key, tern_val val, uint8_t valtype, void *data)
+{
+	pad_bind_config *bindings = data;
+	if (valtype != TVAL_PTR) {
+		return;
+	}
+	int button = render_lookup_button(key);
+	if (button != SDL_CONTROLLER_BUTTON_INVALID) {
+		bindings->button_binds[button] = val.ptrval;
+	}
+}
+
+static void axis_iter(char *key, tern_val val, uint8_t valtype, void *data)
+{
+	pad_bind_config *bindings = data;
+	if (valtype != TVAL_PTR) {
+		return;
+	}
+	int axis;
+	uint8_t is_negative = 0;
+	char *period = strchr(key, '.');
+	if (period) {
+		char *tmp = malloc(period-key + 1);
+		memcpy(tmp, key, period-key);
+		tmp[period-key] = 0;
+		axis = render_lookup_axis(tmp);
+		free(tmp);
+		is_negative = strcmp(period+1, "negative") == 0;
+	} else {
+		axis = render_lookup_axis(key);
+	}
+	switch (axis)
+	{
+	case SDL_CONTROLLER_AXIS_LEFTX:
+	case SDL_CONTROLLER_AXIS_LEFTY:
+		bindings->left_stick[(axis - SDL_CONTROLLER_AXIS_LEFTX) * 2 + is_negative] = val.ptrval;
+		break;
+	case SDL_CONTROLLER_AXIS_RIGHTX:
+	case SDL_CONTROLLER_AXIS_RIGHTY:
+		bindings->right_stick[(axis - SDL_CONTROLLER_AXIS_RIGHTX) * 2 + is_negative] = val.ptrval;
+		break;
+	case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
+	case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
+		bindings->triggers[axis-SDL_CONTROLLER_AXIS_TRIGGERLEFT] = val.ptrval;
+		break;
+	}
+}
+
+enum {
+	SIMILAR_CONTROLLERS,
+	IDENTICAL_CONTROLLERS,
+	BY_INDEX,
+	DEFAULT,
+	NUM_DEST_TYPES
+};
+
+//it would be cleaner to generate this algorithmically for 4th and up,
+//but BlastEm only supports 8 controllers currently so it's not worth the effort
+static const char *by_index_names[] = {
+	"Use for 1st controller",
+	"Use for 2nd controller",
+	"Use for 3rd controller",
+	"Use for 4th controller",
+	"Use for 5th controller",
+	"Use for 6th controller",
+	"Use for 7th controller",
+	"Use for 8th controller",
+};
+
+static void save_stick_binds(char *axes_key, size_t axes_key_size, const char **bindings, char *prefix)
+{
+	for (int i = 0; i < NUM_AXIS_DIRS; i++)
+	{
+		char axis = (i / 2) ? 'x' : 'y';
+		char *suffix = (i % 2) ? ".negative" : ".positive";
+		size_t prefix_len = strlen(prefix), suffix_len = strlen(suffix);
+		size_t full_key_size = axes_key_size + prefix_len + 1 + suffix_len + 2;
+		char *full_key = malloc(full_key_size);
+		memcpy(full_key, axes_key, axes_key_size);
+		memcpy(full_key + axes_key_size, prefix, prefix_len);
+		full_key[axes_key_size+prefix_len] = axis;
+		memcpy(full_key + axes_key_size + prefix_len + 1, suffix, suffix_len  +1);
+		full_key[axes_key_size + prefix_len + 1 + suffix_len + 1] = 0;
+		
+		if (bindings[i]) {
+			tern_insert_path(config, full_key, (tern_val){.ptrval = strdup(bindings[i])}, TVAL_PTR);
+		} else {
+			tern_val prev_val;
+			uint8_t prev_type = tern_delete_path(&config, full_key, &prev_val);
+			if (prev_type == TVAL_PTR) {
+				free(prev_val.ptrval);
+			}
+		}
+		
+		free(full_key);
+	}
+}
+
+static pad_bind_config *bindings;
+static void handle_dest_clicked(uint32_t dest)
+{
+	char key_buf[12];
+	char *key;
+	switch (dest)
+	{
+	case SIMILAR_CONTROLLERS:
+		key = make_controller_type_key(&selected_controller_info);
+		break;
+	case IDENTICAL_CONTROLLERS:
+		key = render_joystick_type_id(selected_controller);
+		break;
+	case BY_INDEX:
+		snprintf(key_buf, sizeof(key_buf), "%d", selected_controller);
+		key = key_buf;
+		break;
+	default:
+		key = "default";
+		break;
+	}
+	static const char base_path[] = "bindings\0pads";
+	size_t pad_key_size = sizeof(base_path) + strlen(key) + 1;
+	char *pad_key = malloc(pad_key_size);
+	memcpy(pad_key, base_path, sizeof(base_path));
+	strcpy(pad_key + sizeof(base_path), key);
+	static const char dpad_base[] = "dpads\0""0";
+	size_t dpad_key_size = pad_key_size + sizeof(dpad_base);
+	char *dpad_key = malloc(dpad_key_size);
+	memcpy(dpad_key, pad_key, pad_key_size);
+	memcpy(dpad_key + pad_key_size, dpad_base, sizeof(dpad_base));
+	static const char button_base[] = "buttons";
+	size_t button_key_size = pad_key_size + sizeof(button_base);
+	char *button_key = malloc(button_key_size);
+	memcpy(button_key, pad_key, pad_key_size);
+	memcpy(button_key + pad_key_size, button_base, sizeof(button_base));
+	
+	char *final_key;
+	for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
+	{
+		char *base;
+		const char *suffix;
+		size_t base_key_len;
+		if ( i < SDL_CONTROLLER_BUTTON_DPAD_UP) {
+			suffix = SDL_GameControllerGetStringForButton(i);
+			base_key_len = button_key_size;
+			base = button_key;
+			
+			
+		} else {
+			static const char *dir_keys[] = {"up", "down", "left", "right"};
+			suffix = dir_keys[i - SDL_CONTROLLER_BUTTON_DPAD_UP];
+			base = dpad_key;
+			base_key_len = dpad_key_size;
+		}
+		size_t suffix_len = strlen(suffix);
+		final_key = malloc(base_key_len + suffix_len + 2);
+		memcpy(final_key, base, base_key_len);
+		memcpy(final_key + base_key_len, suffix, suffix_len + 1);
+		final_key[base_key_len + suffix_len + 1] = 0;
+		if (bindings->button_binds[i]) {
+			tern_insert_path(config, final_key, (tern_val){.ptrval = strdup(bindings->button_binds[i])}, TVAL_PTR);
+		} else {
+			tern_val prev_val;
+			uint8_t prev_type = tern_delete_path(&config, final_key, &prev_val);
+			if (prev_type == TVAL_PTR) {
+				free(prev_val.ptrval);
+			}
+		}
+		free(final_key);
+	}
+	free(button_key);
+	free(dpad_key);
+	
+	static const char axes_base[] = "axes";
+	size_t axes_key_size = pad_key_size + sizeof(axes_base);
+	char *axes_key = malloc(axes_key_size);
+	memcpy(axes_key, pad_key, pad_key_size);
+	memcpy(axes_key + pad_key_size, axes_base, sizeof(axes_base));
+	
+	save_stick_binds(axes_key, axes_key_size,bindings->left_stick, "left");
+	save_stick_binds(axes_key, axes_key_size,bindings->right_stick, "right");
+	for (int i = SDL_CONTROLLER_AXIS_TRIGGERLEFT; i < SDL_CONTROLLER_AXIS_MAX; i++)
+	{
+		const char *suffix = SDL_GameControllerGetStringForAxis(i);
+		size_t suffix_len = strlen(suffix);
+		final_key = malloc(axes_key_size + suffix_len + 2);
+		memcpy(final_key, axes_key, axes_key_size);
+		memcpy(final_key + axes_key_size, suffix, suffix_len + 1);
+		final_key[axes_key_size + suffix_len + 1] = 0;
+		if (bindings->triggers[i - SDL_CONTROLLER_AXIS_TRIGGERLEFT]) {
+			tern_insert_path(config, final_key, (tern_val){.ptrval = strdup(bindings->triggers[i - SDL_CONTROLLER_AXIS_TRIGGERLEFT])}, TVAL_PTR);
+		} else {
+			tern_val prev_val;
+			uint8_t prev_type = tern_delete_path(&config, final_key, &prev_val);
+			if (prev_type == TVAL_PTR) {
+				free(prev_val.ptrval);
+			}
+		}
+		free(final_key);
+	}
+	free(axes_key);
+	
+	free(pad_key);
+	if (dest == SIMILAR_CONTROLLERS) {
+		free(key);
+	}
+	pop_view();
+	config_dirty = 1;
+}
+
+void view_select_binding_dest(struct nk_context *context)
+{
+	static menu_item options[NUM_DEST_TYPES];
+	options[IDENTICAL_CONTROLLERS].title = "Use for identical controllers";
+	options[DEFAULT].title = "Use as default";
+	options[BY_INDEX].title = by_index_names[selected_controller];
+	options[SIMILAR_CONTROLLERS].title = make_human_readable_type_name(&selected_controller_info);
+	
+	if (nk_begin(context, "Select Binding Dest", nk_rect(0, 0, render_width(), render_height()), NK_WINDOW_NO_SCROLLBAR)) {
+		menu(context, NUM_DEST_TYPES, options, handle_dest_clicked);
+		nk_end(context);
+	}
+	free((char *)options[SIMILAR_CONTROLLERS].title);
+}
+
+static ui_image *select_best_image(controller_info *info)
+{
+	if (info->variant != VARIANT_NORMAL) {
+		return controller_ps4_6b;
+	} else if (info->type == TYPE_PSX) {
+		return controller_ps4;
+	} else {
+		return controller_360;
+	}
+}
+
+void view_controller_bindings(struct nk_context *context)
+{
+	if (nk_begin(context, "Controller Bindings", nk_rect(0, 0, render_width(), render_height()), NK_WINDOW_NO_SCROLLBAR)) {
+		if (!bindings) {
+			bindings = calloc(1, sizeof(*bindings));
+			tern_node *pad = get_binding_node_for_pad(selected_controller);
+			if (pad) {
+				tern_foreach(tern_find_node(pad, "buttons"), button_iter, bindings);
+				tern_foreach(tern_find_node(pad, "axes"), axis_iter, bindings);
+				tern_node *dpad = tern_find_path(pad, "dpads\0" "0\0", TVAL_NODE).ptrval;
+				const char *dir_keys[] = {"up", "down", "right", "left"};
+				const int button_idx[] = {SDL_CONTROLLER_BUTTON_DPAD_UP, SDL_CONTROLLER_BUTTON_DPAD_DOWN, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, SDL_CONTROLLER_BUTTON_DPAD_LEFT};
+				for (int i = 0; i < NUM_AXIS_DIRS; i++)
+				{
+					bindings->button_binds[button_idx[i]] = tern_find_ptr(dpad, dir_keys[i]);
+				}
+			}
+		}
+	
+		float orig_height = def_font->handle.height;
+		def_font->handle.height *= 0.5f;
+		
+		uint32_t avail_height = render_height() - 2 * orig_height;
+		float desired_width = render_width() * 0.5f, desired_height = avail_height * 0.5f;
+		ui_image *controller_image = select_best_image(&selected_controller_info);
+		
+		float controller_ratio = (float)controller_image->width / (float)controller_image->height;
+		
+		const struct nk_user_font *font = context->style.font;
+		int MIN_BIND_BOX_WIDTH = font->width(font->userdata, font->height, "Right", strlen("Right"))
+			+ def_font->handle.width(font->userdata, font->height, "Internal Screenshot", strlen("Internal Screenshot"));
+		
+		if (render_width() - desired_width < 2.5f*MIN_BIND_BOX_WIDTH) {
+			desired_width = render_width() - 2.5f*MIN_BIND_BOX_WIDTH;
+		}
+		
+		if (desired_width / desired_height > controller_ratio) {
+			desired_width = desired_height * controller_ratio;
+		} else {
+			desired_height = desired_width / controller_ratio;
+		}
+		float img_left = render_width() / 2.0f - desired_width / 2.0f;
+		float img_top = avail_height / 2.0f - desired_height / 2.0f;
+		float img_right = img_left + desired_width;
+		float img_bot = img_top + desired_height;
+		nk_layout_space_begin(context, NK_STATIC, avail_height, INT_MAX);
+		nk_layout_space_push(context, nk_rect(img_left, img_top, desired_width, desired_height));
+		nk_image(context, controller_image->ui);
+		
+		float bind_box_width = (render_width() - img_right) * 0.8f;
+		if (bind_box_width < MIN_BIND_BOX_WIDTH) {
+			bind_box_width = render_width() - img_right;
+			if (bind_box_width > MIN_BIND_BOX_WIDTH) {
+				bind_box_width = MIN_BIND_BOX_WIDTH;
+			}
+		} else if (bind_box_width > MAX_BIND_BOX_WIDTH) {
+			bind_box_width = MAX_BIND_BOX_WIDTH;
+		}
+		float bind_box_left;
+		if (bind_box_width >= (render_width() - img_right)) {
+			bind_box_left = img_right;
+		} else {
+			bind_box_left = img_right + (render_width() - img_right) / 2.0f - bind_box_width / 2.0f;
+		}
+		
+		if (selected_controller_info.variant == VARIANT_NORMAL) {
+			binding_box(context, bindings, "Action Buttons", bind_box_left, img_top, bind_box_width, 4, (int[]){
+				SDL_CONTROLLER_BUTTON_A,
+				SDL_CONTROLLER_BUTTON_B,
+				SDL_CONTROLLER_BUTTON_X,
+				SDL_CONTROLLER_BUTTON_Y
+			});
+		} else {
+			binding_box(context, bindings, "Action Buttons", bind_box_left, img_top, bind_box_width, 6, (int[]){
+				SDL_CONTROLLER_BUTTON_A,
+				SDL_CONTROLLER_BUTTON_B,
+				selected_controller_info.variant == VARIANT_6B_RIGHT ? AXIS | SDL_CONTROLLER_AXIS_TRIGGERRIGHT : SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
+				SDL_CONTROLLER_BUTTON_X,
+				SDL_CONTROLLER_BUTTON_Y,
+				selected_controller_info.variant == VARIANT_6B_RIGHT ? SDL_CONTROLLER_BUTTON_RIGHTSHOULDER : SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
+			});
+		}
+		
+		binding_box(context, bindings, "Right Shoulder", bind_box_left, font->height/2, bind_box_width,
+			selected_controller_info.variant == VARIANT_6B_BUMPERS ? 1 : 2, 
+			(int[]){
+			selected_controller_info.variant == VARIANT_6B_RIGHT ? SDL_CONTROLLER_BUTTON_LEFTSHOULDER : SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
+			selected_controller_info.variant == VARIANT_6B_RIGHT ? SDL_CONTROLLER_BUTTON_RIGHTSHOULDER : AXIS | SDL_CONTROLLER_AXIS_TRIGGERRIGHT
+		});
+		
+		binding_box(context, bindings, "Misc Buttons", (render_width() - bind_box_width) / 2, font->height/2, bind_box_width, 3, (int[]){
+			SDL_CONTROLLER_BUTTON_BACK,
+			SDL_CONTROLLER_BUTTON_GUIDE,
+			SDL_CONTROLLER_BUTTON_START
+		});
+		
+		if (selected_controller_info.variant == VARIANT_NORMAL)
+		{
+			binding_box(context, bindings, "Right Stick", img_right - desired_width/3, img_bot, bind_box_width, 5, (int[]){
+				RIGHTSTICK | UP,
+				RIGHTSTICK | DOWN,
+				RIGHTSTICK | LEFT,
+				RIGHTSTICK | RIGHT,
+				SDL_CONTROLLER_BUTTON_RIGHTSTICK
+			});
+		}
+		
+		bind_box_left -= img_right;
+		float dpad_left, dpad_top;
+		if (selected_controller_info.variant == VARIANT_NORMAL)
+		{
+			binding_box(context, bindings, "Left Stick", bind_box_left, img_top, bind_box_width, 5, (int[]){
+				LEFTSTICK | UP,
+				LEFTSTICK | DOWN,
+				LEFTSTICK | LEFT,
+				LEFTSTICK | RIGHT,
+				SDL_CONTROLLER_BUTTON_LEFTSTICK
+			});
+			dpad_left = img_left - desired_width/6;
+			dpad_top = img_bot + font->height * 1.5;
+		} else {
+			dpad_left = bind_box_left;
+			dpad_top = img_top;
+		}
+		
+		binding_box(context, bindings, "Left Shoulder", bind_box_left, font->height/2, bind_box_width, 
+			selected_controller_info.variant == VARIANT_6B_BUMPERS ? 1 : 2, 
+			(int[]){
+			selected_controller_info.variant == VARIANT_6B_RIGHT ? SDL_CONTROLLER_BUTTON_LEFTSTICK : SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
+			selected_controller_info.variant == VARIANT_6B_RIGHT ? SDL_CONTROLLER_BUTTON_RIGHTSTICK : AXIS | SDL_CONTROLLER_AXIS_TRIGGERLEFT
+		});
+		
+		binding_box(context, bindings, "D-pad", dpad_left, dpad_top, bind_box_width, 4, (int[]){
+			SDL_CONTROLLER_BUTTON_DPAD_UP,
+			SDL_CONTROLLER_BUTTON_DPAD_DOWN,
+			SDL_CONTROLLER_BUTTON_DPAD_LEFT,
+			SDL_CONTROLLER_BUTTON_DPAD_RIGHT
+		});
+		
+		nk_layout_space_end(context);
+		
+		def_font->handle.height = orig_height;
+		nk_layout_row_static(context, orig_height + 4, (render_width() - 2*orig_height) / 4, 1);
+		if (nk_button_label(context, "Back")) {
+			pop_view();
+			if (controller_binding_changed) {
+				push_view(view_select_binding_dest);
+			}
+		}
+		nk_end(context);
+	}
+}
+
+static int current_button;
+static int current_axis;
+static int button_pressed, last_button;
+static int hat_moved, hat_value, last_hat, last_hat_value;
+static int axis_moved, axis_value, last_axis;
+static char *mapping_string;
+static size_t mapping_pos;
+
+static void start_mapping(void)
+{
+	const char *name;
+	mapping_string[mapping_pos++] = ',';
+	if (current_button != SDL_CONTROLLER_BUTTON_MAX) {
+		name = SDL_GameControllerGetStringForButton(current_button);
+	} else {
+		name = SDL_GameControllerGetStringForAxis(current_axis);
+	}
+	size_t namesz = strlen(name);
+	memcpy(mapping_string + mapping_pos, name, namesz);
+	mapping_pos += namesz;
+	mapping_string[mapping_pos++] = ':';
+}
+
+#define QUIET_FRAMES 9
+static void view_controller_mappings(struct nk_context *context)
+{
+	char buffer[512];
+	static int quiet;
+	uint8_t added_mapping = 0;
+	if (nk_begin(context, "Controllers", nk_rect(0, 0, render_width(), render_height()), NK_WINDOW_NO_SCROLLBAR)) {
+		nk_layout_row_static(context, render_height() - context->style.font->height, render_width() - context->style.font->height, 1);
+		if (current_button < SDL_CONTROLLER_BUTTON_MAX) {
+			snprintf(buffer, sizeof(buffer), "Press Button %s", get_button_label(&selected_controller_info, current_button));
+		} else {
+			snprintf(buffer, sizeof(buffer), "Move Axis %s", get_axis_label(&selected_controller_info, current_axis));
+		}
+		nk_label(context, buffer, NK_TEXT_CENTERED);
+		if (quiet) {
+			--quiet;
+		} else {
+			if (button_pressed >= 0 && button_pressed != last_button) {
+				start_mapping();
+				mapping_string[mapping_pos++] = 'b';
+				if (button_pressed > 9) {
+					mapping_string[mapping_pos++] = '0' + button_pressed / 10;
+				}
+				mapping_string[mapping_pos++] = '0' + button_pressed % 10;
+				added_mapping = 1;
+				last_button = button_pressed;
+			} else if (hat_moved >= 0 && hat_value && (hat_moved != last_hat || hat_value != last_hat_value)) {
+				start_mapping();
+				mapping_string[mapping_pos++] = 'h';
+				mapping_string[mapping_pos++] = '0' + hat_moved;
+				mapping_string[mapping_pos++] = '.';
+				mapping_string[mapping_pos++] = '0' + hat_value;
+				added_mapping = 1;
+				
+				last_hat = hat_moved;
+				last_hat_value = hat_value;
+			} else if (axis_moved >= 0 && abs(axis_value) > 1000 && axis_moved != last_axis) {
+				start_mapping();
+				mapping_string[mapping_pos++] = 'a';
+				if (axis_moved > 9) {
+					mapping_string[mapping_pos++] = '0' + axis_moved / 10;
+				}
+				mapping_string[mapping_pos++] = '0' + axis_moved % 10;
+				added_mapping = 1;
+				last_axis = axis_moved;
+			}
+		}
+			
+		if (added_mapping) {
+			quiet = QUIET_FRAMES;
+			if (current_button < SDL_CONTROLLER_BUTTON_MAX) {
+				current_button++;
+				if (current_button == SDL_CONTROLLER_BUTTON_MAX) {
+					current_axis = 0;
+				}
+			} else {
+				current_axis++;
+				if (current_axis == SDL_CONTROLLER_AXIS_MAX) {
+					mapping_string[mapping_pos] = 0;
+					save_controller_mapping(selected_controller, mapping_string);
+					free(mapping_string);
+					pop_view();
+					push_view(view_controller_bindings);
+					controller_binding_changed = 0;
+				}
+			}
+		}
+		button_pressed = -1;
+		hat_moved = -1;
+		axis_moved = -1;
+		nk_end(context);
+	}
+}
+
+static void view_controller_variant(struct nk_context *context)
+{
+	uint8_t selected = 0;
+	if (nk_begin(context, "Controller Type", nk_rect(0, 0, render_width(), render_height()), 0)) {
+		nk_layout_row_static(context, context->style.font->height*1.25, render_width() - context->style.font->height * 2, 1);
+		nk_label(context, "", NK_TEXT_CENTERED);
+		nk_label(context, "Select the layout that", NK_TEXT_CENTERED);
+		nk_label(context, "best matches your controller", NK_TEXT_CENTERED);
+		nk_label(context, "", NK_TEXT_CENTERED);
+		if (nk_button_label(context, "4 face buttons")) {
+			selected_controller_info.variant = VARIANT_NORMAL;
+			selected = 1;
+		}
+		char buffer[512];
+		snprintf(buffer, sizeof(buffer), "6 face buttons including %s and %s", 
+			get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), 
+			get_axis_label(&selected_controller_info, SDL_CONTROLLER_AXIS_TRIGGERRIGHT)
+		);
+		if (nk_button_label(context, buffer)) {
+			selected_controller_info.variant = VARIANT_6B_RIGHT;
+			selected = 1;
+		}
+		snprintf(buffer, sizeof(buffer), "6 face buttons including %s and %s", 
+			get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_LEFTSHOULDER), 
+			get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
+		);
+		if (nk_button_label(context, buffer)) {
+			selected_controller_info.variant = VARIANT_6B_BUMPERS;
+			selected = 1;
+		}
+		nk_end(context);
+	}
+	if (selected) {
+		save_controller_info(selected_controller, &selected_controller_info);
+		pop_view();
+		SDL_GameController *controller = render_get_controller(selected_controller);
+		if (controller) {
+			push_view(view_controller_bindings);
+			controller_binding_changed = 0;
+			SDL_GameControllerClose(controller);
+		} else {
+			current_button = SDL_CONTROLLER_BUTTON_A;
+			button_pressed = -1;
+			last_button = -1;
+			last_hat = -1;
+			axis_moved = -1;
+			last_axis = -1;
+			SDL_Joystick *joy = render_get_joystick(selected_controller);
+			const char *name = SDL_JoystickName(joy);
+			size_t namesz = strlen(name);
+			mapping_string = malloc(512 + namesz);
+			for (mapping_pos = 0; mapping_pos < namesz; mapping_pos++)
+			{
+				char c = name[mapping_pos];
+				if (c == ',' || c == '\n' || c == '\r') {
+					c = ' ';
+				}
+				mapping_string[mapping_pos] = c;
+			}
+			
+			push_view(view_controller_mappings);
+		}
+	}
+}
+
+static void controller_type_group(struct nk_context *context, char *name, int type_id, int first_subtype_id, const char **types, uint32_t num_types)
+{
+	nk_layout_row_static(context, (context->style.font->height + 3) * num_types + context->style.font->height, render_width() - 80, 1);
+	if (nk_group_begin(context, name, NK_WINDOW_TITLE)) {
+		nk_layout_row_static(context, context->style.font->height, render_width()/2 - 80, 2);
+		for (int i = 0; i < num_types; i++)
+		{
+			if (nk_button_label(context, types[i])) {
+				selected_controller_info.type = type_id;
+				selected_controller_info.subtype = first_subtype_id + i;
+				pop_view();
+				push_view(view_controller_variant);
+			}
+		}
+		nk_group_end(context);
+	}
+}
+
+void view_controller_type(struct nk_context *context)
+{
+	if (nk_begin(context, "Controller Type", nk_rect(0, 0, render_width(), render_height()), 0)) {
+		controller_type_group(context, "Xbox", TYPE_XBOX, SUBTYPE_XBOX, (const char *[]){
+			"Original", "Xbox 360", "Xbox One"
+		}, 3);
+		controller_type_group(context, "Playstation", TYPE_PSX, SUBTYPE_PS3, (const char *[]){
+			"PS3", "PS4"
+		}, 2);
+		controller_type_group(context, "Sega", TYPE_SEGA, SUBTYPE_GENESIS, (const char *[]){
+			"Genesis", "Saturn"
+		}, 2);
+		controller_type_group(context, "Nintendo", TYPE_NINTENDO, SUBTYPE_WIIU, (const char *[]){
+			"WiiU", "Switch"
+		}, 2);
+		nk_end(context);
+	}
+}
+
+void view_controllers(struct nk_context *context)
+{
+	if (nk_begin(context, "Controllers", nk_rect(0, 0, render_width(), render_height()), NK_WINDOW_NO_SCROLLBAR)) {
+		int height = (render_width() - 2*context->style.font->height) / MAX_JOYSTICKS;
+		for (int i = 0; i < MAX_JOYSTICKS; i++)
+		{
+			SDL_Joystick *joy = render_get_joystick(i);
+			if (joy) {
+				controller_info info = get_controller_info(i);
+				ui_image *controller_image = select_best_image(&info);
+				int image_width = height * controller_image->width / controller_image->height;
+				nk_layout_row_begin(context, NK_STATIC, height, 2);
+				nk_layout_row_push(context, image_width);
+				if (info.type == TYPE_UNKNOWN || info.type == TYPE_GENERIC_MAPPING) {
+					nk_label(context, "?", NK_TEXT_CENTERED);
+				} else {
+					nk_image(context, controller_image->ui);
+				}
+				nk_layout_row_push(context, render_width() - image_width - 2 * context->style.font->height);
+				if (nk_button_label(context, info.name)) {
+					selected_controller = i;
+					selected_controller_info = info;
+					if (info.type == TYPE_UNKNOWN || info.type == TYPE_GENERIC_MAPPING) {
+						push_view(view_controller_type);
+					} else {
+						push_view(view_controller_bindings);
+						controller_binding_changed = 0;
+					}
+					
+				}
+				nk_layout_row_end(context);
+			}
+		}
+		nk_layout_row_static(context, context->style.font->height, (render_width() - 2 * context->style.font->height) / 2, 2);
+		nk_label(context, "", NK_TEXT_LEFT);
+		if (nk_button_label(context, "Back")) {
+			pop_view();
+		}
+		nk_end(context);
+	}
+}
+
+void settings_toggle(struct nk_context *context, char *label, char *path, uint8_t def)
+{
+	uint8_t curval = !strcmp("on", tern_find_path_default(config, path, (tern_val){.ptrval = def ? "on": "off"}, TVAL_PTR).ptrval);
+	nk_label(context, label, NK_TEXT_LEFT);
+	uint8_t newval = nk_check_label(context, "", curval);
+	if (newval != curval) {
+		config_dirty = 1;
+		config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(newval ? "on" : "off")}, TVAL_PTR);
+	}
+}
+
+void settings_int_input(struct nk_context *context, char *label, char *path, char *def)
+{
+	char buffer[12];
+	nk_label(context, label, NK_TEXT_LEFT);
+	uint32_t curval;
+	char *curstr = tern_find_path_default(config, path, (tern_val){.ptrval = def}, TVAL_PTR).ptrval;
+	uint32_t len = strlen(curstr);
+	if (len > 11) {
+		len = 11;
+	}
+	memcpy(buffer, curstr, len);
+	memset(buffer+len, 0, sizeof(buffer)-len);
+	nk_edit_string(context, NK_EDIT_SIMPLE, buffer, &len, sizeof(buffer)-1, nk_filter_decimal);
+	buffer[len] = 0;
+	if (strcmp(buffer, curstr)) {
+		config_dirty = 1;
+		config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(buffer)}, TVAL_PTR);
+	}
+}
+
+void settings_int_property(struct nk_context *context, char *label, char *name, char *path, int def, int min, int max)
+{
+	char *curstr = tern_find_path(config, path, TVAL_PTR).ptrval;
+	int curval = curstr ? atoi(curstr) : def;
+	nk_label(context, label, NK_TEXT_LEFT);
+	int val = curval;
+	nk_property_int(context, name, min, &val, max, 1, 1.0f);
+	if (val != curval) {
+		char buffer[12];
+		sprintf(buffer, "%d", val);
+		config_dirty = 1;
+		config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(buffer)}, TVAL_PTR);
+	}
+}
+
+typedef struct {
+	char *fragment;
+	char *vertex;
+} shader_prog;
+
+shader_prog *get_shader_progs(dir_entry *entries, size_t num_entries, shader_prog *progs, uint32_t *num_existing, uint32_t *storage)
+{
+	uint32_t num_progs = *num_existing;
+	uint32_t prog_storage = *storage;
+	uint32_t starting = num_progs;
+	
+	for (uint32_t i = 0; i < num_entries; i++) {
+		if (entries[i].is_dir) {
+			continue;
+		}
+		char *no_ext = basename_no_extension(entries[i].name);
+		uint32_t len = strlen(no_ext);
+		if (no_ext[len-1] == 'f' && no_ext[len-2] == '.') {
+			uint8_t dupe = 0;;
+			for (uint32_t j = 0; j < starting; j++) {
+				if (!strcmp(entries[i].name, progs[j].fragment)) {
+					dupe = 1;
+					break;
+				}
+			}
+			if (!dupe) {
+				if (num_progs == prog_storage) {
+					prog_storage = prog_storage ? prog_storage*2 : 4;
+					progs = realloc(progs, sizeof(progs) * prog_storage);
+				}
+				progs[num_progs].vertex = NULL;
+				progs[num_progs++].fragment = strdup(entries[i].name); 
+			}
+		}
+		free(no_ext);
+	}
+	
+	for (uint32_t i = 0; i < num_entries; i++) {
+		if (entries[i].is_dir) {
+			continue;
+		}
+		char *no_ext = basename_no_extension(entries[i].name);
+		uint32_t len = strlen(no_ext);
+		if (no_ext[len-1] == 'v' && no_ext[len-2] == '.') {
+			for (uint32_t j = 0; j < num_progs; j++) {
+				if (!strncmp(no_ext, progs[j].fragment, len-1) && progs[j].fragment[len-1] == 'f' && progs[j].fragment[len] == '.') {
+					progs[j].vertex = strdup(entries[i].name);
+				}
+			}
+		}
+		free(no_ext);
+	}
+	free_dir_list(entries, num_entries);
+	*num_existing = num_progs;
+	*storage = prog_storage;
+	return progs;
+}
+
+shader_prog *get_shader_list(uint32_t *num_out)
+{
+	char *shader_dir = path_append(get_config_dir(), "shaders");
+	size_t num_entries;
+	dir_entry *entries = get_dir_list(shader_dir, &num_entries);
+	free(shader_dir);
+	shader_prog *progs;
+	uint32_t num_progs = 0, prog_storage;
+	if (num_entries) {
+		progs = calloc(num_entries, sizeof(shader_prog));
+		prog_storage = num_entries;
+		progs = get_shader_progs(entries, num_entries, progs, &num_progs, &prog_storage);
+	} else {
+		progs = NULL;
+		prog_storage = 0;
+	}
+	shader_dir = path_append(get_exe_dir(), "shaders");
+	entries = get_dir_list(shader_dir, &num_entries);
+	progs = get_shader_progs(entries, num_entries, progs, &num_progs, &prog_storage);
+	*num_out = num_progs;
+	return progs;
+}
+
+int32_t find_match(const char **options, uint32_t num_options, char *path, char *def)
+{
+	char *setting = tern_find_path_default(config, path, (tern_val){.ptrval = def}, TVAL_PTR).ptrval;
+	int32_t selected = -1;
+	for (uint32_t i = 0; i < num_options; i++)
+	{
+		if (!strcmp(setting, options[i])) {
+			selected = i;
+			break;
+		}
+	}
+	if (selected == -1) {
+		for (uint32_t i = 0; i < num_options; i++)
+		{
+			if (!strcmp(def, options[i])) {
+				selected = i;
+				break;
+			}
+		}
+	}
+	return selected;
+}
+
+int32_t settings_dropdown_ex(struct nk_context *context, char *label, const char **options, const char **opt_display, uint32_t num_options, int32_t current, char *path)
+{
+	nk_label(context, label, NK_TEXT_LEFT);
+	int32_t next = nk_combo(context, opt_display, num_options, current, 30, nk_vec2(300, 300));
+	if (next != current) {
+		config_dirty = 1;
+		config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(options[next])}, TVAL_PTR);
+	}
+	return next;
+}
+
+int32_t settings_dropdown(struct nk_context *context, char *label, const char **options, uint32_t num_options, int32_t current, char *path)
+{
+	return settings_dropdown_ex(context, label, options, options, num_options, current, path);
+}
+
+void view_video_settings(struct nk_context *context)
+{
+	const char *vsync_opts[] = {"on", "off", "tear"};
+	const char *vsync_opt_names[] = {
+		"On",
+		"Off",
+		"On, tear if late"
+	};
+	const uint32_t num_vsync_opts = sizeof(vsync_opts)/sizeof(*vsync_opts);
+	static shader_prog *progs;
+	static char **prog_names;
+	static uint32_t num_progs;
+	static uint32_t selected_prog;
+	static int32_t selected_vsync = -1;
+	if (selected_vsync < 0) {
+		selected_vsync = find_match(vsync_opts, num_vsync_opts, "video\0vsync\0", "off");
+	}
+	if(!progs) {
+		progs = get_shader_list(&num_progs);
+		prog_names = calloc(num_progs, sizeof(char*));
+		for (uint32_t i = 0; i < num_progs; i++)
+		{
+			prog_names[i] = basename_no_extension(progs[i].fragment);;
+			uint32_t len = strlen(prog_names[i]);
+			if (len > 2) {
+				prog_names[i][len-2] = 0;
+			}
+			if (!progs[i].vertex) {
+				progs[i].vertex = strdup("default.v.glsl");
+			}
+			if (!strcmp(
+				progs[i].fragment,
+				tern_find_path_default(config, "video\0fragment_shader\0", (tern_val){.ptrval = "default.f.glsl"}, TVAL_PTR).ptrval
+			)) {
+				selected_prog = i;
+			}
+		}
+	}
+	uint32_t width = render_width();
+	uint32_t height = render_height();
+	uint32_t desired_width = context->style.font->height * 10;
+	if (desired_width > width) {
+		desired_width = width;
+	}
+	if (nk_begin(context, "Video Settings", nk_rect(0, 0, width, height), 0)) {
+		nk_layout_row_static(context, context->style.font->height, desired_width, 2);
+		settings_toggle(context, "Fullscreen", "video\0fullscreen\0", 0);
+		settings_toggle(context, "Open GL", "video\0gl\0", 1);
+		settings_toggle(context, "Scanlines", "video\0scanlines\0", 0);
+		selected_vsync = settings_dropdown_ex(context, "VSync", vsync_opts, vsync_opt_names, num_vsync_opts, selected_vsync, "video\0vsync\0");
+		settings_int_input(context, "Windowed Width", "video\0width\0", "640");
+		nk_label(context, "Shader", NK_TEXT_LEFT);
+		uint32_t next_selected = nk_combo(context, (const char **)prog_names, num_progs, selected_prog, context->style.font->height, nk_vec2(desired_width, desired_width));
+		if (next_selected != selected_prog) {
+			selected_prog = next_selected;
+			config_dirty = 1;
+			config = tern_insert_path(config, "video\0fragment_shader\0", (tern_val){.ptrval = strdup(progs[next_selected].fragment)}, TVAL_PTR);
+			config = tern_insert_path(config, "video\0vertex_shader\0", (tern_val){.ptrval = strdup(progs[next_selected].vertex)}, TVAL_PTR);
+		}
+		settings_int_property(context, "NTSC Overscan", "Top", "video\0ntsc\0overscan\0top\0", 2, 0, 32);
+		settings_int_property(context, "", "Bottom", "video\0ntsc\0overscan\0bottom\0", 17, 0, 32);
+		settings_int_property(context, "", "Left", "video\0ntsc\0overscan\0left\0", 13, 0, 32);
+		settings_int_property(context, "", "Right", "video\0ntsc\0overscan\0right\0", 14, 0, 32);
+		settings_int_property(context, "PAL Overscan", "Top", "video\0pal\0overscan\0top\0", 2, 0, 32);
+		settings_int_property(context, "", "Bottom", "video\0pal\0overscan\0bottom\0", 17, 0, 32);
+		settings_int_property(context, "", "Left", "video\0pal\0overscan\0left\0", 13, 0, 32);
+		settings_int_property(context, "", "Right", "video\0pal\0overscan\0right\0", 14, 0, 32);
+		
+		if (nk_button_label(context, "Back")) {
+			pop_view();
+		}
+		nk_end(context);
+	}
+}
+
+void view_audio_settings(struct nk_context *context)
+{
+	const char *rates[] = {
+		"192000",
+		"96000",
+		"48000",
+		"44100",
+		"22050"
+	};
+	const char *sizes[] = {
+		"1024",
+		"512",
+		"256",
+		"128",
+		"64"
+	};
+	const uint32_t num_rates = sizeof(rates)/sizeof(*rates);
+	const uint32_t num_sizes = sizeof(sizes)/sizeof(*sizes);
+	static int32_t selected_rate = -1;
+	static int32_t selected_size = -1;
+	if (selected_rate < 0 || selected_size < 0) {
+		selected_rate = find_match(rates, num_rates, "autio\0rate\0", "48000");
+		selected_size = find_match(sizes, num_sizes, "audio\0buffer\0", "512");
+	}
+	uint32_t width = render_width();
+	uint32_t height = render_height();
+	uint32_t desired_width = context->style.font->height * 10;
+	if (desired_width > width) {
+		desired_width = width;
+	}
+	if (nk_begin(context, "Audio Settings", nk_rect(0, 0, width, height), 0)) {
+		nk_layout_row_static(context, context->style.font->height , desired_width, 2);
+		selected_rate = settings_dropdown(context, "Rate in Hz", rates, num_rates, selected_rate, "audio\0rate\0");
+		selected_size = settings_dropdown(context, "Buffer Samples", sizes, num_sizes, selected_size, "audio\0buffer\0");
+		settings_int_input(context, "Lowpass Cutoff Hz", "audio\0lowpass_cutoff\0", "3390");
+		if (nk_button_label(context, "Back")) {
+			pop_view();
+		}
+		nk_end(context);
+	}
+}
+void view_system_settings(struct nk_context *context)
+{
+	const char *sync_opts[] = {
+		"video",
+		"audio"
+	};
+	const uint32_t num_sync_opts = sizeof(sync_opts)/sizeof(*sync_opts);
+	static int32_t selected_sync = -1;
+	if (selected_sync < 0) {
+		selected_sync = find_match(sync_opts, num_sync_opts, "system\0sync_source\0", "video");
+	}
+	const char *regions[] = {
+		"J - Japan",
+		"U - Americas",
+		"E - Europe"
+	};
+	const char *region_codes[] = {"J", "U", "E"};
+	const uint32_t num_regions = sizeof(regions)/sizeof(*regions);
+	static int32_t selected_region = -1;
+	if (selected_region < 0) {
+		selected_region = find_match(region_codes, num_regions, "system\0default_region\0", "U");
+	}
+	const char *formats[] = {
+		"native",
+		"gst"
+	};
+	const uint32_t num_formats = sizeof(formats)/sizeof(*formats);
+	int32_t selected_format = -1;
+	if (selected_format < 0) {
+		selected_format = find_match(formats, num_formats, "ui\0state_format\0", "native");
+	}
+	const char *ram_inits[] = {
+		"zero",
+		"random"
+	};
+	const uint32_t num_inits = sizeof(ram_inits)/sizeof(*ram_inits);
+	static int32_t selected_init = -1;
+	if (selected_init < 0) {
+		selected_init = find_match(ram_inits, num_inits, "system\0ram_init\0", "zero");
+	}
+	const char *io_opts_1[] = {
+		"gamepad2.1",
+		"gamepad3.1",
+		"gamepad6.1",
+		"mouse.1",
+		"saturn keyboard",
+		"xband keyboard"
+	};
+	const char *io_opts_2[] = {
+		"gamepad2.2",
+		"gamepad3.2",
+		"gamepad6.2",
+		"mouse.1",
+		"saturn keyboard",
+		"xband keyboard"
+	};
+	static int32_t selected_io_1 = -1;
+	static int32_t selected_io_2 = -1;
+	const uint32_t num_io = sizeof(io_opts_1)/sizeof(*io_opts_1);
+	if (selected_io_1 < 0 || selected_io_2 < 0) {
+		selected_io_1 = find_match(io_opts_1, num_io, "io\0devices\0""1\0", "gamepad6.1");
+		selected_io_2 = find_match(io_opts_2, num_io, "io\0devices\0""2\0", "gamepad6.2");
+	}
+	
+	uint32_t width = render_width();
+	uint32_t height = render_height();
+	uint32_t desired_width = context->style.font->height * 10;
+	if (nk_begin(context, "System Settings", nk_rect(0, 0, width, height), 0)) {
+		nk_layout_row_static(context, context->style.font->height, desired_width, 2);
+		selected_sync = settings_dropdown(context, "Sync Source", sync_opts, num_sync_opts, selected_sync, "system\0sync_source\0");
+		settings_int_property(context, "68000 Clock Divider", "", "clocks\0m68k_divider\0", 7, 1, 53);
+		settings_toggle(context, "Remember ROM Path", "ui\0remember_path\0", 1);
+		selected_region = settings_dropdown_ex(context, "Default Region", region_codes, regions, num_regions, selected_region, "system\0default_region\0");
+		selected_format = settings_dropdown(context, "Save State Format", formats, num_formats, selected_format, "ui\0state_format\0");
+		selected_init = settings_dropdown(context, "Initial RAM Value", ram_inits, num_inits, selected_init, "system\0ram_init\0");
+		selected_io_1 = settings_dropdown_ex(context, "IO Port 1 Device", io_opts_1, device_type_names, num_io, selected_io_1, "io\0devices\0""1\0");
+		selected_io_2 = settings_dropdown_ex(context, "IO Port 2 Device", io_opts_2, device_type_names, num_io, selected_io_2, "io\0devices\0""2\0");
+		if (nk_button_label(context, "Back")) {
+			pop_view();
+		}
+		nk_end(context);
+	}
+}
+
+void view_back(struct nk_context *context)
+{
+	pop_view();
+	pop_view();
+	current_view(context);
+}
+
+void view_settings(struct nk_context *context)
+{
+	static menu_item items[] = {
+		{"Key Bindings", view_key_bindings},
+		{"Controllers", view_controllers},
+		{"Video", view_video_settings},
+		{"Audio", view_audio_settings},
+		{"System", view_system_settings},
+		{"Back", view_back}
+	};
+	
+	if (nk_begin(context, "Settings Menu", nk_rect(0, 0, render_width(), render_height()), 0)) {
+		menu(context, sizeof(items)/sizeof(*items), items, NULL);
+		nk_end(context);
+	}
+}
+
+void exit_handler(uint32_t index)
+{
+	exit(0);
+}
+
+void view_pause(struct nk_context *context)
+{
+	static menu_item items[] = {
+		{"Resume", view_play},
+		{"Load ROM", view_load},
+		{"Lock On", view_lock_on},
+		{"Save State", view_save_state},
+		{"Load State", view_load_state},
+		{"Settings", view_settings},
+		{"Exit", NULL}
+	};
+	
+	if (nk_begin(context, "Main Menu", nk_rect(0, 0, render_width(), render_height()), 0)) {
+		menu(context, sizeof(items)/sizeof(*items), items, exit_handler);
+		nk_end(context);
+	}
+}
+
+void view_menu(struct nk_context *context)
+{
+	static menu_item items[] = {
+		{"Load ROM", view_load},
+		{"Settings", view_settings},
+		{"About", view_about},
+		{"Exit", NULL}
+	};
+	
+	if (nk_begin(context, "Main Menu", nk_rect(0, 0, render_width(), render_height()), 0)) {
+		menu(context, sizeof(items)/sizeof(*items), items, exit_handler);
+		nk_end(context);
+	}
+}
+
+void blastem_nuklear_render(void)
+{
+	nk_input_end(context);
+	current_view(context);
+	nk_sdl_render(NK_ANTI_ALIASING_ON, 512 * 1024, 128 * 1024);
+	nk_input_begin(context);
+}
+
+void ui_idle_loop(void)
+{
+	const uint32_t MIN_UI_DELAY = 15;
+	static uint32_t last;
+	while (current_view != view_play)
+	{
+		uint32_t current = render_elapsed_ms();
+		if ((current - last) < MIN_UI_DELAY) {
+			render_sleep_ms(MIN_UI_DELAY - (current - last) - 1);
+		}
+		last = current;
+		render_update_display();
+	}
+	if (config_dirty) {
+		apply_updated_config();
+		persist_config(config);
+		config_dirty = 0;
+	}
+}
+static void handle_event(SDL_Event *event)
+{
+	if (event->type == SDL_KEYDOWN) {
+		keycode = event->key.keysym.sym;
+	}
+	else if (event->type == SDL_JOYBUTTONDOWN) {
+		button_pressed = event->jbutton.button;
+	}
+	else if (event->type == SDL_JOYHATMOTION) {
+		hat_moved = event->jhat.hat;
+		hat_value = event->jhat.value;
+	}
+	else if (event->type == SDL_JOYAXISMOTION) {
+		if (event->jaxis.axis == axis_moved || abs(event->jaxis.value) > abs(axis_value) || abs(event->jaxis.value) > 1000) {
+			axis_moved = event->jaxis.axis;
+			axis_value = event->jaxis.value;
+		}
+	}
+	nk_sdl_handle_event(event);
+}
+
+static void context_destroyed(void)
+{
+	nk_sdl_shutdown();
+}
+
+static struct nk_image load_image_texture(uint32_t *buf, uint32_t width, uint32_t height)
+{
+	GLuint tex;
+	glGenTextures(1, &tex);
+	glBindTexture(GL_TEXTURE_2D, tex);
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, buf);
+	return nk_image_id((int)tex);
+}
+
+static void texture_init(void)
+{
+	struct nk_font_atlas *atlas;
+	nk_sdl_font_stash_begin(&atlas);
+	uint32_t font_size;
+	uint8_t *font = default_font(&font_size);
+	if (!font) {
+		fatal_error("Failed to find default font path\n");
+	}
+	def_font = nk_font_atlas_add_from_memory(atlas, font, font_size, render_height() / 16, NULL);
+	free(font);
+	nk_sdl_font_stash_end();
+	nk_style_set_font(context, &def_font->handle);
+	for (uint32_t i = 0; i < num_ui_images; i++)
+	{
+		ui_images[i]->ui = load_image_texture(ui_images[i]->image_data, ui_images[i]->width, ui_images[i]->height);
+	}
+}
+
+static void context_created(void)
+{
+	context = nk_sdl_init(render_get_window());
+	texture_init();
+}
+
+void show_pause_menu(void)
+{
+	context->style.window.background = nk_rgba(0, 0, 0, 128);
+	context->style.window.fixed_background = nk_style_item_color(nk_rgba(0, 0, 0, 128));
+	current_view = view_pause;
+	current_system->request_exit(current_system);
+}
+
+void show_play_view(void)
+{
+	current_view = view_play;
+}
+
+static uint8_t active;
+uint8_t is_nuklear_active(void)
+{
+	return active;
+}
+
+uint8_t is_nuklear_available(void)
+{
+	if (!render_has_gl()) {
+		//currently no fallback if GL2 unavailable
+		return 0;
+	}
+	char *style = tern_find_path(config, "ui\0style\0", TVAL_PTR).ptrval;
+	if (!style) {
+		return 1;
+	}
+	return strcmp(style, "rom") != 0;
+}
+
+static void persist_config_exit(void)
+{
+	if (config_dirty) {
+		persist_config(config);
+	}
+}
+
+ui_image *load_ui_image(char *name)
+{
+	uint32_t buf_size;
+	uint8_t *buf = (uint8_t *)read_bundled_file(name, &buf_size);
+	if (buf) {
+		num_ui_images++;
+		if (num_ui_images > ui_image_storage) {
+			ui_image_storage = (ui_image_storage + 1) * 2;
+			ui_images = realloc(ui_images, ui_image_storage * sizeof(*ui_images));
+		}
+		ui_image *this_image = ui_images[num_ui_images-1] = calloc(1, sizeof(ui_image));
+		this_image->image_data = load_png(buf, buf_size, &this_image->width, &this_image->height);
+		free(buf);
+		if (!this_image->image_data) {
+			num_ui_images--;
+			free(this_image);
+			return NULL;
+		}
+		return this_image;
+	} else {
+		return NULL;
+	}
+}
+
+void blastem_nuklear_init(uint8_t file_loaded)
+{
+	context = nk_sdl_init(render_get_window());
+	
+	controller_360 = load_ui_image("images/360.png");
+	controller_ps4 = load_ui_image("images/ps4.png");
+	controller_ps4_6b = load_ui_image("images/ps4_6b.png");
+	
+	texture_init();
+	
+	current_view = file_loaded ? view_play : view_menu;
+	render_set_ui_render_fun(blastem_nuklear_render);
+	render_set_event_handler(handle_event);
+	render_set_gl_context_handlers(context_destroyed, context_created);
+	
+	atexit(persist_config_exit);
+	
+	active = 1;
+	ui_idle_loop();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nuklear_ui/blastem_nuklear.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,20 @@
+#ifndef BLASTEM_NUKLEAR_H_
+#define BLASTEM_NUKLEAR_H_
+
+#define NK_INCLUDE_FIXED_TYPES
+#define NK_INCLUDE_STANDARD_IO
+#define NK_INCLUDE_STANDARD_VARARGS
+#define NK_INCLUDE_DEFAULT_ALLOCATOR
+#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
+#define NK_INCLUDE_FONT_BAKING
+#include "nuklear.h"
+#include "nuklear_sdl_gles2.h"
+
+void blastem_nuklear_init(uint8_t file_loaded);
+void show_pause_menu(void);
+void show_play_view(void);
+uint8_t is_nuklear_active(void);
+uint8_t is_nuklear_available(void);
+void ui_idle_loop(void);
+
+#endif //BLASTEM_NUKLEAR_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nuklear_ui/font.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "../util.h"
+#include "sfnt.h"
+
+char *default_font_path(void)
+{
+	FILE *fc_pipe = popen("fc-match -f '%{file}'", "r");
+	if (!fc_pipe) {
+		return NULL;
+	}
+	size_t buf_size = 128;
+	char *buffer = NULL;
+	size_t total = 0, read = 0;
+	do {
+		total += read;
+		buf_size *= 2;
+		buffer = realloc(buffer, buf_size);
+		if (!buffer) {
+			return NULL;
+		}
+		read = fread(buffer, 1, buf_size - total, fc_pipe);
+	} while (read == (buf_size - total));
+	total += read;
+	buffer[total] = 0;
+	
+	return buffer;
+}
+
+uint8_t *default_font(uint32_t *size_out)
+{
+	char *path = default_font_path();
+	if (!path) {
+		goto error;
+	}
+	FILE *f = fopen(path, "rb");
+	free(path);
+	if (!f) {
+		goto error;
+	}
+	long size = file_size(f);
+	uint8_t *buffer = malloc(size);
+	if (size != fread(buffer, 1, size, f)) {
+		fclose(f);
+		goto error;
+	}
+	fclose(f);
+	sfnt_container *sfnt = load_sfnt(buffer, size);
+	if (!sfnt) {
+		free(buffer);
+		goto error;
+	}
+	return sfnt_flatten(sfnt->tables, size_out);
+error:
+	//TODO: try to find a suitable font in /usr/share/fonts as a fallback
+	return NULL;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nuklear_ui/font.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,6 @@
+#ifndef FONT_H_
+#define FONT_H_
+
+uint8_t *default_font(uint32_t *size_out);
+
+#endif //FONT_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nuklear_ui/font_mac.m	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,118 @@
+#import <AppKit/AppKit.h>
+#include <stddef.h>
+#include "../paths.h"
+#include "../util.h"
+#include "sfnt.h"
+
+static sfnt_table *find_font_in_dir(char *path, char *prefix, const char *ps_name)
+{
+	size_t num_entries;
+	dir_entry *entries = get_dir_list(path, &num_entries);
+	size_t prefix_len = prefix ? strlen(prefix) : 0;
+	sfnt_table *selected = NULL;
+	for (size_t i = 0; i < num_entries && !selected; i++)
+	{
+		char *ext = path_extension(entries[i].name);
+		if (!ext || (strcasecmp(ext, "ttf") && strcasecmp(ext, "ttc") && strcasecmp(ext, "dfont"))) {
+			//not a truetype font, ignore
+			printf("Skipping %s because of its extension\n", entries[i].name);
+			free(ext);
+			continue;
+		}
+		free(ext);
+		if (!prefix || !strncasecmp(entries[i].name, prefix, prefix_len)) {
+			char *full_path = path_append(path, entries[i].name);
+			FILE *f = fopen(full_path, "rb");
+			if (f)
+			{
+				long font_size = file_size(f);
+				uint8_t *blob = malloc(font_size);
+				if (font_size == fread(blob, 1, font_size, f))
+				{
+					sfnt_container *sfnt = load_sfnt(blob, font_size);
+					if (sfnt) {
+						printf("Examining font file %s\n", entries[i].name);
+						for (uint8_t j = 0; j < sfnt->num_fonts && !selected; j++)
+						{
+							char *cur_ps = sfnt_name(sfnt->tables + j, SFNT_POSTSCRIPT);
+							printf("\t%s\n", cur_ps);
+							if (!strcmp(cur_ps, ps_name)) {
+								selected = sfnt->tables + j;
+							}
+							free(cur_ps);
+						}
+					} else {
+						printf("Failed to load %s as sfnt containern\n", entries[i].name);
+						free(blob);
+					}
+				} else {
+					free(blob);
+				}
+				fclose(f);
+			}
+			free(full_path);
+		}
+	}
+	return selected;
+}
+
+static sfnt_table *find_font_by_ps_name(const char*ps_name, uint8_t exhaustive)
+{
+	const unsigned char *prefix_start = (const unsigned char *)ps_name;
+	while(*prefix_start && (
+		*prefix_start < '0' || 
+		(*prefix_start > 'z' && *prefix_start <= 0x80) || 
+		(*prefix_start > 'Z' && *prefix_start < 'a') || 
+		(*prefix_start > '9' && *prefix_start < 'A')
+	))
+	{
+		prefix_start++;
+	}
+	if (!*prefix_start) {
+		//Didn't find a suitable starting character, just start from the beginning
+		prefix_start = (const unsigned char *)ps_name;
+	}
+	const unsigned char *prefix_end = (const unsigned char *)prefix_start + 1;
+	while (*prefix_end && *prefix_end >= 'a')
+	{
+		prefix_end++;
+	}
+	char *prefix = malloc(prefix_end - prefix_start + 1);
+	memcpy(prefix, prefix_start, prefix_end - prefix_start);
+	prefix[prefix_end-prefix_start] = 0;
+	//check /Library/Fonts first
+	sfnt_table *selected = find_font_in_dir("/Library/Fonts", (char *)prefix, ps_name);
+	if (!selected) {
+		selected = find_font_in_dir("/System/Library/Fonts", (char *)prefix, ps_name);
+	}
+	if (exhaustive) {
+		if (!selected) {
+			puts("Check using prefix failed, exhaustively checking fonts");
+			selected = find_font_in_dir("/Library/Fonts", NULL, ps_name);
+		}
+		if (!selected) {
+			selected = find_font_in_dir("/System/Library/Fonts", NULL, ps_name);
+		}
+	}
+	free(prefix);
+	return selected;
+}
+
+uint8_t *default_font(uint32_t *size_out)
+{
+	NSFont *sys = [NSFont systemFontOfSize:0];
+	NSString *name = [sys fontName];
+	sfnt_table *selected = find_font_by_ps_name([name UTF8String], 1);
+	if (!selected) {
+		selected = find_font_by_ps_name(".HelveticaNeueDeskInterface-Regular", 0);
+	}
+	if (!selected) {
+		selected = find_font_by_ps_name(".LucidaGrandeUI", 0);
+	}
+	
+	if (!selected) {
+		fatal_error("Failed to find system font %s\n", [name UTF8String]);
+	}
+	return sfnt_flatten(selected, size_out);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nuklear_ui/font_win.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,146 @@
+#include <windows.h>
+#include <shlobj.h>
+#include <string.h>
+#include "../paths.h"
+#include "../util.h"
+#include "sfnt.h"
+
+uint8_t *default_font(uint32_t *size_out)
+{
+	static const char *thin[] = {"Thin", NULL};
+	static const char *extra_light[] = {"ExtraLight", "UltraLight", NULL};
+	static const char *light[] = {"Light", NULL};
+	static const char *regular[] = {"Regular", "Normal", "Book", NULL};
+	static const char *medium[] = {"Medium", NULL};
+	static const char *semi_bold[] = {"SemiBold", "DemiBold", NULL};
+	static const char *bold[] = {"Bold", NULL};
+	static const char *extra_bold[] = {"ExtraBold", "UltraBold", NULL};
+	static const char *heavy[] = {"Heavy", "Black", NULL};
+	static const char **weight_to_subfamilies[] = {
+		NULL,
+		thin,
+		extra_light,
+		light,
+		regular,
+		medium,
+		semi_bold,
+		bold,
+		extra_bold,
+		heavy
+	};
+
+	NONCLIENTMETRICSA metrics = {
+		.cbSize = sizeof(metrics)
+	};
+	char *pref_name = NULL, *pref_prefix = NULL;
+	const char **pref_sub_families;
+	if (SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(metrics), &metrics, 0)) {
+		pref_name = metrics.lfMenuFont.lfFaceName;
+		int32_t weight = metrics.lfMenuFont.lfWeight / 100;
+		if (weight < 1 || weight > 9) {
+			weight = 4;
+		}
+		pref_sub_families = weight_to_subfamilies[weight];
+	}
+	if (pref_name) {
+		uint32_t prefix_len = 0;
+		while (pref_name[prefix_len] && pref_name[prefix_len] != ' ')
+		{
+			prefix_len++;
+		}
+		pref_prefix = malloc(prefix_len + 1);
+		memcpy(pref_prefix, pref_name, prefix_len);
+		pref_prefix[prefix_len] = 0;
+	}
+	sfnt_table *selected = NULL;
+	char windows[MAX_PATH];
+	SHGetFolderPathA(NULL, CSIDL_WINDOWS, NULL, 0, windows);
+	char *fonts = path_append(windows, "Fonts");
+	size_t num_entries;
+	char *tahoma = NULL, *arial = NULL;
+	dir_entry *entries = get_dir_list(fonts, &num_entries);
+	char *path = NULL;
+	for (size_t i = 0; i < num_entries; i++)
+	{
+		if (entries[i].is_dir) {
+			continue;
+		}
+		char *ext = path_extension(entries[i].name);
+		if (!ext || (strcasecmp(ext, "ttf") && strcasecmp(ext, "ttc") && strcasecmp(ext, "dfont"))) {
+			//not a truetype font, ignore
+			free(ext);
+			continue;
+		}
+		free(ext);
+		char *base = basename_no_extension(entries[i].name);
+		if (pref_prefix && !strncasecmp(base, pref_prefix, 6)) {
+			path = path_append(fonts, entries[i].name);
+			FILE *f = fopen(path, "rb");
+			if (f)
+			{
+				long font_size = file_size(f);
+				uint8_t *blob = malloc(font_size);
+				if (font_size == fread(blob, 1, font_size, f))
+				{
+					sfnt_container *sfnt = load_sfnt(blob, font_size);
+					if (sfnt) {
+						selected = sfnt_subfamily_by_names(sfnt, pref_sub_families);
+						if (!selected) {
+							sfnt_free(sfnt);
+						}
+					} else {
+						free(blob);
+					}
+				} else {
+					free(blob);
+				}
+				fclose(f);
+			}
+			free(path);
+			free(base);
+			if (selected) {
+				printf("Found preferred font in %s\n", entries[i].name);
+				break;
+			}
+		} else if (!strcasecmp(base, "tahoma")) {
+			tahoma = entries[i].name;
+		} else if (!strcasecmp(base, "arial")) {
+			arial = entries[i].name;
+		}
+		free(base);
+	}
+	if (!selected) {
+		path = NULL;
+		if (tahoma) {
+			path = path_append(fonts, tahoma);
+		} else if (arial) {
+			path = path_append(fonts, arial);
+		}
+		if (path) {
+			FILE *f = fopen(path, "rb");
+			if (f)
+			{
+				long font_size = file_size(f);
+				uint8_t *blob = malloc(font_size);
+				if (font_size == fread(blob, 1, font_size, f))
+				{
+					sfnt_container *sfnt = load_sfnt(blob, font_size);
+					if (sfnt) {
+						selected = sfnt->tables;
+					} else {
+						free(blob);
+					}
+				}
+				fclose(f);
+			}
+			free(path);
+		}
+	}
+	free(pref_prefix);
+	free(fonts);
+	free_dir_list(entries, num_entries);
+	if (selected) {
+		return sfnt_flatten(selected, size_out);
+	}
+	return NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nuklear_ui/nuklear.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,23652 @@
+/*
+ Nuklear - 2.00.0 - public domain
+ no warranty implied; use at your own risk.
+ authored from 2015-2017 by Micha Mettke
+
+ABOUT:
+    This is a minimal state graphical user interface single header 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.
+
+VALUES:
+    - 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
+
+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.:
+
+        #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.
+    IMPORTANT:  Every time you include "nuklear.h" you have to define the same flags.
+                This is very important not doing it either leads to compiler errors
+                or even worse stack corruptions.
+
+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
+
+OPTIONAL DEFINES:
+    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 <stdint.h> 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.
+        <!> If used needs to be defined for implementation and header <!>
+
+    NK_INCLUDE_DEFAULT_ALLOCATOR
+        if defined it will include header <stdlib.h> and provide additional functions
+        to use this library without caring for memory allocation control and therefore
+        ease memory management.
+        <!> Adds the standard library with malloc and free so don't define if you
+            don't want to link to the standard library <!>
+        <!> If used needs to be defined for implementation and header <!>
+
+    NK_INCLUDE_STANDARD_IO
+        if defined it will include header <stdio.h> and provide
+        additional functions depending on file loading.
+        <!> Adds the standard library with fopen, fclose,... so don't define this
+            if you don't want to link to the standard library <!>
+        <!> If used needs to be defined for implementation and header <!>
+
+    NK_INCLUDE_STANDARD_VARARGS
+        if defined it will include header <stdarg.h> and provide
+        additional functions depending on variable arguments
+        <!> Adds the standard library with va_list and  so don't define this if
+            you don't want to link to the standard library<!>
+        <!> If used needs to be defined for implementation and header <!>
+
+    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,...
+        <!> If used needs to be defined for implementation and header <!>
+
+    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.
+        <!> If used needs to be defined for implementation and header <!>
+
+    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
+        <!> Enabling this adds ~12kb to global stack memory <!>
+        <!> If used needs to be defined for implementation and header <!>
+
+    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.
+        <!> If used needs to be defined for implementation and header <!>
+
+    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.
+        <!> If used it is only required to be defined for the implementation part <!>
+
+    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_ASSERT
+        If you don't define this, nuklear will use <assert.h> with assert().
+        <!> Adds the standard library so define to nothing of not wanted <!>
+        <!> If used needs to be defined for implementation and header <!>
+
+    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.
+        <!> If used needs to be defined for implementation and header <!>
+
+    NK_MAX_NUMBER_BUFFER
+        Maximum buffer size for the conversion buffer between float and string
+        Under normal circumstances this should be more than sufficient.
+        <!> If used needs to be defined for implementation and header <!>
+
+    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.
+        <!> If used it is only required to be defined for the implementation part <!>
+
+    NK_MEMSET
+        You can define this to 'memset' or your own memset implementation
+        replacement. If not nuklear will use its own version.
+        <!> If used it is only required to be defined for the implementation part <!>
+
+    NK_MEMCPY
+        You can define this to 'memcpy' or your own memcpy implementation
+        replacement. If not nuklear will use its own version.
+        <!> If used it is only required to be defined for the implementation part <!>
+
+    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.
+        <!> If used it is only required to be defined for the implementation part <!>
+
+    NK_SIN
+        You can define this to 'sinf' or your own sine implementation
+        replacement. If not nuklear will use its own approximation implementation.
+        <!> If used it is only required to be defined for the implementation part <!>
+
+    NK_COS
+        You can define this to 'cosf' or your own cosine implementation
+        replacement. If not nuklear will use its own approximation implementation.
+        <!> If used it is only required to be defined for the implementation part <!>
+
+    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!).
+        <!> If used it is only required to be defined for the implementation part <!>
+
+    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!).
+        <!> If used it is only required to be defined for the implementation part <!>
+
+    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.
+        <!> If used it is only required to be defined for the implementation part <!>
+
+    NK_BYTE
+    NK_INT16
+    NK_UINT16
+    NK_INT32
+    NK_UINT32
+    NK_SIZE_TYPE
+    NK_POINTER_TYPE
+        If you compile without NK_USE_FIXED_TYPE then a number of standard types
+        will be selected and compile time validated. If they are incorrect you can
+        define the correct types by overloading these type defines.
+
+CREDITS:
+    Developed by Micha Mettke and every direct or indirect contributor.
+
+    Embeds stb_texedit, stb_truetype and stb_rectpack by Sean Barret (public domain)
+    Embeds ProggyClean.ttf font by Tristan Grimmer (MIT license).
+
+    Big thank you to Omar Cornut (ocornut@github) for his imgui library and
+    giving me the inspiration for this library, Casey Muratori for handmade hero
+    and his original immediate mode graphical user interface idea and Sean
+    Barret for his amazing single header libraries which restored my faith
+    in libraries and brought me to create some of my own.
+
+LICENSE:
+    This software is dual-licensed to the public domain and under the following
+    license: you are granted a perpetual, irrevocable license to copy, modify,
+    publish and distribute this file as you see fit.
+*/
+#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
+
+#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))
+/*
+ * ===============================================================
+ *
+ *                          BASIC
+ *
+ * ===============================================================
+ */
+#ifdef NK_INCLUDE_FIXED_TYPES
+ #include <stdint.h>
+ #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 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
+
+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_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*));
+
+/* ============================================================================
+ *
+ *                                  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 int(*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
+ *
+ * =============================================================================*/
+/*  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.
+ *
+ *      struct nk_context ctx;
+ *      nk_init_xxx(&ctx, ...);
+ *      while (1) {
+ *          [...]
+ *          nk_clear(&ctx);
+ *      }
+ *      nk_free(&ctx);
+ *
+ *  Reference
+ *  -------------------
+ *  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.
+ *  Parameters:
+ *      @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
+ *  Return values:
+ *      true(1) on success
+ *      false(0) on failure */
+NK_API int nk_init_default(struct nk_context*, const struct nk_user_font*);
+#endif
+/*  nk_init_fixed - Initializes a `nk_context` struct from a 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.
+ *  IMPORTANT: make sure the passed memory block is aligned correctly for `nk_draw_commands`
+ *  Parameters:
+ *      @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
+ *  Return values:
+ *      true(1) on success
+ *      false(0) on failure */
+NK_API int 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.
+ *  Parameters:
+ *      @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
+ *  Return values:
+ *      true(1) on success
+ *      false(0) on failure */
+NK_API int 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.
+ *  Parameters:
+ *      @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
+ *  Return values:
+ *      true(1) on success
+ *      false(0) on failure */
+NK_API int 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.
+ *  Parameters:
+ *      @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`.
+ *  Parameters:
+ *      @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.
+ *  Parameters:
+ *      @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
+ *
+ * =============================================================================*/
+/*  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 in 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.
+ *
+ *  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.
+ *
+ *      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
+ *  -------------------
+ *  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,
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct */
+NK_API void nk_input_begin(struct nk_context*);
+/*  nk_input_motion - Mirrors current mouse position to nuklear
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @x must contain an integer describing the current mouse cursor x-position
+ *      @y must contain 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 state of a specific key to nuklear
+ *  Parameters:
+ *      @ctx must point to an 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, int down);
+/*  nk_input_button - Mirrors the state of a specific mouse button to nuklear
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @nk_buttons 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, int 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
+ *  from touch for example.
+ *  Parameters:
+ *      @ctx must point to an 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 that you can only push up to NK_INPUT_MAX bytes into
+ *  struct `nk_input` between `nk_input_begin` and `nk_input_end`.
+ *  Parameters:
+ *      @ctx must point to an 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_unicode - Converts a encoded unicode rune into UTF-8 and copies the result
+ *  into an internal text buffer.
+ *  Note that you can only push up to NK_INPUT_MAX bytes into
+ *  struct `nk_input` between `nk_input_begin` and `nk_input_end`.
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @glyph 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 that you can only push up to NK_INPUT_MAX bytes into
+ *  struct `nk_input` between `nk_input_begin` and `nk_input_end`.
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @glyph 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.
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct */
+NK_API void nk_input_end(struct nk_context*);
+/* =============================================================================
+ *
+ *                                  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:
+ *
+ *      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.
+ *
+ *      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.
+ *
+ *      struct nk_context ctx;
+ *      nk_init_xxx(&ctx, ...);
+ *      while (1) {
+ *          [...wait for input ]
+ *
+ *          [...do two UI passes ...]
+ *          do_ui(...)
+ *          nk_clear(&ctx);
+ *          do_ui(...)
+ *
+ *          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).
+ *
+ *      [... other defines ...]
+ *      #define NK_ZERO_COMMAND_MEMORY
+ *      #include "nuklear.h"
+ *
+ *      struct nk_context ctx;
+ *      void *last = calloc(1,64*1024);
+ *      void *buf = calloc(1,64*1024);
+ *      nk_init_fixed(&ctx, buf, 64*1024);
+ *      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:
+ *
+ *      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;
+ *
+ *      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);
+ *      nk_draw_foreach(cmd, &ctx, &cmds) {
+ *          if (!cmd->elem_count) continue;
+ *          [...]
+ *      }
+ *      nk_buffer_free(&cms);
+ *      nk_buffer_free(&verts);
+ *      nk_buffer_free(&idx);
+ *
+ *  Reference
+ *  -------------------
+ *  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.
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct at the end of a frame
+ *  Return values:
+ *      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 a draw command list iterator to iterate all draw
+ *  Parameters:
+ *      @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`
+ *  Return values:
+ *      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
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct at the end of a frame
+ *      @cmd pointer initialized to NULL */
+#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 command 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.
+ *  Parameters:
+ *      @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:
+ *      returns NK_CONVERT_SUCCESS on success and a enum nk_convert_result error values if not */
+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 each the vertex draw command buffer
+ *  Parameters:
+ *      @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
+ *  Return values:
+ *      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
+ *  Parameters:
+ *      @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
+ *  Return values:
+ *      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
+ *  Parameters:
+ *      @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
+ *  Return values:
+ *      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
+ *  Parameters:
+ *      @cmd nk_draw_command pointer 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
+ *
+ * =============================================================================
+ * 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 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.
+ *
+ *      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.
+ *
+ *      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
+ *  -------------------
+ *  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_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_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
+ */
+enum nk_panel_flags {
+    NK_WINDOW_BORDER            = NK_FLAG(0), /* Draws a border around the window to visually separate window from the background */
+    NK_WINDOW_MOVABLE           = NK_FLAG(1), /* The movable flag indicates that a window can be moved by user input or by dragging the window header */
+    NK_WINDOW_SCALABLE          = NK_FLAG(2), /* 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          = NK_FLAG(3), /* adds a closable icon into the header */
+    NK_WINDOW_MINIMIZABLE       = NK_FLAG(4), /* adds a minimize icon into the header */
+    NK_WINDOW_NO_SCROLLBAR      = NK_FLAG(5), /* Removes the scrollbar from the window */
+    NK_WINDOW_TITLE             = NK_FLAG(6), /* Forces a header at the top at the window showing the title */
+    NK_WINDOW_SCROLL_AUTO_HIDE  = NK_FLAG(7), /* Automatically hides the window scrollbar if no user interaction: also requires delta time in `nk_context` to be set each frame */
+    NK_WINDOW_BACKGROUND        = NK_FLAG(8), /* Always keep window in the background */
+    NK_WINDOW_SCALE_LEFT        = NK_FLAG(9), /* Puts window scaler in the left-ottom corner instead right-bottom*/
+    NK_WINDOW_NO_INPUT          = NK_FLAG(10) /* Prevents window of scaling, moving or getting focus */
+};
+/*  nk_begin - starts a new window; needs to be called every frame for every window (unless hidden) or otherwise the window gets removed
+ *  Parameters:
+ *      @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 `enum nk_panel_flags` with a number of different window behaviors
+ *  Return values:
+ *      returns 1 if the window can be filled up with widgets from this point until `nk_end or 0 otherwise for example if minimized `*/
+NK_API int 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 name but not title
+ *  Parameters:
+ *      @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 `enum nk_panel_flags` with a number of different window behaviors
+ *  Return values:
+ *      returns 1 if the window can be filled up with widgets from this point until `nk_end or 0 otherwise `*/
+NK_API int 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
+ *  Parameters:
+ *      @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 the window with give name
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @name window identifier
+ *  Return values:
+ *      returns a `nk_window` struct pointing to the identified window or 0 if no window with 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.
+ *  IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *  Return values:
+ *      returns a `nk_rect` struct with window upper left 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.
+ *  IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *  Return values:
+ *      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.
+ *  IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *  Return values:
+ *      returns a `nk_vec2` struct with window size */
+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.
+ *  IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *  Return values:
+ *      returns the 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.
+ *  IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *  Return values:
+ *      returns the 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.
+ *  IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *  Return values:
+ *      returns a pointer to window internal `nk_panel` state. DO NOT keep this pointer around it is only valid until `nk_end` */
+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.
+ *  IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *  Return values:
+ *      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.
+ *  IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *  Return values:
+ *      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.
+ *  IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *  Return values:
+ *      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
+ *  IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *  Return values:
+ *      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
+ *  IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *  Return values:
+ *      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_has_focus - returns if the currently processed window is currently active
+ *  IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *  Return values:
+ *      returns 0 if current window is not active or 1 if it is */
+NK_API int nk_window_has_focus(const struct nk_context*);
+/*  nk_window_is_collapsed - returns if the window with given name is currently minimized/collapsed
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @name of window you want to check is collapsed
+ *  Return values:
+ *      returns 1 if current window is minimized and 0 if window not found or is not minimized */
+NK_API int 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`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @name of window you want to check is closed
+ *  Return values:
+ *      returns 1 if current window was closed or 0 window not found or not closed */
+NK_API int nk_window_is_closed(struct nk_context*, const char*);
+/*  nk_window_is_hidden - returns if the window with given name is hidden
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @name of window you want to check is hidden
+ *  Return values:
+ *      returns 1 if current window is hidden or 0 window not found or visible */
+NK_API int nk_window_is_hidden(struct nk_context*, const char*);
+/*  nk_window_is_active - same as nk_window_has_focus for some reason
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @name of window you want to check is hidden
+ *  Return values:
+ *      returns 1 if current window is active or 0 window not found or not active */
+NK_API int nk_window_is_active(struct nk_context*, const char*);
+/*  nk_window_is_hovered - return if the current window is being hovered
+ *  IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *  Return values:
+ *      returns 1 if current window is hovered or 0 otherwise */
+NK_API int nk_window_is_hovered(struct nk_context*);
+/*  nk_window_is_any_hovered - returns if the any window is being hovered
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *  Return values:
+ *      returns 1 if any window is hovered or 0 otherwise */
+NK_API int 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.
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *  Return values:
+ *      returns 1 if any window is hovered or any item is active or 0 otherwise */
+NK_API int nk_item_is_any_active(struct nk_context*);
+/*  nk_window_set_bounds - updates position and size of the currently processed window
+ *  IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @name of the window to modify both position and size
+ *      @bounds points to a `nk_rect` struct with the new position and size of currently active window */
+NK_API void nk_window_set_bounds(struct nk_context*, const char *name, struct nk_rect bounds);
+/*  nk_window_set_position - updates position of the currently processed window
+ *  IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @name of the window to modify position of
+ *      @pos points to a `nk_vec2` struct with the new position of currently active window */
+NK_API void nk_window_set_position(struct nk_context*, const char *name, struct nk_vec2 pos);
+/*  nk_window_set_size - updates size of the currently processed window
+ *  IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @name of the window to modify size of
+ *      @size points to a `nk_vec2` struct with the new size of currently active window */
+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
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @name of the window to be set active */
+NK_API void nk_window_set_focus(struct nk_context*, const char *name);
+/*  nk_window_close - closed a window and marks it for being freed at the end of the frame
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @name of the window to be closed */
+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
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @name of the window to be either collapse or maximize */
+NK_API void nk_window_collapse(struct nk_context*, const char *name, enum nk_collapse_states state);
+/*  nk_window_collapse - updates collapse state of a window with given name if given condition is met
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @name of the window to be either collapse or maximize
+ *      @state the window should be put into
+ *      @condition that has to be true 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
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @name of the window to be either collapse or maximize
+ *      @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
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @name of the window to be either collapse or maximize
+ *      @state with either visible or hidden to modify the window with
+ *      @condition that has to be true to actually commit the visible state change */
+NK_API void nk_window_show_if(struct nk_context*, const char *name, enum nk_show_states, int cond);
+/* =============================================================================
+ *
+ *                                  LAYOUT
+ *
+ * ============================================================================= */
+/*  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:
+ *
+ *      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.
+ *
+ *      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.
+ *
+ *      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.
+ *
+ *      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.
+ *
+ *      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 static widget size specifier 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 which are completely flexible
+ *  and unlike variable widgets can even shrink to zero if not enough space
+ *  is provided.
+ *
+ *      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);
+ *
+ *          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.
+ *
+ *      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
+ *  -------------------
+ *  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.
+ *  IMPORTANT: The passed height needs to include both your preferred row height
+ *  as well as padding. No internal padding is added.
+ *  Parameters:
+ *      @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 + additional padding (style_window.min_row_height_padding)
+ *  Parameters:
+ *      @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
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` */
+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
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context`
+ *      @pixel_width to convert to window ratio */
+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.
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
+ *      @row_height holds height of each widget in row or zero for auto layouting
+ *      @cols 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.
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct after call `nk_begin_xxx`
+ *      @height holds row height to allocate from panel for widget height
+ *      @item_width holds width of each widget in row
+ *      @cols 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.
+ *  Parameters:
+ *      @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
+ *      @row_height holds height of each widget in row or zero for auto layouting
+ *      @cols 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
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_begin`
+ *      @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
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_begin` */
+NK_API void nk_layout_row_end(struct nk_context*);
+/*  nk_layout_row - specifies row columns in array as either window ratio or size
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context`
+ *      @fmt either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns
+ *      @row_height holds height of each widget in row or zero for auto layouting
+ *      @cols 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
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @row_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
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_template_begin` */
+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
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_template_begin`
+ *      @min_width holds the minimum pixel width the next column must 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
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_template_begin`
+ *      @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
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_template_begin` */
+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.
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct
+ *      @fmt either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns
+ *      @row_height holds height of each widget in row or zero for auto layouting
+ *      @widget_count 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
+ *  Parameters:
+ *      @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);
+/*  nk_layout_space_end - marks the end of the layout space
+ *  Parameters:
+ *      @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 - returns total space allocated for `nk_layout_space`
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` */
+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
+ *  Parameters:
+ *      @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 */
+NK_API struct nk_vec2 nk_layout_space_to_screen(struct nk_context*, struct nk_vec2);
+/*  nk_layout_space_to_screen - converts vector from layout space into screen space
+ *  Parameters:
+ *      @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 */
+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
+ *  Parameters:
+ *      @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 */
+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
+ *  Parameters:
+ *      @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin`
+ *      @bounds rectangle to convert from screen space into layout space */
+NK_API struct nk_rect nk_layout_space_rect_to_local(struct nk_context*, struct nk_rect);
+/* =============================================================================
+ *
+ *                                  GROUP
+ *
+ * ============================================================================= */
+NK_API int nk_group_begin(struct nk_context*, const char *title, nk_flags);
+NK_API int nk_group_scrolled_offset_begin(struct nk_context*, nk_uint *x_offset, nk_uint *y_offset, const char*, nk_flags);
+NK_API int nk_group_scrolled_begin(struct nk_context*, struct nk_scroll*, const char *title, nk_flags);
+NK_API void nk_group_scrolled_end(struct nk_context*);
+NK_API void nk_group_end(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 int 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*);
+/* =============================================================================
+ *
+ *                                  TREE
+ *
+ * ============================================================================= */
+#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__)
+#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_API 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);
+#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__)
+#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_API 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);
+NK_API void nk_tree_pop(struct nk_context*);
+NK_API int nk_tree_state_push(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states *state);
+NK_API int nk_tree_state_image_push(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states *state);
+NK_API void nk_tree_state_pop(struct nk_context*);
+/* =============================================================================
+ *
+ *                                  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 int nk_widget_is_hovered(struct nk_context*);
+NK_API int nk_widget_is_mouse_clicked(struct nk_context*, enum nk_buttons);
+NK_API int nk_widget_has_mouse_click_down(struct nk_context*, enum nk_buttons, int 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);
+#ifdef NK_INCLUDE_STANDARD_VARARGS
+NK_API void nk_labelf(struct nk_context*, nk_flags, const char*, ...);
+NK_API void nk_labelf_colored(struct nk_context*, nk_flags align, struct nk_color, const char*,...);
+NK_API void nk_labelf_wrap(struct nk_context*, const char*,...);
+NK_API void nk_labelf_colored_wrap(struct nk_context*, struct nk_color, const char*,...);
+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 int nk_button_text(struct nk_context*, const char *title, int len);
+NK_API int nk_button_label(struct nk_context*, const char *title);
+NK_API int nk_button_color(struct nk_context*, struct nk_color);
+NK_API int nk_button_symbol(struct nk_context*, enum nk_symbol_type);
+NK_API int nk_button_image(struct nk_context*, struct nk_image img);
+NK_API int nk_button_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags text_alignment);
+NK_API int nk_button_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment);
+NK_API int nk_button_image_label(struct nk_context*, struct nk_image img, const char*, nk_flags text_alignment);
+NK_API int nk_button_image_text(struct nk_context*, struct nk_image img, const char*, int, nk_flags alignment);
+NK_API int nk_button_text_styled(struct nk_context*, const struct nk_style_button*, const char *title, int len);
+NK_API int nk_button_label_styled(struct nk_context*, const struct nk_style_button*, const char *title);
+NK_API int nk_button_symbol_styled(struct nk_context*, const struct nk_style_button*, enum nk_symbol_type);
+NK_API int nk_button_image_styled(struct nk_context*, const struct nk_style_button*, struct nk_image img);
+NK_API int 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 int 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 int 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 int 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 int nk_button_push_behavior(struct nk_context*, enum nk_button_behavior);
+NK_API int nk_button_pop_behavior(struct nk_context*);
+/* =============================================================================
+ *
+ *                                  CHECKBOX
+ *
+ * ============================================================================= */
+NK_API int nk_check_label(struct nk_context*, const char*, int active);
+NK_API int nk_check_text(struct nk_context*, const char*, int,int 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 int nk_checkbox_label(struct nk_context*, const char*, int *active);
+NK_API int nk_checkbox_text(struct nk_context*, const char*, int, int *active);
+NK_API int nk_checkbox_flags_label(struct nk_context*, const char*, unsigned int *flags, unsigned int value);
+NK_API int nk_checkbox_flags_text(struct nk_context*, const char*, int, unsigned int *flags, unsigned int value);
+/* =============================================================================
+ *
+ *                                  RADIO BUTTON
+ *
+ * ============================================================================= */
+NK_API int nk_radio_label(struct nk_context*, const char*, int *active);
+NK_API int nk_radio_text(struct nk_context*, const char*, int, int *active);
+NK_API int nk_option_label(struct nk_context*, const char*, int active);
+NK_API int nk_option_text(struct nk_context*, const char*, int, int active);
+/* =============================================================================
+ *
+ *                                  SELECTABLE
+ *
+ * ============================================================================= */
+NK_API int nk_selectable_label(struct nk_context*, const char*, nk_flags align, int *value);
+NK_API int nk_selectable_text(struct nk_context*, const char*, int, nk_flags align, int *value);
+NK_API int nk_selectable_image_label(struct nk_context*,struct nk_image,  const char*, nk_flags align, int *value);
+NK_API int nk_selectable_image_text(struct nk_context*,struct nk_image, const char*, int, nk_flags align, int *value);
+NK_API int nk_select_label(struct nk_context*, const char*, nk_flags align, int value);
+NK_API int nk_select_text(struct nk_context*, const char*, int, nk_flags align, int value);
+NK_API int nk_select_image_label(struct nk_context*, struct nk_image,const char*, nk_flags align, int value);
+NK_API int nk_select_image_text(struct nk_context*, struct nk_image,const char*, int, nk_flags align, int 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 int nk_slider_float(struct nk_context*, float min, float *val, float max, float step);
+NK_API int nk_slider_int(struct nk_context*, int min, int *val, int max, int step);
+/* =============================================================================
+ *
+ *                                  PROGRESSBAR
+ *
+ * ============================================================================= */
+NK_API int nk_progress(struct nk_context*, nk_size *cur, nk_size max, int modifyable);
+NK_API nk_size nk_prog(struct nk_context*, nk_size cur, nk_size max, int modifyable);
+
+/* =============================================================================
+ *
+ *                                  COLOR PICKER
+ *
+ * ============================================================================= */
+NK_API struct nk_color nk_color_picker(struct nk_context*, struct nk_color, enum nk_color_format);
+NK_API int nk_color_pick(struct nk_context*, struct nk_color*, enum nk_color_format);
+/* =============================================================================
+ *
+ *                                  PROPERTIES
+ *
+ * ============================================================================= */
+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_API void nk_property_float(struct nk_context*, const char *name, float min, float *val, float max, float step, float inc_per_pixel);
+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_API int nk_propertyi(struct nk_context*, const char *name, int min, int val, int max, int step, float inc_per_pixel);
+NK_API float nk_propertyf(struct nk_context*, const char *name, float min, float val, float max, float step, float inc_per_pixel);
+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 int nk_chart_begin(struct nk_context*, enum nk_chart_type, int num, float min, float max);
+NK_API int 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 int 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*);
+/* =============================================================================
+ *
+ *                                  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 int nk_combo_begin_text(struct nk_context*, const char *selected, int, struct nk_vec2 size);
+NK_API int nk_combo_begin_label(struct nk_context*, const char *selected, struct nk_vec2 size);
+NK_API int nk_combo_begin_color(struct nk_context*, struct nk_color color, struct nk_vec2 size);
+NK_API int nk_combo_begin_symbol(struct nk_context*,  enum nk_symbol_type,  struct nk_vec2 size);
+NK_API int nk_combo_begin_symbol_label(struct nk_context*, const char *selected, enum nk_symbol_type, struct nk_vec2 size);
+NK_API int nk_combo_begin_symbol_text(struct nk_context*, const char *selected, int, enum nk_symbol_type, struct nk_vec2 size);
+NK_API int nk_combo_begin_image(struct nk_context*, struct nk_image img,  struct nk_vec2 size);
+NK_API int nk_combo_begin_image_label(struct nk_context*, const char *selected, struct nk_image, struct nk_vec2 size);
+NK_API int nk_combo_begin_image_text(struct nk_context*,  const char *selected, int, struct nk_image, struct nk_vec2 size);
+NK_API int nk_combo_item_label(struct nk_context*, const char*, nk_flags alignment);
+NK_API int nk_combo_item_text(struct nk_context*, const char*,int, nk_flags alignment);
+NK_API int nk_combo_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment);
+NK_API int nk_combo_item_image_text(struct nk_context*, struct nk_image, const char*, int,nk_flags alignment);
+NK_API int nk_combo_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment);
+NK_API int 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 int nk_contextual_begin(struct nk_context*, nk_flags, struct nk_vec2, struct nk_rect trigger_bounds);
+NK_API int nk_contextual_item_text(struct nk_context*, const char*, int,nk_flags align);
+NK_API int nk_contextual_item_label(struct nk_context*, const char*, nk_flags align);
+NK_API int nk_contextual_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment);
+NK_API int nk_contextual_item_image_text(struct nk_context*, struct nk_image, const char*, int len, nk_flags alignment);
+NK_API int nk_contextual_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment);
+NK_API int 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*);
+NK_API int 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 int nk_menu_begin_text(struct nk_context*, const char* title, int title_len, nk_flags align, struct nk_vec2 size);
+NK_API int nk_menu_begin_label(struct nk_context*, const char*, nk_flags align, struct nk_vec2 size);
+NK_API int nk_menu_begin_image(struct nk_context*, const char*, struct nk_image, struct nk_vec2 size);
+NK_API int nk_menu_begin_image_text(struct nk_context*, const char*, int,nk_flags align,struct nk_image, struct nk_vec2 size);
+NK_API int nk_menu_begin_image_label(struct nk_context*, const char*, nk_flags align,struct nk_image, struct nk_vec2 size);
+NK_API int nk_menu_begin_symbol(struct nk_context*, const char*, enum nk_symbol_type, struct nk_vec2 size);
+NK_API int nk_menu_begin_symbol_text(struct nk_context*, const char*, int,nk_flags align,enum nk_symbol_type, struct nk_vec2 size);
+NK_API int nk_menu_begin_symbol_label(struct nk_context*, const char*, nk_flags align,enum nk_symbol_type, struct nk_vec2 size);
+NK_API int nk_menu_item_text(struct nk_context*, const char*, int,nk_flags align);
+NK_API int nk_menu_item_label(struct nk_context*, const char*, nk_flags alignment);
+NK_API int nk_menu_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment);
+NK_API int nk_menu_item_image_text(struct nk_context*, struct nk_image, const char*, int len, nk_flags alignment);
+NK_API int nk_menu_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment);
+NK_API int 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 int 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 int nk_style_push_font(struct nk_context*, const struct nk_user_font*);
+NK_API int nk_style_push_float(struct nk_context*, float*, float);
+NK_API int nk_style_push_vec2(struct nk_context*, struct nk_vec2*, struct nk_vec2);
+NK_API int nk_style_push_style_item(struct nk_context*, struct nk_style_item*, struct nk_style_item);
+NK_API int nk_style_push_flags(struct nk_context*, nk_flags*, nk_flags);
+NK_API int nk_style_push_color(struct nk_context*, struct nk_color*, struct nk_color);
+
+NK_API int nk_style_pop_font(struct nk_context*);
+NK_API int nk_style_pop_float(struct nk_context*);
+NK_API int nk_style_pop_vec2(struct nk_context*);
+NK_API int nk_style_pop_style_item(struct nk_context*);
+NK_API int nk_style_pop_flags(struct nk_context*);
+NK_API int 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_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_hex(const char *rgb);
+
+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 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 int 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);
+NK_API double nk_strtod(const char *str, const char **endptr);
+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_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_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 {
+    int 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 int nk_filter_default(const struct nk_text_edit*, nk_rune unicode);
+NK_API int nk_filter_ascii(const struct nk_text_edit*, nk_rune unicode);
+NK_API int nk_filter_float(const struct nk_text_edit*, nk_rune unicode);
+NK_API int nk_filter_decimal(const struct nk_text_edit*, nk_rune unicode);
+NK_API int nk_filter_hex(const struct nk_text_edit*, nk_rune unicode);
+NK_API int nk_filter_oct(const struct nk_text_edit*, nk_rune unicode);
+NK_API int 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 int nk_textedit_cut(struct nk_text_edit*);
+NK_API int 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 {
+    int 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 {
+    int 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 int nk_input_has_mouse_click(const struct nk_input*, enum nk_buttons);
+NK_API int nk_input_has_mouse_click_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect);
+NK_API int nk_input_has_mouse_click_down_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect, int down);
+NK_API int nk_input_is_mouse_click_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect);
+NK_API int nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, struct nk_rect b, int down);
+NK_API int nk_input_any_mouse_click_in_rect(const struct nk_input*, struct nk_rect);
+NK_API int nk_input_is_mouse_prev_hovering_rect(const struct nk_input*, struct nk_rect);
+NK_API int nk_input_is_mouse_hovering_rect(const struct nk_input*, struct nk_rect);
+NK_API int nk_input_mouse_clicked(const struct nk_input*, enum nk_buttons, struct nk_rect);
+NK_API int nk_input_is_mouse_down(const struct nk_input*, enum nk_buttons);
+NK_API int nk_input_is_mouse_pressed(const struct nk_input*, enum nk_buttons);
+NK_API int nk_input_is_mouse_released(const struct nk_input*, enum nk_buttons);
+NK_API int nk_input_is_key_pressed(const struct nk_input*, enum nk_keys);
+NK_API int nk_input_is_key_released(const struct nk_input*, enum nk_keys);
+NK_API int 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.
+*/
+typedef nk_ushort nk_draw_index;
+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);
+NK_API void nk_draw_list_clear(struct nk_draw_list*);
+
+/* 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*);
+NK_API void nk_draw_list_clear(struct nk_draw_list *list);
+
+/* 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_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;
+    int 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;
+    int 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)) || ((x1 + w1) < x0) || (y1 > (y0 + h0)) || (y1 + h1) < y0)))
+#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 <stdint.h> */
+# 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<typename T> struct nk_alignof;
+template<typename T, int size_diff> struct nk_helper{enum {value = size_diff};};
+template<typename T> struct nk_helper<T,0>{enum {value = nk_alignof<T>::value};};
+template<typename T> struct nk_alignof{struct Big {T x; char c;}; enum {
+    diff = sizeof(Big) - sizeof(T), value = nk_helper<Big, diff>::value};};
+#define NK_ALIGNOF(t) (nk_alignof<t>::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_ */
+/*
+ * ==============================================================
+ *
+ *                          IMPLEMENTATION
+ *
+ * ===============================================================
+ */
+#ifdef NK_IMPLEMENTATION
+
+#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 <stdlib.h> /* malloc, free */
+#endif
+#ifdef NK_INCLUDE_STANDARD_IO
+#include <stdio.h> /* fopen, fclose,... */
+#endif
+#ifdef NK_INCLUDE_STANDARD_VARARGS
+#include <stdarg.h> /* valist, va_start, va_end, ... */
+#endif
+#ifndef NK_ASSERT
+#include <assert.h>
+#define NK_ASSERT(expr) assert(expr)
+#endif
+
+#ifndef NK_MEMSET
+#define NK_MEMSET nk_memset
+#endif
+#ifndef NK_MEMCPY
+#define NK_MEMCPY nk_memcopy
+#endif
+#ifndef NK_SQRT
+#define NK_SQRT nk_sqrt
+#endif
+#ifndef NK_SIN
+#define NK_SIN nk_sin
+#endif
+#ifndef NK_COS
+#define NK_COS nk_cos
+#endif
+#ifndef NK_STRTOD
+#define NK_STRTOD nk_strtod
+#endif
+#ifndef NK_DTOA
+#define NK_DTOA nk_dtoa
+#endif
+
+#define NK_DEFAULT (-1)
+
+#ifndef NK_VSNPRINTF
+/* If your compiler does support `vsnprintf` I would highly recommend
+ * defining this to vsnprintf instead since `vsprintf` is basically
+ * unbelievable unsafe and should *NEVER* be used. But I have to support
+ * it since C89 only provides this unsafe version. */
+  #if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) ||\
+      (defined(__cplusplus) && (__cplusplus >= 201103L)) || \
+      (defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)) ||\
+      (defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)) ||\
+       defined(_ISOC99_SOURCE) || defined(_BSD_SOURCE)
+      #define NK_VSNPRINTF(s,n,f,a) vsnprintf(s,n,f,a)
+  #else
+    #define NK_VSNPRINTF(s,n,f,a) vsprintf(s,f,a)
+  #endif
+#endif
+
+#define NK_SCHAR_MIN (-127)
+#define NK_SCHAR_MAX 127
+#define NK_UCHAR_MIN 0
+#define NK_UCHAR_MAX 256
+#define NK_SSHORT_MIN (-32767)
+#define NK_SSHORT_MAX 32767
+#define NK_USHORT_MIN 0
+#define NK_USHORT_MAX 65535
+#define NK_SINT_MIN (-2147483647)
+#define NK_SINT_MAX 2147483647
+#define NK_UINT_MIN 0
+#define NK_UINT_MAX 4294967295u
+
+/* 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_size) >= sizeof(void*));
+NK_STATIC_ASSERT(sizeof(nk_ptr) == sizeof(void*));
+NK_STATIC_ASSERT(sizeof(nk_flags) >= 4);
+NK_STATIC_ASSERT(sizeof(nk_rune) >= 4);
+NK_STATIC_ASSERT(sizeof(nk_ushort) == 2);
+NK_STATIC_ASSERT(sizeof(nk_short) == 2);
+NK_STATIC_ASSERT(sizeof(nk_uint) == 4);
+NK_STATIC_ASSERT(sizeof(nk_int) == 4);
+NK_STATIC_ASSERT(sizeof(nk_byte) == 1);
+
+NK_GLOBAL const struct nk_rect nk_null_rect = {-8192.0f, -8192.0f, 16384, 16384};
+#define NK_FLOAT_PRECISION 0.00000000000001
+
+NK_GLOBAL const struct nk_color nk_red = {255,0,0,255};
+NK_GLOBAL const struct nk_color nk_green = {0,255,0,255};
+NK_GLOBAL const struct nk_color nk_blue = {0,0,255,255};
+NK_GLOBAL const struct nk_color nk_white = {255,255,255,255};
+NK_GLOBAL const struct nk_color nk_black = {0,0,0,255};
+NK_GLOBAL const struct nk_color nk_yellow = {255,255,0,255};
+
+/*
+ * ==============================================================
+ *
+ *                          MATH
+ *
+ * ===============================================================
+ */
+/*  Since nuklear is supposed to work on all systems providing floating point
+    math without any dependencies I also had to implement my own math functions
+    for sqrt, sin and cos. Since the actual highly accurate implementations for
+    the standard library functions are quite complex and I do not need high
+    precision for my use cases I use approximations.
+
+    Sqrt
+    ----
+    For square root nuklear uses the famous fast inverse square root:
+    https://en.wikipedia.org/wiki/Fast_inverse_square_root with
+    slightly tweaked magic constant. While on today's hardware it is
+    probably not faster it is still fast and accurate enough for
+    nuklear's use cases. IMPORTANT: this requires float format IEEE 754
+
+    Sine/Cosine
+    -----------
+    All constants inside both function are generated Remez's minimax
+    approximations for value range 0...2*PI. The reason why I decided to
+    approximate exactly that range is that nuklear only needs sine and
+    cosine to generate circles which only requires that exact range.
+    In addition I used Remez instead of Taylor for additional precision:
+    www.lolengine.net/blog/2011/12/21/better-function-approximations.
+
+    The tool I used to generate constants for both sine and cosine
+    (it can actually approximate a lot more functions) can be
+    found here: www.lolengine.net/wiki/oss/lolremez
+*/
+NK_INTERN float
+nk_inv_sqrt(float number)
+{
+    float x2;
+    const float threehalfs = 1.5f;
+    union {nk_uint i; float f;} conv = {0};
+    conv.f = number;
+    x2 = number * 0.5f;
+    conv.i = 0x5f375A84 - (conv.i >> 1);
+    conv.f = conv.f * (threehalfs - (x2 * conv.f * conv.f));
+    return conv.f;
+}
+
+NK_INTERN float
+nk_sqrt(float x)
+{
+    return x * nk_inv_sqrt(x);
+}
+
+NK_INTERN float
+nk_sin(float x)
+{
+    NK_STORAGE const float a0 = +1.91059300966915117e-31f;
+    NK_STORAGE const float a1 = +1.00086760103908896f;
+    NK_STORAGE const float a2 = -1.21276126894734565e-2f;
+    NK_STORAGE const float a3 = -1.38078780785773762e-1f;
+    NK_STORAGE const float a4 = -2.67353392911981221e-2f;
+    NK_STORAGE const float a5 = +2.08026600266304389e-2f;
+    NK_STORAGE const float a6 = -3.03996055049204407e-3f;
+    NK_STORAGE const float a7 = +1.38235642404333740e-4f;
+    return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*a7))))));
+}
+
+NK_INTERN float
+nk_cos(float x)
+{
+    NK_STORAGE const float a0 = +1.00238601909309722f;
+    NK_STORAGE const float a1 = -3.81919947353040024e-2f;
+    NK_STORAGE const float a2 = -3.94382342128062756e-1f;
+    NK_STORAGE const float a3 = -1.18134036025221444e-1f;
+    NK_STORAGE const float a4 = +1.07123798512170878e-1f;
+    NK_STORAGE const float a5 = -1.86637164165180873e-2f;
+    NK_STORAGE const float a6 = +9.90140908664079833e-4f;
+    NK_STORAGE const float a7 = -5.23022132118824778e-14f;
+    return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*a7))))));
+}
+
+NK_INTERN nk_uint
+nk_round_up_pow2(nk_uint v)
+{
+    v--;
+    v |= v >> 1;
+    v |= v >> 2;
+    v |= v >> 4;
+    v |= v >> 8;
+    v |= v >> 16;
+    v++;
+    return v;
+}
+
+NK_API struct nk_rect
+nk_get_null_rect(void)
+{
+    return nk_null_rect;
+}
+
+NK_API struct nk_rect
+nk_rect(float x, float y, float w, float h)
+{
+    struct nk_rect r;
+    r.x = x; r.y = y;
+    r.w = w; r.h = h;
+    return r;
+}
+
+NK_API struct nk_rect
+nk_recti(int x, int y, int w, int h)
+{
+    struct nk_rect r;
+    r.x = (float)x;
+    r.y = (float)y;
+    r.w = (float)w;
+    r.h = (float)h;
+    return r;
+}
+
+NK_API struct nk_rect
+nk_recta(struct nk_vec2 pos, struct nk_vec2 size)
+{
+    return nk_rect(pos.x, pos.y, size.x, size.y);
+}
+
+NK_API struct nk_rect
+nk_rectv(const float *r)
+{
+    return nk_rect(r[0], r[1], r[2], r[3]);
+}
+
+NK_API struct nk_rect
+nk_rectiv(const int *r)
+{
+    return nk_recti(r[0], r[1], r[2], r[3]);
+}
+
+NK_API struct nk_vec2
+nk_rect_pos(struct nk_rect r)
+{
+    struct nk_vec2 ret;
+    ret.x = r.x; ret.y = r.y;
+    return ret;
+}
+
+NK_API struct nk_vec2
+nk_rect_size(struct nk_rect r)
+{
+    struct nk_vec2 ret;
+    ret.x = r.w; ret.y = r.h;
+    return ret;
+}
+
+NK_INTERN struct nk_rect
+nk_shrink_rect(struct nk_rect r, float amount)
+{
+    struct nk_rect res;
+    r.w = NK_MAX(r.w, 2 * amount);
+    r.h = NK_MAX(r.h, 2 * amount);
+    res.x = r.x + amount;
+    res.y = r.y + amount;
+    res.w = r.w - 2 * amount;
+    res.h = r.h - 2 * amount;
+    return res;
+}
+
+NK_INTERN struct nk_rect
+nk_pad_rect(struct nk_rect r, struct nk_vec2 pad)
+{
+    r.w = NK_MAX(r.w, 2 * pad.x);
+    r.h = NK_MAX(r.h, 2 * pad.y);
+    r.x += pad.x; r.y += pad.y;
+    r.w -= 2 * pad.x;
+    r.h -= 2 * pad.y;
+    return r;
+}
+
+NK_API struct nk_vec2
+nk_vec2(float x, float y)
+{
+    struct nk_vec2 ret;
+    ret.x = x; ret.y = y;
+    return ret;
+}
+
+NK_API struct nk_vec2
+nk_vec2i(int x, int y)
+{
+    struct nk_vec2 ret;
+    ret.x = (float)x;
+    ret.y = (float)y;
+    return ret;
+}
+
+NK_API struct nk_vec2
+nk_vec2v(const float *v)
+{
+    return nk_vec2(v[0], v[1]);
+}
+
+NK_API struct nk_vec2
+nk_vec2iv(const int *v)
+{
+    return nk_vec2i(v[0], v[1]);
+}
+
+/*
+ * ==============================================================
+ *
+ *                          UTIL
+ *
+ * ===============================================================
+ */
+NK_INTERN int nk_str_match_here(const char *regexp, const char *text);
+NK_INTERN int nk_str_match_star(int c, const char *regexp, const char *text);
+NK_INTERN int nk_is_lower(int c) {return (c >= 'a' && c <= 'z') || (c >= 0xE0 && c <= 0xFF);}
+NK_INTERN int nk_is_upper(int c){return (c >= 'A' && c <= 'Z') || (c >= 0xC0 && c <= 0xDF);}
+NK_INTERN int nk_to_upper(int c) {return (c >= 'a' && c <= 'z') ? (c - ('a' - 'A')) : c;}
+NK_INTERN int nk_to_lower(int c) {return (c >= 'A' && c <= 'Z') ? (c - ('a' + 'A')) : c;}
+
+NK_INTERN void*
+nk_memcopy(void *dst0, const void *src0, nk_size length)
+{
+    nk_ptr t;
+    char *dst = (char*)dst0;
+    const char *src = (const char*)src0;
+    if (length == 0 || dst == src)
+        goto done;
+
+    #define nk_word int
+    #define nk_wsize sizeof(nk_word)
+    #define nk_wmask (nk_wsize-1)
+    #define NK_TLOOP(s) if (t) NK_TLOOP1(s)
+    #define NK_TLOOP1(s) do { s; } while (--t)
+
+    if (dst < src) {
+        t = (nk_ptr)src; /* only need low bits */
+        if ((t | (nk_ptr)dst) & nk_wmask) {
+            if ((t ^ (nk_ptr)dst) & nk_wmask || length < nk_wsize)
+                t = length;
+            else
+                t = nk_wsize - (t & nk_wmask);
+            length -= t;
+            NK_TLOOP1(*dst++ = *src++);
+        }
+        t = length / nk_wsize;
+        NK_TLOOP(*(nk_word*)(void*)dst = *(const nk_word*)(const void*)src;
+            src += nk_wsize; dst += nk_wsize);
+        t = length & nk_wmask;
+        NK_TLOOP(*dst++ = *src++);
+    } else {
+        src += length;
+        dst += length;
+        t = (nk_ptr)src;
+        if ((t | (nk_ptr)dst) & nk_wmask) {
+            if ((t ^ (nk_ptr)dst) & nk_wmask || length <= nk_wsize)
+                t = length;
+            else
+                t &= nk_wmask;
+            length -= t;
+            NK_TLOOP1(*--dst = *--src);
+        }
+        t = length / nk_wsize;
+        NK_TLOOP(src -= nk_wsize; dst -= nk_wsize;
+            *(nk_word*)(void*)dst = *(const nk_word*)(const void*)src);
+        t = length & nk_wmask;
+        NK_TLOOP(*--dst = *--src);
+    }
+    #undef nk_word
+    #undef nk_wsize
+    #undef nk_wmask
+    #undef NK_TLOOP
+    #undef NK_TLOOP1
+done:
+    return (dst0);
+}
+
+NK_INTERN void
+nk_memset(void *ptr, int c0, nk_size size)
+{
+    #define nk_word unsigned
+    #define nk_wsize sizeof(nk_word)
+    #define nk_wmask (nk_wsize - 1)
+    nk_byte *dst = (nk_byte*)ptr;
+    unsigned c = 0;
+    nk_size t = 0;
+
+    if ((c = (nk_byte)c0) != 0) {
+        c = (c << 8) | c; /* at least 16-bits  */
+        if (sizeof(unsigned int) > 2)
+            c = (c << 16) | c; /* at least 32-bits*/
+    }
+
+    /* too small of a word count */
+    dst = (nk_byte*)ptr;
+    if (size < 3 * nk_wsize) {
+        while (size--) *dst++ = (nk_byte)c0;
+        return;
+    }
+
+    /* align destination */
+    if ((t = NK_PTR_TO_UINT(dst) & nk_wmask) != 0) {
+        t = nk_wsize -t;
+        size -= t;
+        do {
+            *dst++ = (nk_byte)c0;
+        } while (--t != 0);
+    }
+
+    /* fill word */
+    t = size / nk_wsize;
+    do {
+        *(nk_word*)((void*)dst) = c;
+        dst += nk_wsize;
+    } while (--t != 0);
+
+    /* fill trailing bytes */
+    t = (size & nk_wmask);
+    if (t != 0) {
+        do {
+            *dst++ = (nk_byte)c0;
+        } while (--t != 0);
+    }
+
+    #undef nk_word
+    #undef nk_wsize
+    #undef nk_wmask
+}
+
+NK_INTERN void
+nk_zero(void *ptr, nk_size size)
+{
+    NK_ASSERT(ptr);
+    NK_MEMSET(ptr, 0, size);
+}
+
+NK_API int
+nk_strlen(const char *str)
+{
+    int siz = 0;
+    NK_ASSERT(str);
+    while (str && *str++ != '\0') siz++;
+    return siz;
+}
+
+NK_API int
+nk_strtoi(const char *str, const char **endptr)
+{
+    int neg = 1;
+    const char *p = str;
+    int value = 0;
+
+    NK_ASSERT(str);
+    if (!str) return 0;
+
+    /* skip whitespace */
+    while (*p == ' ') p++;
+    if (*p == '-') {
+        neg = -1;
+        p++;
+    }
+    while (*p && *p >= '0' && *p <= '9') {
+        value = value * 10 + (int) (*p - '0');
+        p++;
+    }
+    if (endptr)
+        *endptr = p;
+    return neg*value;
+}
+
+NK_API double
+nk_strtod(const char *str, const char **endptr)
+{
+    double m;
+    double neg = 1.0;
+    const char *p = str;
+    double value = 0;
+    double number = 0;
+
+    NK_ASSERT(str);
+    if (!str) return 0;
+
+    /* skip whitespace */
+    while (*p == ' ') p++;
+    if (*p == '-') {
+        neg = -1.0;
+        p++;
+    }
+
+    while (*p && *p != '.' && *p != 'e') {
+        value = value * 10.0 + (double) (*p - '0');
+        p++;
+    }
+
+    if (*p == '.') {
+        p++;
+        for(m = 0.1; *p && *p != 'e'; p++ ) {
+            value = value + (double) (*p - '0') * m;
+            m *= 0.1;
+        }
+    }
+    if (*p == 'e') {
+        int i, pow, div;
+        p++;
+        if (*p == '-') {
+            div = nk_true;
+            p++;
+        } else if (*p == '+') {
+            div = nk_false;
+            p++;
+        } else div = nk_false;
+
+        for (pow = 0; *p; p++)
+            pow = pow * 10 + (int) (*p - '0');
+
+        for (m = 1.0, i = 0; i < pow; i++)
+            m *= 10.0;
+
+        if (div)
+            value /= m;
+        else value *= m;
+    }
+    number = value * neg;
+    if (endptr)
+        *endptr = p;
+    return number;
+}
+
+NK_API float
+nk_strtof(const char *str, const char **endptr)
+{
+    float float_value;
+    double double_value;
+    double_value = NK_STRTOD(str, endptr);
+    float_value = (float)double_value;
+    return float_value;
+}
+
+NK_API int
+nk_stricmp(const char *s1, const char *s2)
+{
+    nk_int c1,c2,d;
+    do {
+        c1 = *s1++;
+        c2 = *s2++;
+        d = c1 - c2;
+        while (d) {
+            if (c1 <= 'Z' && c1 >= 'A') {
+                d += ('a' - 'A');
+                if (!d) break;
+            }
+            if (c2 <= 'Z' && c2 >= 'A') {
+                d -= ('a' - 'A');
+                if (!d) break;
+            }
+            return ((d >= 0) << 1) - 1;
+        }
+    } while (c1);
+    return 0;
+}
+
+NK_API int
+nk_stricmpn(const char *s1, const char *s2, int n)
+{
+    int c1,c2,d;
+    NK_ASSERT(n >= 0);
+    do {
+        c1 = *s1++;
+        c2 = *s2++;
+        if (!n--) return 0;
+
+        d = c1 - c2;
+        while (d) {
+            if (c1 <= 'Z' && c1 >= 'A') {
+                d += ('a' - 'A');
+                if (!d) break;
+            }
+            if (c2 <= 'Z' && c2 >= 'A') {
+                d -= ('a' - 'A');
+                if (!d) break;
+            }
+            return ((d >= 0) << 1) - 1;
+        }
+    } while (c1);
+    return 0;
+}
+
+NK_INTERN int
+nk_str_match_here(const char *regexp, const char *text)
+{
+    if (regexp[0] == '\0')
+        return 1;
+    if (regexp[1] == '*')
+        return nk_str_match_star(regexp[0], regexp+2, text);
+    if (regexp[0] == '$' && regexp[1] == '\0')
+        return *text == '\0';
+    if (*text!='\0' && (regexp[0]=='.' || regexp[0]==*text))
+        return nk_str_match_here(regexp+1, text+1);
+    return 0;
+}
+
+NK_INTERN int
+nk_str_match_star(int c, const char *regexp, const char *text)
+{
+    do {/* a '* matches zero or more instances */
+        if (nk_str_match_here(regexp, text))
+            return 1;
+    } while (*text != '\0' && (*text++ == c || c == '.'));
+    return 0;
+}
+
+NK_API int
+nk_strfilter(const char *text, const char *regexp)
+{
+    /*
+    c    matches any literal character c
+    .    matches any single character
+    ^    matches the beginning of the input string
+    $    matches the end of the input string
+    *    matches zero or more occurrences of the previous character*/
+    if (regexp[0] == '^')
+        return nk_str_match_here(regexp+1, text);
+    do {    /* must look even if string is empty */
+        if (nk_str_match_here(regexp, text))
+            return 1;
+    } while (*text++ != '\0');
+    return 0;
+}
+
+NK_API int
+nk_strmatch_fuzzy_text(const char *str, int str_len,
+    const char *pattern, int *out_score)
+{
+    /* Returns true if each character in pattern is found sequentially within str
+     * if found then out_score is also set. Score value has no intrinsic meaning.
+     * Range varies with pattern. Can only compare scores with same search pattern. */
+
+    /* ------- scores --------- */
+    /* bonus for adjacent matches */
+    #define NK_ADJACENCY_BONUS 5
+    /* bonus if match occurs after a separator */
+    #define NK_SEPARATOR_BONUS 10
+    /* bonus if match is uppercase and prev is lower */
+    #define NK_CAMEL_BONUS 10
+    /* penalty applied for every letter in str before the first match */
+    #define NK_LEADING_LETTER_PENALTY (-3)
+    /* maximum penalty for leading letters */
+    #define NK_MAX_LEADING_LETTER_PENALTY (-9)
+    /* penalty for every letter that doesn't matter */
+    #define NK_UNMATCHED_LETTER_PENALTY (-1)
+
+    /* loop variables */
+    int score = 0;
+    char const * pattern_iter = pattern;
+    int str_iter = 0;
+    int prev_matched = nk_false;
+    int prev_lower = nk_false;
+    /* true so if first letter match gets separator bonus*/
+    int prev_separator = nk_true;
+
+    /* use "best" matched letter if multiple string letters match the pattern */
+    char const * best_letter = 0;
+    int best_letter_score = 0;
+
+    /* loop over strings */
+    NK_ASSERT(str);
+    NK_ASSERT(pattern);
+    if (!str || !str_len || !pattern) return 0;
+    while (str_iter < str_len)
+    {
+        const char pattern_letter = *pattern_iter;
+        const char str_letter = str[str_iter];
+
+        int next_match = *pattern_iter != '\0' &&
+            nk_to_lower(pattern_letter) == nk_to_lower(str_letter);
+        int rematch = best_letter && nk_to_upper(*best_letter) == nk_to_upper(str_letter);
+
+        int advanced = next_match && best_letter;
+        int pattern_repeat = best_letter && *pattern_iter != '\0';
+        pattern_repeat = pattern_repeat &&
+            nk_to_lower(*best_letter) == nk_to_lower(pattern_letter);
+
+        if (advanced || pattern_repeat) {
+            score += best_letter_score;
+            best_letter = 0;
+            best_letter_score = 0;
+        }
+
+        if (next_match || rematch)
+        {
+            int new_score = 0;
+            /* Apply penalty for each letter before the first pattern match */
+            if (pattern_iter == pattern) {
+                int count = (int)(&str[str_iter] - str);
+                int penalty = NK_LEADING_LETTER_PENALTY * count;
+                if (penalty < NK_MAX_LEADING_LETTER_PENALTY)
+                    penalty = NK_MAX_LEADING_LETTER_PENALTY;
+
+                score += penalty;
+            }
+
+            /* apply bonus for consecutive bonuses */
+            if (prev_matched)
+                new_score += NK_ADJACENCY_BONUS;
+
+            /* apply bonus for matches after a separator */
+            if (prev_separator)
+                new_score += NK_SEPARATOR_BONUS;
+
+            /* apply bonus across camel case boundaries */
+            if (prev_lower && nk_is_upper(str_letter))
+                new_score += NK_CAMEL_BONUS;
+
+            /* update pattern iter IFF the next pattern letter was matched */
+            if (next_match)
+                ++pattern_iter;
+
+            /* update best letter in str which may be for a "next" letter or a rematch */
+            if (new_score >= best_letter_score) {
+                /* apply penalty for now skipped letter */
+                if (best_letter != 0)
+                    score += NK_UNMATCHED_LETTER_PENALTY;
+
+                best_letter = &str[str_iter];
+                best_letter_score = new_score;
+            }
+            prev_matched = nk_true;
+        } else {
+            score += NK_UNMATCHED_LETTER_PENALTY;
+            prev_matched = nk_false;
+        }
+
+        /* separators should be more easily defined */
+        prev_lower = nk_is_lower(str_letter) != 0;
+        prev_separator = str_letter == '_' || str_letter == ' ';
+
+        ++str_iter;
+    }
+
+    /* apply score for last match */
+    if (best_letter)
+        score += best_letter_score;
+
+    /* did not match full pattern */
+    if (*pattern_iter != '\0')
+        return nk_false;
+
+    if (out_score)
+        *out_score = score;
+    return nk_true;
+}
+
+NK_API int
+nk_strmatch_fuzzy_string(char const *str, char const *pattern, int *out_score)
+{return nk_strmatch_fuzzy_text(str, nk_strlen(str), pattern, out_score);}
+
+NK_INTERN int
+nk_string_float_limit(char *string, int prec)
+{
+    int dot = 0;
+    char *c = string;
+    while (*c) {
+        if (*c == '.') {
+            dot = 1;
+            c++;
+            continue;
+        }
+        if (dot == (prec+1)) {
+            *c = 0;
+            break;
+        }
+        if (dot > 0) dot++;
+        c++;
+    }
+    return (int)(c - string);
+}
+
+NK_INTERN double
+nk_pow(double x, int n)
+{
+    /*  check the sign of n */
+    double r = 1;
+    int plus = n >= 0;
+    n = (plus) ? n : -n;
+    while (n > 0) {
+        if ((n & 1) == 1)
+            r *= x;
+        n /= 2;
+        x *= x;
+    }
+    return plus ? r : 1.0 / r;
+}
+
+NK_INTERN int
+nk_ifloord(double x)
+{
+    x = (double)((int)x - ((x < 0.0) ? 1 : 0));
+    return (int)x;
+}
+
+NK_INTERN int
+nk_ifloorf(float x)
+{
+    x = (float)((int)x - ((x < 0.0f) ? 1 : 0));
+    return (int)x;
+}
+
+NK_INTERN int
+nk_iceilf(float x)
+{
+    if (x >= 0) {
+        int i = (int)x;
+        return (x > i) ? i+1: i;
+    } else {
+        int t = (int)x;
+        float r = x - (float)t;
+        return (r > 0.0f) ? t+1: t;
+    }
+}
+
+NK_INTERN int
+nk_log10(double n)
+{
+    int neg;
+    int ret;
+    int exp = 0;
+
+    neg = (n < 0) ? 1 : 0;
+    ret = (neg) ? (int)-n : (int)n;
+    while ((ret / 10) > 0) {
+        ret /= 10;
+        exp++;
+    }
+    if (neg) exp = -exp;
+    return exp;
+}
+
+NK_INTERN void
+nk_strrev_ascii(char *s)
+{
+    int len = nk_strlen(s);
+    int end = len / 2;
+    int i = 0;
+    char t;
+    for (; i < end; ++i) {
+        t = s[i];
+        s[i] = s[len - 1 - i];
+        s[len -1 - i] = t;
+    }
+}
+
+NK_INTERN char*
+nk_itoa(char *s, long n)
+{
+    long i = 0;
+    if (n == 0) {
+        s[i++] = '0';
+        s[i] = 0;
+        return s;
+    }
+    if (n < 0) {
+        s[i++] = '-';
+        n = -n;
+    }
+    while (n > 0) {
+        s[i++] = (char)('0' + (n % 10));
+        n /= 10;
+    }
+    s[i] = 0;
+    if (s[0] == '-')
+        ++s;
+
+    nk_strrev_ascii(s);
+    return s;
+}
+
+NK_INTERN char*
+nk_dtoa(char *s, double n)
+{
+    int useExp = 0;
+    int digit = 0, m = 0, m1 = 0;
+    char *c = s;
+    int neg = 0;
+
+    NK_ASSERT(s);
+    if (!s) return 0;
+
+    if (n == 0.0) {
+        s[0] = '0'; s[1] = '\0';
+        return s;
+    }
+
+    neg = (n < 0);
+    if (neg) n = -n;
+
+    /* calculate magnitude */
+    m = nk_log10(n);
+    useExp = (m >= 14 || (neg && m >= 9) || m <= -9);
+    if (neg) *(c++) = '-';
+
+    /* set up for scientific notation */
+    if (useExp) {
+        if (m < 0)
+           m -= 1;
+        n = n / (double)nk_pow(10.0, m);
+        m1 = m;
+        m = 0;
+    }
+    if (m < 1.0) {
+        m = 0;
+    }
+
+    /* convert the number */
+    while (n > NK_FLOAT_PRECISION || m >= 0) {
+        double weight = nk_pow(10.0, m);
+        if (weight > 0) {
+            double t = (double)n / weight;
+            digit = nk_ifloord(t);
+            n -= ((double)digit * weight);
+            *(c++) = (char)('0' + (char)digit);
+        }
+        if (m == 0 && n > 0)
+            *(c++) = '.';
+        m--;
+    }
+
+    if (useExp) {
+        /* convert the exponent */
+        int i, j;
+        *(c++) = 'e';
+        if (m1 > 0) {
+            *(c++) = '+';
+        } else {
+            *(c++) = '-';
+            m1 = -m1;
+        }
+        m = 0;
+        while (m1 > 0) {
+            *(c++) = (char)('0' + (char)(m1 % 10));
+            m1 /= 10;
+            m++;
+        }
+        c -= m;
+        for (i = 0, j = m-1; i<j; i++, j--) {
+            /* swap without temporary */
+            c[i] ^= c[j];
+            c[j] ^= c[i];
+            c[i] ^= c[j];
+        }
+        c += m;
+    }
+    *(c) = '\0';
+    return s;
+}
+
+#ifdef NK_INCLUDE_STANDARD_VARARGS
+#ifndef NK_INCLUDE_STANDARD_IO
+static int
+nk_vsnprintf(char *buf, int buf_size, const char *fmt, va_list args)
+{
+    enum nk_arg_type {
+        NK_ARG_TYPE_CHAR,
+        NK_ARG_TYPE_SHORT,
+        NK_ARG_TYPE_DEFAULT,
+        NK_ARG_TYPE_LONG
+    };
+    enum nk_arg_flags {
+        NK_ARG_FLAG_LEFT = 0x01,
+        NK_ARG_FLAG_PLUS = 0x02,
+        NK_ARG_FLAG_SPACE = 0x04,
+        NK_ARG_FLAG_NUM = 0x10,
+        NK_ARG_FLAG_ZERO = 0x20
+    };
+
+    char number_buffer[NK_MAX_NUMBER_BUFFER];
+    enum nk_arg_type arg_type = NK_ARG_TYPE_DEFAULT;
+    int precision = NK_DEFAULT;
+    int width = NK_DEFAULT;
+    nk_flags flag = 0;
+
+    int len = 0;
+    int result = -1;
+    const char *iter = fmt;
+
+    NK_ASSERT(buf);
+    NK_ASSERT(buf_size);
+    if (!buf || !buf_size || !fmt) return 0;
+    for (iter = fmt; *iter && len < buf_size; iter++) {
+        /* copy all non-format characters */
+        while (*iter && (*iter != '%') && (len < buf_size))
+            buf[len++] = *iter++;
+        if (!(*iter) || len >= buf_size) break;
+        iter++;
+
+        /* flag arguments */
+        while (*iter) {
+            if (*iter == '-') flag |= NK_ARG_FLAG_LEFT;
+            else if (*iter == '+') flag |= NK_ARG_FLAG_PLUS;
+            else if (*iter == ' ') flag |= NK_ARG_FLAG_SPACE;
+            else if (*iter == '#') flag |= NK_ARG_FLAG_NUM;
+            else if (*iter == '0') flag |= NK_ARG_FLAG_ZERO;
+            else break;
+            iter++;
+        }
+
+        /* width argument */
+        width = NK_DEFAULT;
+        if (*iter >= '1' && *iter <= '9') {
+            const char *end;
+            width = nk_strtoi(iter, &end);
+            if (end == iter)
+                width = -1;
+            else iter = end;
+        } else if (*iter == '*') {
+            width = va_arg(args, int);
+            iter++;
+        }
+
+        /* precision argument */
+        precision = NK_DEFAULT;
+        if (*iter == '.') {
+            iter++;
+            if (*iter == '*') {
+                precision = va_arg(args, int);
+                iter++;
+            } else {
+                const char *end;
+                precision = nk_strtoi(iter, &end);
+                if (end == iter)
+                    precision = -1;
+                else iter = end;
+            }
+        }
+
+        /* length modifier */
+        if (*iter == 'h') {
+            if (*(iter+1) == 'h') {
+                arg_type = NK_ARG_TYPE_CHAR;
+                iter++;
+            } else arg_type = NK_ARG_TYPE_SHORT;
+            iter++;
+        } else if (*iter == 'l') {
+            arg_type = NK_ARG_TYPE_LONG;
+            iter++;
+        } else arg_type = NK_ARG_TYPE_DEFAULT;
+
+        /* specifier */
+        if (*iter == '%') {
+            NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT);
+            NK_ASSERT(precision == NK_DEFAULT);
+            NK_ASSERT(width == NK_DEFAULT);
+            if (len < buf_size)
+                buf[len++] = '%';
+        } else if (*iter == 's') {
+            /* string  */
+            const char *str = va_arg(args, const char*);
+            NK_ASSERT(str != buf && "buffer and argument are not allowed to overlap!");
+            NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT);
+            NK_ASSERT(precision == NK_DEFAULT);
+            NK_ASSERT(width == NK_DEFAULT);
+            if (str == buf) return -1;
+            while (str && *str && len < buf_size)
+                buf[len++] = *str++;
+        } else if (*iter == 'n') {
+            /* current length callback */
+            signed int *n = va_arg(args, int*);
+            NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT);
+            NK_ASSERT(precision == NK_DEFAULT);
+            NK_ASSERT(width == NK_DEFAULT);
+            if (n) *n = len;
+        } else if (*iter == 'c' || *iter == 'i' || *iter == 'd') {
+            /* signed integer */
+            long value = 0;
+            const char *num_iter;
+            int num_len, num_print, padding;
+            int cur_precision = NK_MAX(precision, 1);
+            int cur_width = NK_MAX(width, 0);
+
+            /* retrieve correct value type */
+            if (arg_type == NK_ARG_TYPE_CHAR)
+                value = (signed char)va_arg(args, int);
+            else if (arg_type == NK_ARG_TYPE_SHORT)
+                value = (signed short)va_arg(args, int);
+            else if (arg_type == NK_ARG_TYPE_LONG)
+                value = va_arg(args, signed long);
+            else if (*iter == 'c')
+                value = (unsigned char)va_arg(args, int);
+            else value = va_arg(args, signed int);
+
+            /* convert number to string */
+            nk_itoa(number_buffer, value);
+            num_len = nk_strlen(number_buffer);
+            padding = NK_MAX(cur_width - NK_MAX(cur_precision, num_len), 0);
+            if ((flag & NK_ARG_FLAG_PLUS) || (flag & NK_ARG_FLAG_SPACE))
+                padding = NK_MAX(padding-1, 0);
+
+            /* fill left padding up to a total of `width` characters */
+            if (!(flag & NK_ARG_FLAG_LEFT)) {
+                while (padding-- > 0 && (len < buf_size)) {
+                    if ((flag & NK_ARG_FLAG_ZERO) && (precision == NK_DEFAULT))
+                        buf[len++] = '0';
+                    else buf[len++] = ' ';
+                }
+            }
+
+            /* copy string value representation into buffer */
+            if ((flag & NK_ARG_FLAG_PLUS) && value >= 0 && len < buf_size)
+                buf[len++] = '+';
+            else if ((flag & NK_ARG_FLAG_SPACE) && value >= 0 && len < buf_size)
+                buf[len++] = ' ';
+
+            /* fill up to precision number of digits with '0' */
+            num_print = NK_MAX(cur_precision, num_len);
+            while (precision && (num_print > num_len) && (len < buf_size)) {
+                buf[len++] = '0';
+                num_print--;
+            }
+
+            /* copy string value representation into buffer */
+            num_iter = number_buffer;
+            while (precision && *num_iter && len < buf_size)
+                buf[len++] = *num_iter++;
+
+            /* fill right padding up to width characters */
+            if (flag & NK_ARG_FLAG_LEFT) {
+                while ((padding-- > 0) && (len < buf_size))
+                    buf[len++] = ' ';
+            }
+        } else if (*iter == 'o' || *iter == 'x' || *iter == 'X' || *iter == 'u') {
+            /* unsigned integer */
+            unsigned long value = 0;
+            int num_len = 0, num_print, padding = 0;
+            int cur_precision = NK_MAX(precision, 1);
+            int cur_width = NK_MAX(width, 0);
+            unsigned int base = (*iter == 'o') ? 8: (*iter == 'u')? 10: 16;
+
+            /* print oct/hex/dec value */
+            const char *upper_output_format = "0123456789ABCDEF";
+            const char *lower_output_format = "0123456789abcdef";
+            const char *output_format = (*iter == 'x') ?
+                lower_output_format: upper_output_format;
+
+            /* retrieve correct value type */
+            if (arg_type == NK_ARG_TYPE_CHAR)
+                value = (unsigned char)va_arg(args, int);
+            else if (arg_type == NK_ARG_TYPE_SHORT)
+                value = (unsigned short)va_arg(args, int);
+            else if (arg_type == NK_ARG_TYPE_LONG)
+                value = va_arg(args, unsigned long);
+            else value = va_arg(args, unsigned int);
+
+            do {
+                /* convert decimal number into hex/oct number */
+                int digit = output_format[value % base];
+                if (num_len < NK_MAX_NUMBER_BUFFER)
+                    number_buffer[num_len++] = (char)digit;
+                value /= base;
+            } while (value > 0);
+
+            num_print = NK_MAX(cur_precision, num_len);
+            padding = NK_MAX(cur_width - NK_MAX(cur_precision, num_len), 0);
+            if (flag & NK_ARG_FLAG_NUM)
+                padding = NK_MAX(padding-1, 0);
+
+            /* fill left padding up to a total of `width` characters */
+            if (!(flag & NK_ARG_FLAG_LEFT)) {
+                while ((padding-- > 0) && (len < buf_size)) {
+                    if ((flag & NK_ARG_FLAG_ZERO) && (precision == NK_DEFAULT))
+                        buf[len++] = '0';
+                    else buf[len++] = ' ';
+                }
+            }
+
+            /* fill up to precision number of digits */
+            if (num_print && (flag & NK_ARG_FLAG_NUM)) {
+                if ((*iter == 'o') && (len < buf_size)) {
+                    buf[len++] = '0';
+                } else if ((*iter == 'x') && ((len+1) < buf_size)) {
+                    buf[len++] = '0';
+                    buf[len++] = 'x';
+                } else if ((*iter == 'X') && ((len+1) < buf_size)) {
+                    buf[len++] = '0';
+                    buf[len++] = 'X';
+                }
+            }
+            while (precision && (num_print > num_len) && (len < buf_size)) {
+                buf[len++] = '0';
+                num_print--;
+            }
+
+            /* reverse number direction */
+            while (num_len > 0) {
+                if (precision && (len < buf_size))
+                    buf[len++] = number_buffer[num_len-1];
+                num_len--;
+            }
+
+            /* fill right padding up to width characters */
+            if (flag & NK_ARG_FLAG_LEFT) {
+                while ((padding-- > 0) && (len < buf_size))
+                    buf[len++] = ' ';
+            }
+        } else if (*iter == 'f') {
+            /* floating point */
+            const char *num_iter;
+            int cur_precision = (precision < 0) ? 6: precision;
+            int prefix, cur_width = NK_MAX(width, 0);
+            double value = va_arg(args, double);
+            int num_len = 0, frac_len = 0, dot = 0;
+            int padding = 0;
+
+            NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT);
+            NK_DTOA(number_buffer, value);
+            num_len = nk_strlen(number_buffer);
+
+            /* calculate padding */
+            num_iter = number_buffer;
+            while (*num_iter && *num_iter != '.')
+                num_iter++;
+
+            prefix = (*num_iter == '.')?(int)(num_iter - number_buffer)+1:0;
+            padding = NK_MAX(cur_width - (prefix + NK_MIN(cur_precision, num_len - prefix)) , 0);
+            if ((flag & NK_ARG_FLAG_PLUS) || (flag & NK_ARG_FLAG_SPACE))
+                padding = NK_MAX(padding-1, 0);
+
+            /* fill left padding up to a total of `width` characters */
+            if (!(flag & NK_ARG_FLAG_LEFT)) {
+                while (padding-- > 0 && (len < buf_size)) {
+                    if (flag & NK_ARG_FLAG_ZERO)
+                        buf[len++] = '0';
+                    else buf[len++] = ' ';
+                }
+            }
+
+            /* copy string value representation into buffer */
+            num_iter = number_buffer;
+            if ((flag & NK_ARG_FLAG_PLUS) && (value >= 0) && (len < buf_size))
+                buf[len++] = '+';
+            else if ((flag & NK_ARG_FLAG_SPACE) && (value >= 0) && (len < buf_size))
+                buf[len++] = ' ';
+            while (*num_iter) {
+                if (dot) frac_len++;
+                if (len < buf_size)
+                    buf[len++] = *num_iter;
+                if (*num_iter == '.') dot = 1;
+                if (frac_len >= cur_precision) break;
+                num_iter++;
+            }
+
+            /* fill number up to precision */
+            while (frac_len < cur_precision) {
+                if (!dot && len < buf_size) {
+                    buf[len++] = '.';
+                    dot = 1;
+                }
+                if (len < buf_size)
+                    buf[len++] = '0';
+                frac_len++;
+            }
+
+            /* fill right padding up to width characters */
+            if (flag & NK_ARG_FLAG_LEFT) {
+                while ((padding-- > 0) && (len < buf_size))
+                    buf[len++] = ' ';
+            }
+        } else {
+            /* Specifier not supported: g,G,e,E,p,z */
+            NK_ASSERT(0 && "specifier is not supported!");
+            return result;
+        }
+    }
+    buf[(len >= buf_size)?(buf_size-1):len] = 0;
+    result = (len >= buf_size)?-1:len;
+    return result;
+}
+#endif
+
+NK_INTERN int
+nk_strfmt(char *buf, int buf_size, const char *fmt, va_list args)
+{
+    int result = -1;
+    NK_ASSERT(buf);
+    NK_ASSERT(buf_size);
+    if (!buf || !buf_size || !fmt) return 0;
+#ifdef NK_INCLUDE_STANDARD_IO
+    result = NK_VSNPRINTF(buf, (nk_size)buf_size, fmt, args);
+    result = (result >= buf_size) ? -1: result;
+    buf[buf_size-1] = 0;
+#else
+    result = nk_vsnprintf(buf, buf_size, fmt, args);
+#endif
+    return result;
+}
+#endif
+
+NK_API nk_hash
+nk_murmur_hash(const void * key, int len, nk_hash seed)
+{
+    /* 32-Bit MurmurHash3: https://code.google.com/p/smhasher/wiki/MurmurHash3*/
+    #define NK_ROTL(x,r) ((x) << (r) | ((x) >> (32 - r)))
+    union {const nk_uint *i; const nk_byte *b;} conv = {0};
+    const nk_byte *data = (const nk_byte*)key;
+    const int nblocks = len/4;
+    nk_uint h1 = seed;
+    const nk_uint c1 = 0xcc9e2d51;
+    const nk_uint c2 = 0x1b873593;
+    const nk_byte *tail;
+    const nk_uint *blocks;
+    nk_uint k1;
+    int i;
+
+    /* body */
+    if (!key) return 0;
+    conv.b = (data + nblocks*4);
+    blocks = (const nk_uint*)conv.i;
+    for (i = -nblocks; i; ++i) {
+        k1 = blocks[i];
+        k1 *= c1;
+        k1 = NK_ROTL(k1,15);
+        k1 *= c2;
+
+        h1 ^= k1;
+        h1 = NK_ROTL(h1,13);
+        h1 = h1*5+0xe6546b64;
+    }
+
+    /* tail */
+    tail = (const nk_byte*)(data + nblocks*4);
+    k1 = 0;
+    switch (len & 3) {
+    case 3: k1 ^= (nk_uint)(tail[2] << 16);
+    case 2: k1 ^= (nk_uint)(tail[1] << 8u);
+    case 1: k1 ^= tail[0];
+            k1 *= c1;
+            k1 = NK_ROTL(k1,15);
+            k1 *= c2;
+            h1 ^= k1;
+    default: break;
+    }
+
+    /* finalization */
+    h1 ^= (nk_uint)len;
+    /* fmix32 */
+    h1 ^= h1 >> 16;
+    h1 *= 0x85ebca6b;
+    h1 ^= h1 >> 13;
+    h1 *= 0xc2b2ae35;
+    h1 ^= h1 >> 16;
+
+    #undef NK_ROTL
+    return h1;
+}
+
+#ifdef NK_INCLUDE_STANDARD_IO
+NK_INTERN char*
+nk_file_load(const char* path, nk_size* siz, struct nk_allocator *alloc)
+{
+    char *buf;
+    FILE *fd;
+    long ret;
+
+    NK_ASSERT(path);
+    NK_ASSERT(siz);
+    NK_ASSERT(alloc);
+    if (!path || !siz || !alloc)
+        return 0;
+
+    fd = fopen(path, "rb");
+    if (!fd) return 0;
+    fseek(fd, 0, SEEK_END);
+    ret = ftell(fd);
+    if (ret < 0) {
+        fclose(fd);
+        return 0;
+    }
+    *siz = (nk_size)ret;
+    fseek(fd, 0, SEEK_SET);
+    buf = (char*)alloc->alloc(alloc->userdata,0, *siz);
+    NK_ASSERT(buf);
+    if (!buf) {
+        fclose(fd);
+        return 0;
+    }
+    *siz = (nk_size)fread(buf, *siz, 1, fd);
+    fclose(fd);
+    return buf;
+}
+#endif
+
+/*
+ * ==============================================================
+ *
+ *                          COLOR
+ *
+ * ===============================================================
+ */
+NK_INTERN int
+nk_parse_hex(const char *p, int length)
+{
+    int i = 0;
+    int len = 0;
+    while (len < length) {
+        i <<= 4;
+        if (p[len] >= 'a' && p[len] <= 'f')
+            i += ((p[len] - 'a') + 10);
+        else if (p[len] >= 'A' && p[len] <= 'F')
+            i += ((p[len] - 'A') + 10);
+        else i += (p[len] - '0');
+        len++;
+    }
+    return i;
+}
+
+NK_API struct nk_color
+nk_rgba(int r, int g, int b, int a)
+{
+    struct nk_color ret;
+    ret.r = (nk_byte)NK_CLAMP(0, r, 255);
+    ret.g = (nk_byte)NK_CLAMP(0, g, 255);
+    ret.b = (nk_byte)NK_CLAMP(0, b, 255);
+    ret.a = (nk_byte)NK_CLAMP(0, a, 255);
+    return ret;
+}
+
+NK_API struct nk_color
+nk_rgb_hex(const char *rgb)
+{
+    struct nk_color col;
+    const char *c = rgb;
+    if (*c == '#') c++;
+    col.r = (nk_byte)nk_parse_hex(c, 2);
+    col.g = (nk_byte)nk_parse_hex(c+2, 2);
+    col.b = (nk_byte)nk_parse_hex(c+4, 2);
+    col.a = 255;
+    return col;
+}
+
+NK_API struct nk_color
+nk_rgba_hex(const char *rgb)
+{
+    struct nk_color col;
+    const char *c = rgb;
+    if (*c == '#') c++;
+    col.r = (nk_byte)nk_parse_hex(c, 2);
+    col.g = (nk_byte)nk_parse_hex(c+2, 2);
+    col.b = (nk_byte)nk_parse_hex(c+4, 2);
+    col.a = (nk_byte)nk_parse_hex(c+6, 2);
+    return col;
+}
+
+NK_API void
+nk_color_hex_rgba(char *output, struct nk_color col)
+{
+    #define NK_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i))
+    output[0] = (char)NK_TO_HEX((col.r & 0xF0) >> 4);
+    output[1] = (char)NK_TO_HEX((col.r & 0x0F));
+    output[2] = (char)NK_TO_HEX((col.g & 0xF0) >> 4);
+    output[3] = (char)NK_TO_HEX((col.g & 0x0F));
+    output[4] = (char)NK_TO_HEX((col.b & 0xF0) >> 4);
+    output[5] = (char)NK_TO_HEX((col.b & 0x0F));
+    output[6] = (char)NK_TO_HEX((col.a & 0xF0) >> 4);
+    output[7] = (char)NK_TO_HEX((col.a & 0x0F));
+    output[8] = '\0';
+    #undef NK_TO_HEX
+}
+
+NK_API void
+nk_color_hex_rgb(char *output, struct nk_color col)
+{
+    #define NK_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i))
+    output[0] = (char)NK_TO_HEX((col.r & 0xF0) >> 4);
+    output[1] = (char)NK_TO_HEX((col.r & 0x0F));
+    output[2] = (char)NK_TO_HEX((col.g & 0xF0) >> 4);
+    output[3] = (char)NK_TO_HEX((col.g & 0x0F));
+    output[4] = (char)NK_TO_HEX((col.b & 0xF0) >> 4);
+    output[5] = (char)NK_TO_HEX((col.b & 0x0F));
+    output[6] = '\0';
+    #undef NK_TO_HEX
+}
+
+NK_API struct nk_color
+nk_rgba_iv(const int *c)
+{
+    return nk_rgba(c[0], c[1], c[2], c[3]);
+}
+
+NK_API struct nk_color
+nk_rgba_bv(const nk_byte *c)
+{
+    return nk_rgba(c[0], c[1], c[2], c[3]);
+}
+
+NK_API struct nk_color
+nk_rgb(int r, int g, int b)
+{
+    struct nk_color ret;
+    ret.r = (nk_byte)NK_CLAMP(0, r, 255);
+    ret.g = (nk_byte)NK_CLAMP(0, g, 255);
+    ret.b = (nk_byte)NK_CLAMP(0, b, 255);
+    ret.a = (nk_byte)255;
+    return ret;
+}
+
+NK_API struct nk_color
+nk_rgb_iv(const int *c)
+{
+    return nk_rgb(c[0], c[1], c[2]);
+}
+
+NK_API struct nk_color
+nk_rgb_bv(const nk_byte* c)
+{
+    return nk_rgb(c[0], c[1], c[2]);
+}
+
+NK_API struct nk_color
+nk_rgba_u32(nk_uint in)
+{
+    struct nk_color ret;
+    ret.r = (in & 0xFF);
+    ret.g = ((in >> 8) & 0xFF);
+    ret.b = ((in >> 16) & 0xFF);
+    ret.a = (nk_byte)((in >> 24) & 0xFF);
+    return ret;
+}
+
+NK_API struct nk_color
+nk_rgba_f(float r, float g, float b, float a)
+{
+    struct nk_color ret;
+    ret.r = (nk_byte)(NK_SATURATE(r) * 255.0f);
+    ret.g = (nk_byte)(NK_SATURATE(g) * 255.0f);
+    ret.b = (nk_byte)(NK_SATURATE(b) * 255.0f);
+    ret.a = (nk_byte)(NK_SATURATE(a) * 255.0f);
+    return ret;
+}
+
+NK_API struct nk_color
+nk_rgba_fv(const float *c)
+{
+    return nk_rgba_f(c[0], c[1], c[2], c[3]);
+}
+
+NK_API struct nk_color
+nk_rgb_f(float r, float g, float b)
+{
+    struct nk_color ret;
+    ret.r = (nk_byte)(NK_SATURATE(r) * 255.0f);
+    ret.g = (nk_byte)(NK_SATURATE(g) * 255.0f);
+    ret.b = (nk_byte)(NK_SATURATE(b) * 255.0f);
+    ret.a = 255;
+    return ret;
+}
+
+NK_API struct nk_color
+nk_rgb_fv(const float *c)
+{
+    return nk_rgb_f(c[0], c[1], c[2]);
+}
+
+NK_API struct nk_color
+nk_hsv(int h, int s, int v)
+{
+    return nk_hsva(h, s, v, 255);
+}
+
+NK_API struct nk_color
+nk_hsv_iv(const int *c)
+{
+    return nk_hsv(c[0], c[1], c[2]);
+}
+
+NK_API struct nk_color
+nk_hsv_bv(const nk_byte *c)
+{
+    return nk_hsv(c[0], c[1], c[2]);
+}
+
+NK_API struct nk_color
+nk_hsv_f(float h, float s, float v)
+{
+    return nk_hsva_f(h, s, v, 1.0f);
+}
+
+NK_API struct nk_color
+nk_hsv_fv(const float *c)
+{
+    return nk_hsv_f(c[0], c[1], c[2]);
+}
+
+NK_API struct nk_color
+nk_hsva(int h, int s, int v, int a)
+{
+    float hf = ((float)NK_CLAMP(0, h, 255)) / 255.0f;
+    float sf = ((float)NK_CLAMP(0, s, 255)) / 255.0f;
+    float vf = ((float)NK_CLAMP(0, v, 255)) / 255.0f;
+    float af = ((float)NK_CLAMP(0, a, 255)) / 255.0f;
+    return nk_hsva_f(hf, sf, vf, af);
+}
+
+NK_API struct nk_color
+nk_hsva_iv(const int *c)
+{
+    return nk_hsva(c[0], c[1], c[2], c[3]);
+}
+
+NK_API struct nk_color
+nk_hsva_bv(const nk_byte *c)
+{
+    return nk_hsva(c[0], c[1], c[2], c[3]);
+}
+
+NK_API struct nk_color
+nk_hsva_f(float h, float s, float v, float a)
+{
+    struct nk_colorf out = {0,0,0,0};
+    float p, q, t, f;
+    int i;
+
+    if (s <= 0.0f) {
+        out.r = v; out.g = v; out.b = v;
+        return nk_rgb_f(out.r, out.g, out.b);
+    }
+
+    h = h / (60.0f/360.0f);
+    i = (int)h;
+    f = h - (float)i;
+    p = v * (1.0f - s);
+    q = v * (1.0f - (s * f));
+    t = v * (1.0f - s * (1.0f - f));
+
+    switch (i) {
+    case 0: default: out.r = v; out.g = t; out.b = p; break;
+    case 1: out.r = q; out.g = v; out.b = p; break;
+    case 2: out.r = p; out.g = v; out.b = t; break;
+    case 3: out.r = p; out.g = q; out.b = v; break;
+    case 4: out.r = t; out.g = p; out.b = v; break;
+    case 5: out.r = v; out.g = p; out.b = q; break;
+    }
+    return nk_rgba_f(out.r, out.g, out.b, a);
+}
+
+NK_API struct nk_color
+nk_hsva_fv(const float *c)
+{
+    return nk_hsva_f(c[0], c[1], c[2], c[3]);
+}
+
+NK_API nk_uint
+nk_color_u32(struct nk_color in)
+{
+    nk_uint out = (nk_uint)in.r;
+    out |= ((nk_uint)in.g << 8);
+    out |= ((nk_uint)in.b << 16);
+    out |= ((nk_uint)in.a << 24);
+    return out;
+}
+
+NK_API void
+nk_color_f(float *r, float *g, float *b, float *a, struct nk_color in)
+{
+    NK_STORAGE const float s = 1.0f/255.0f;
+    *r = (float)in.r * s;
+    *g = (float)in.g * s;
+    *b = (float)in.b * s;
+    *a = (float)in.a * s;
+}
+
+NK_API void
+nk_color_fv(float *c, struct nk_color in)
+{
+    nk_color_f(&c[0], &c[1], &c[2], &c[3], in);
+}
+
+NK_API void
+nk_color_d(double *r, double *g, double *b, double *a, struct nk_color in)
+{
+    NK_STORAGE const double s = 1.0/255.0;
+    *r = (double)in.r * s;
+    *g = (double)in.g * s;
+    *b = (double)in.b * s;
+    *a = (double)in.a * s;
+}
+
+NK_API void
+nk_color_dv(double *c, struct nk_color in)
+{
+    nk_color_d(&c[0], &c[1], &c[2], &c[3], in);
+}
+
+NK_API void
+nk_color_hsv_f(float *out_h, float *out_s, float *out_v, struct nk_color in)
+{
+    float a;
+    nk_color_hsva_f(out_h, out_s, out_v, &a, in);
+}
+
+NK_API void
+nk_color_hsv_fv(float *out, struct nk_color in)
+{
+    float a;
+    nk_color_hsva_f(&out[0], &out[1], &out[2], &a, in);
+}
+
+NK_API void
+nk_color_hsva_f(float *out_h, float *out_s,
+    float *out_v, float *out_a, struct nk_color in)
+{
+    float chroma;
+    float K = 0.0f;
+    float r,g,b,a;
+
+    nk_color_f(&r,&g,&b,&a, in);
+    if (g < b) {
+        const float t = g; g = b; b = t;
+        K = -1.f;
+    }
+    if (r < g) {
+        const float t = r; r = g; g = t;
+        K = -2.f/6.0f - K;
+    }
+    chroma = r - ((g < b) ? g: b);
+    *out_h = NK_ABS(K + (g - b)/(6.0f * chroma + 1e-20f));
+    *out_s = chroma / (r + 1e-20f);
+    *out_v = r;
+    *out_a = (float)in.a / 255.0f;
+}
+
+NK_API void
+nk_color_hsva_fv(float *out, struct nk_color in)
+{
+    nk_color_hsva_f(&out[0], &out[1], &out[2], &out[3], in);
+}
+
+NK_API void
+nk_color_hsva_i(int *out_h, int *out_s, int *out_v,
+                int *out_a, struct nk_color in)
+{
+    float h,s,v,a;
+    nk_color_hsva_f(&h, &s, &v, &a, in);
+    *out_h = (nk_byte)(h * 255.0f);
+    *out_s = (nk_byte)(s * 255.0f);
+    *out_v = (nk_byte)(v * 255.0f);
+    *out_a = (nk_byte)(a * 255.0f);
+}
+
+NK_API void
+nk_color_hsva_iv(int *out, struct nk_color in)
+{
+    nk_color_hsva_i(&out[0], &out[1], &out[2], &out[3], in);
+}
+
+NK_API void
+nk_color_hsva_bv(nk_byte *out, struct nk_color in)
+{
+    int tmp[4];
+    nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in);
+    out[0] = (nk_byte)tmp[0];
+    out[1] = (nk_byte)tmp[1];
+    out[2] = (nk_byte)tmp[2];
+    out[3] = (nk_byte)tmp[3];
+}
+
+NK_API void
+nk_color_hsva_b(nk_byte *h, nk_byte *s, nk_byte *v, nk_byte *a, struct nk_color in)
+{
+    int tmp[4];
+    nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in);
+    *h = (nk_byte)tmp[0];
+    *s = (nk_byte)tmp[1];
+    *v = (nk_byte)tmp[2];
+    *a = (nk_byte)tmp[3];
+}
+
+NK_API void
+nk_color_hsv_i(int *out_h, int *out_s, int *out_v, struct nk_color in)
+{
+    int a;
+    nk_color_hsva_i(out_h, out_s, out_v, &a, in);
+}
+
+NK_API void
+nk_color_hsv_b(nk_byte *out_h, nk_byte *out_s, nk_byte *out_v, struct nk_color in)
+{
+    int tmp[4];
+    nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in);
+    *out_h = (nk_byte)tmp[0];
+    *out_s = (nk_byte)tmp[1];
+    *out_v = (nk_byte)tmp[2];
+}
+
+NK_API void
+nk_color_hsv_iv(int *out, struct nk_color in)
+{
+    nk_color_hsv_i(&out[0], &out[1], &out[2], in);
+}
+
+NK_API void
+nk_color_hsv_bv(nk_byte *out, struct nk_color in)
+{
+    int tmp[4];
+    nk_color_hsv_i(&tmp[0], &tmp[1], &tmp[2], in);
+    out[0] = (nk_byte)tmp[0];
+    out[1] = (nk_byte)tmp[1];
+    out[2] = (nk_byte)tmp[2];
+}
+/*
+ * ==============================================================
+ *
+ *                          IMAGE
+ *
+ * ===============================================================
+ */
+NK_API nk_handle
+nk_handle_ptr(void *ptr)
+{
+    nk_handle handle = {0};
+    handle.ptr = ptr;
+    return handle;
+}
+
+NK_API nk_handle
+nk_handle_id(int id)
+{
+    nk_handle handle;
+    nk_zero_struct(handle);
+    handle.id = id;
+    return handle;
+}
+
+NK_API struct nk_image
+nk_subimage_ptr(void *ptr, unsigned short w, unsigned short h, struct nk_rect r)
+{
+    struct nk_image s;
+    nk_zero(&s, sizeof(s));
+    s.handle.ptr = ptr;
+    s.w = w; s.h = h;
+    s.region[0] = (unsigned short)r.x;
+    s.region[1] = (unsigned short)r.y;
+    s.region[2] = (unsigned short)r.w;
+    s.region[3] = (unsigned short)r.h;
+    return s;
+}
+
+NK_API struct nk_image
+nk_subimage_id(int id, unsigned short w, unsigned short h, struct nk_rect r)
+{
+    struct nk_image s;
+    nk_zero(&s, sizeof(s));
+    s.handle.id = id;
+    s.w = w; s.h = h;
+    s.region[0] = (unsigned short)r.x;
+    s.region[1] = (unsigned short)r.y;
+    s.region[2] = (unsigned short)r.w;
+    s.region[3] = (unsigned short)r.h;
+    return s;
+}
+
+NK_API struct nk_image
+nk_subimage_handle(nk_handle handle, unsigned short w, unsigned short h,
+    struct nk_rect r)
+{
+    struct nk_image s;
+    nk_zero(&s, sizeof(s));
+    s.handle = handle;
+    s.w = w; s.h = h;
+    s.region[0] = (unsigned short)r.x;
+    s.region[1] = (unsigned short)r.y;
+    s.region[2] = (unsigned short)r.w;
+    s.region[3] = (unsigned short)r.h;
+    return s;
+}
+
+NK_API struct nk_image
+nk_image_handle(nk_handle handle)
+{
+    struct nk_image s;
+    nk_zero(&s, sizeof(s));
+    s.handle = handle;
+    s.w = 0; s.h = 0;
+    s.region[0] = 0;
+    s.region[1] = 0;
+    s.region[2] = 0;
+    s.region[3] = 0;
+    return s;
+}
+
+NK_API struct nk_image
+nk_image_ptr(void *ptr)
+{
+    struct nk_image s;
+    nk_zero(&s, sizeof(s));
+    NK_ASSERT(ptr);
+    s.handle.ptr = ptr;
+    s.w = 0; s.h = 0;
+    s.region[0] = 0;
+    s.region[1] = 0;
+    s.region[2] = 0;
+    s.region[3] = 0;
+    return s;
+}
+
+NK_API struct nk_image
+nk_image_id(int id)
+{
+    struct nk_image s;
+    nk_zero(&s, sizeof(s));
+    s.handle.id = id;
+    s.w = 0; s.h = 0;
+    s.region[0] = 0;
+    s.region[1] = 0;
+    s.region[2] = 0;
+    s.region[3] = 0;
+    return s;
+}
+
+NK_API int
+nk_image_is_subimage(const struct nk_image* img)
+{
+    NK_ASSERT(img);
+    return !(img->w == 0 && img->h == 0);
+}
+
+NK_INTERN void
+nk_unify(struct nk_rect *clip, const struct nk_rect *a, float x0, float y0,
+    float x1, float y1)
+{
+    NK_ASSERT(a);
+    NK_ASSERT(clip);
+    clip->x = NK_MAX(a->x, x0);
+    clip->y = NK_MAX(a->y, y0);
+    clip->w = NK_MIN(a->x + a->w, x1) - clip->x;
+    clip->h = NK_MIN(a->y + a->h, y1) - clip->y;
+    clip->w = NK_MAX(0, clip->w);
+    clip->h = NK_MAX(0, clip->h);
+}
+
+NK_API void
+nk_triangle_from_direction(struct nk_vec2 *result, struct nk_rect r,
+    float pad_x, float pad_y, enum nk_heading direction)
+{
+    float w_half, h_half;
+    NK_ASSERT(result);
+
+    r.w = NK_MAX(2 * pad_x, r.w);
+    r.h = NK_MAX(2 * pad_y, r.h);
+    r.w = r.w - 2 * pad_x;
+    r.h = r.h - 2 * pad_y;
+
+    r.x = r.x + pad_x;
+    r.y = r.y + pad_y;
+
+    w_half = r.w / 2.0f;
+    h_half = r.h / 2.0f;
+
+    if (direction == NK_UP) {
+        result[0] = nk_vec2(r.x + w_half, r.y);
+        result[1] = nk_vec2(r.x + r.w, r.y + r.h);
+        result[2] = nk_vec2(r.x, r.y + r.h);
+    } else if (direction == NK_RIGHT) {
+        result[0] = nk_vec2(r.x, r.y);
+        result[1] = nk_vec2(r.x + r.w, r.y + h_half);
+        result[2] = nk_vec2(r.x, r.y + r.h);
+    } else if (direction == NK_DOWN) {
+        result[0] = nk_vec2(r.x, r.y);
+        result[1] = nk_vec2(r.x + r.w, r.y);
+        result[2] = nk_vec2(r.x + w_half, r.y + r.h);
+    } else {
+        result[0] = nk_vec2(r.x, r.y + h_half);
+        result[1] = nk_vec2(r.x + r.w, r.y);
+        result[2] = nk_vec2(r.x + r.w, r.y + r.h);
+    }
+}
+
+NK_INTERN int
+nk_text_clamp(const struct nk_user_font *font, const char *text,
+    int text_len, float space, int *glyphs, float *text_width,
+    nk_rune *sep_list, int sep_count)
+{
+    int i = 0;
+    int glyph_len = 0;
+    float last_width = 0;
+    nk_rune unicode = 0;
+    float width = 0;
+    int len = 0;
+    int g = 0;
+    float s;
+
+    int sep_len = 0;
+    int sep_g = 0;
+    float sep_width = 0;
+    sep_count = NK_MAX(sep_count,0);
+
+    glyph_len = nk_utf_decode(text, &unicode, text_len);
+    while (glyph_len && (width < space) && (len < text_len)) {
+        len += glyph_len;
+        s = font->width(font->userdata, font->height, text, len);
+        for (i = 0; i < sep_count; ++i) {
+            if (unicode != sep_list[i]) continue;
+            sep_width = last_width = width;
+            sep_g = g+1;
+            sep_len = len;
+            break;
+        }
+        if (i == sep_count){
+            last_width = sep_width = width;
+            sep_g = g+1;
+        }
+        width = s;
+        glyph_len = nk_utf_decode(&text[len], &unicode, text_len - len);
+        g++;
+    }
+    if (len >= text_len) {
+        *glyphs = g;
+        *text_width = last_width;
+        return len;
+    } else {
+        *glyphs = sep_g;
+        *text_width = sep_width;
+        return (!sep_len) ? len: sep_len;
+    }
+}
+
+enum {NK_DO_NOT_STOP_ON_NEW_LINE, NK_STOP_ON_NEW_LINE};
+NK_INTERN struct nk_vec2
+nk_text_calculate_text_bounds(const struct nk_user_font *font,
+    const char *begin, int byte_len, float row_height, const char **remaining,
+    struct nk_vec2 *out_offset, int *glyphs, int op)
+{
+    float line_height = row_height;
+    struct nk_vec2 text_size = nk_vec2(0,0);
+    float line_width = 0.0f;
+
+    float glyph_width;
+    int glyph_len = 0;
+    nk_rune unicode = 0;
+    int text_len = 0;
+    if (!begin || byte_len <= 0 || !font)
+        return nk_vec2(0,row_height);
+
+    glyph_len = nk_utf_decode(begin, &unicode, byte_len);
+    if (!glyph_len) return text_size;
+    glyph_width = font->width(font->userdata, font->height, begin, glyph_len);
+
+    *glyphs = 0;
+    while ((text_len < byte_len) && glyph_len) {
+        if (unicode == '\n') {
+            text_size.x = NK_MAX(text_size.x, line_width);
+            text_size.y += line_height;
+            line_width = 0;
+            *glyphs+=1;
+            if (op == NK_STOP_ON_NEW_LINE)
+                break;
+
+            text_len++;
+            glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len);
+            continue;
+        }
+
+        if (unicode == '\r') {
+            text_len++;
+            *glyphs+=1;
+            glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len);
+            continue;
+        }
+
+        *glyphs = *glyphs + 1;
+        text_len += glyph_len;
+        line_width += (float)glyph_width;
+        glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len);
+        glyph_width = font->width(font->userdata, font->height, begin+text_len, glyph_len);
+        continue;
+    }
+
+    if (text_size.x < line_width)
+        text_size.x = line_width;
+    if (out_offset)
+        *out_offset = nk_vec2(line_width, text_size.y + line_height);
+    if (line_width > 0 || text_size.y == 0.0f)
+        text_size.y += line_height;
+    if (remaining)
+        *remaining = begin+text_len;
+    return text_size;
+}
+
+/* ==============================================================
+ *
+ *                          UTF-8
+ *
+ * ===============================================================*/
+NK_GLOBAL const nk_byte nk_utfbyte[NK_UTF_SIZE+1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
+NK_GLOBAL const nk_byte nk_utfmask[NK_UTF_SIZE+1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
+NK_GLOBAL const nk_uint nk_utfmin[NK_UTF_SIZE+1] = {0, 0, 0x80, 0x800, 0x10000};
+NK_GLOBAL const nk_uint nk_utfmax[NK_UTF_SIZE+1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
+
+NK_INTERN int
+nk_utf_validate(nk_rune *u, int i)
+{
+    NK_ASSERT(u);
+    if (!u) return 0;
+    if (!NK_BETWEEN(*u, nk_utfmin[i], nk_utfmax[i]) ||
+         NK_BETWEEN(*u, 0xD800, 0xDFFF))
+            *u = NK_UTF_INVALID;
+    for (i = 1; *u > nk_utfmax[i]; ++i);
+    return i;
+}
+
+NK_INTERN nk_rune
+nk_utf_decode_byte(char c, int *i)
+{
+    NK_ASSERT(i);
+    if (!i) return 0;
+    for(*i = 0; *i < (int)NK_LEN(nk_utfmask); ++(*i)) {
+        if (((nk_byte)c & nk_utfmask[*i]) == nk_utfbyte[*i])
+            return (nk_byte)(c & ~nk_utfmask[*i]);
+    }
+    return 0;
+}
+
+NK_API int
+nk_utf_decode(const char *c, nk_rune *u, int clen)
+{
+    int i, j, len, type=0;
+    nk_rune udecoded;
+
+    NK_ASSERT(c);
+    NK_ASSERT(u);
+
+    if (!c || !u) return 0;
+    if (!clen) return 0;
+    *u = NK_UTF_INVALID;
+
+    udecoded = nk_utf_decode_byte(c[0], &len);
+    if (!NK_BETWEEN(len, 1, NK_UTF_SIZE))
+        return 1;
+
+    for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
+        udecoded = (udecoded << 6) | nk_utf_decode_byte(c[i], &type);
+        if (type != 0)
+            return j;
+    }
+    if (j < len)
+        return 0;
+    *u = udecoded;
+    nk_utf_validate(u, len);
+    return len;
+}
+
+NK_INTERN char
+nk_utf_encode_byte(nk_rune u, int i)
+{
+    return (char)((nk_utfbyte[i]) | ((nk_byte)u & ~nk_utfmask[i]));
+}
+
+NK_API int
+nk_utf_encode(nk_rune u, char *c, int clen)
+{
+    int len, i;
+    len = nk_utf_validate(&u, 0);
+    if (clen < len || !len || len > NK_UTF_SIZE)
+        return 0;
+
+    for (i = len - 1; i != 0; --i) {
+        c[i] = nk_utf_encode_byte(u, 0);
+        u >>= 6;
+    }
+    c[0] = nk_utf_encode_byte(u, len);
+    return len;
+}
+
+NK_API int
+nk_utf_len(const char *str, int len)
+{
+    const char *text;
+    int glyphs = 0;
+    int text_len;
+    int glyph_len;
+    int src_len = 0;
+    nk_rune unicode;
+
+    NK_ASSERT(str);
+    if (!str || !len) return 0;
+
+    text = str;
+    text_len = len;
+    glyph_len = nk_utf_decode(text, &unicode, text_len);
+    while (glyph_len && src_len < len) {
+        glyphs++;
+        src_len = src_len + glyph_len;
+        glyph_len = nk_utf_decode(text + src_len, &unicode, text_len - src_len);
+    }
+    return glyphs;
+}
+
+NK_API const char*
+nk_utf_at(const char *buffer, int length, int index,
+    nk_rune *unicode, int *len)
+{
+    int i = 0;
+    int src_len = 0;
+    int glyph_len = 0;
+    const char *text;
+    int text_len;
+
+    NK_ASSERT(buffer);
+    NK_ASSERT(unicode);
+    NK_ASSERT(len);
+
+    if (!buffer || !unicode || !len) return 0;
+    if (index < 0) {
+        *unicode = NK_UTF_INVALID;
+        *len = 0;
+        return 0;
+    }
+
+    text = buffer;
+    text_len = length;
+    glyph_len = nk_utf_decode(text, unicode, text_len);
+    while (glyph_len) {
+        if (i == index) {
+            *len = glyph_len;
+            break;
+        }
+
+        i++;
+        src_len = src_len + glyph_len;
+        glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len);
+    }
+    if (i != index) return 0;
+    return buffer + src_len;
+}
+
+/* ==============================================================
+ *
+ *                          BUFFER
+ *
+ * ===============================================================*/
+#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR
+NK_INTERN void* nk_malloc(nk_handle unused, void *old,nk_size size)
+{NK_UNUSED(unused); NK_UNUSED(old); return malloc(size);}
+NK_INTERN void nk_mfree(nk_handle unused, void *ptr)
+{NK_UNUSED(unused); free(ptr);}
+
+NK_API void
+nk_buffer_init_default(struct nk_buffer *buffer)
+{
+    struct nk_allocator alloc;
+    alloc.userdata.ptr = 0;
+    alloc.alloc = nk_malloc;
+    alloc.free = nk_mfree;
+    nk_buffer_init(buffer, &alloc, NK_BUFFER_DEFAULT_INITIAL_SIZE);
+}
+#endif
+
+NK_API void
+nk_buffer_init(struct nk_buffer *b, const struct nk_allocator *a,
+    nk_size initial_size)
+{
+    NK_ASSERT(b);
+    NK_ASSERT(a);
+    NK_ASSERT(initial_size);
+    if (!b || !a || !initial_size) return;
+
+    nk_zero(b, sizeof(*b));
+    b->type = NK_BUFFER_DYNAMIC;
+    b->memory.ptr = a->alloc(a->userdata,0, initial_size);
+    b->memory.size = initial_size;
+    b->size = initial_size;
+    b->grow_factor = 2.0f;
+    b->pool = *a;
+}
+
+NK_API void
+nk_buffer_init_fixed(struct nk_buffer *b, void *m, nk_size size)
+{
+    NK_ASSERT(b);
+    NK_ASSERT(m);
+    NK_ASSERT(size);
+    if (!b || !m || !size) return;
+
+    nk_zero(b, sizeof(*b));
+    b->type = NK_BUFFER_FIXED;
+    b->memory.ptr = m;
+    b->memory.size = size;
+    b->size = size;
+}
+
+NK_INTERN void*
+nk_buffer_align(void *unaligned, nk_size align, nk_size *alignment,
+    enum nk_buffer_allocation_type type)
+{
+    void *memory = 0;
+    switch (type) {
+    default:
+    case NK_BUFFER_MAX:
+    case NK_BUFFER_FRONT:
+        if (align) {
+            memory = NK_ALIGN_PTR(unaligned, align);
+            *alignment = (nk_size)((nk_byte*)memory - (nk_byte*)unaligned);
+        } else {
+            memory = unaligned;
+            *alignment = 0;
+        }
+        break;
+    case NK_BUFFER_BACK:
+        if (align) {
+            memory = NK_ALIGN_PTR_BACK(unaligned, align);
+            *alignment = (nk_size)((nk_byte*)unaligned - (nk_byte*)memory);
+        } else {
+            memory = unaligned;
+            *alignment = 0;
+        }
+        break;
+    }
+    return memory;
+}
+
+NK_INTERN void*
+nk_buffer_realloc(struct nk_buffer *b, nk_size capacity, nk_size *size)
+{
+    void *temp;
+    nk_size buffer_size;
+
+    NK_ASSERT(b);
+    NK_ASSERT(size);
+    if (!b || !size || !b->pool.alloc || !b->pool.free)
+        return 0;
+
+    buffer_size = b->memory.size;
+    temp = b->pool.alloc(b->pool.userdata, b->memory.ptr, capacity);
+    NK_ASSERT(temp);
+    if (!temp) return 0;
+
+    *size = capacity;
+    if (temp != b->memory.ptr) {
+        NK_MEMCPY(temp, b->memory.ptr, buffer_size);
+        b->pool.free(b->pool.userdata, b->memory.ptr);
+    }
+
+    if (b->size == buffer_size) {
+        /* no back buffer so just set correct size */
+        b->size = capacity;
+        return temp;
+    } else {
+        /* copy back buffer to the end of the new buffer */
+        void *dst, *src;
+        nk_size back_size;
+        back_size = buffer_size - b->size;
+        dst = nk_ptr_add(void, temp, capacity - back_size);
+        src = nk_ptr_add(void, temp, b->size);
+        NK_MEMCPY(dst, src, back_size);
+        b->size = capacity - back_size;
+    }
+    return temp;
+}
+
+NK_INTERN void*
+nk_buffer_alloc(struct nk_buffer *b, enum nk_buffer_allocation_type type,
+    nk_size size, nk_size align)
+{
+    int full;
+    nk_size alignment;
+    void *unaligned;
+    void *memory;
+
+    NK_ASSERT(b);
+    NK_ASSERT(size);
+    if (!b || !size) return 0;
+    b->needed += size;
+
+    /* calculate total size with needed alignment + size */
+    if (type == NK_BUFFER_FRONT)
+        unaligned = nk_ptr_add(void, b->memory.ptr, b->allocated);
+    else unaligned = nk_ptr_add(void, b->memory.ptr, b->size - size);
+    memory = nk_buffer_align(unaligned, align, &alignment, type);
+
+    /* check if buffer has enough memory*/
+    if (type == NK_BUFFER_FRONT)
+        full = ((b->allocated + size + alignment) > b->size);
+    else full = ((b->size - NK_MIN(b->size,(size + alignment))) <= b->allocated);
+
+    if (full) {
+        nk_size capacity;
+        if (b->type != NK_BUFFER_DYNAMIC)
+            return 0;
+        NK_ASSERT(b->pool.alloc && b->pool.free);
+        if (b->type != NK_BUFFER_DYNAMIC || !b->pool.alloc || !b->pool.free)
+            return 0;
+
+        /* buffer is full so allocate bigger buffer if dynamic */
+        capacity = (nk_size)((float)b->memory.size * b->grow_factor);
+        capacity = NK_MAX(capacity, nk_round_up_pow2((nk_uint)(b->allocated + size)));
+        b->memory.ptr = nk_buffer_realloc(b, capacity, &b->memory.size);
+        if (!b->memory.ptr) return 0;
+
+        /* align newly allocated pointer */
+        if (type == NK_BUFFER_FRONT)
+            unaligned = nk_ptr_add(void, b->memory.ptr, b->allocated);
+        else unaligned = nk_ptr_add(void, b->memory.ptr, b->size - size);
+        memory = nk_buffer_align(unaligned, align, &alignment, type);
+    }
+    if (type == NK_BUFFER_FRONT)
+        b->allocated += size + alignment;
+    else b->size -= (size + alignment);
+    b->needed += alignment;
+    b->calls++;
+    return memory;
+}
+
+NK_API void
+nk_buffer_push(struct nk_buffer *b, enum nk_buffer_allocation_type type,
+    const void *memory, nk_size size, nk_size align)
+{
+    void *mem = nk_buffer_alloc(b, type, size, align);
+    if (!mem) return;
+    NK_MEMCPY(mem, memory, size);
+}
+
+NK_API void
+nk_buffer_mark(struct nk_buffer *buffer, enum nk_buffer_allocation_type type)
+{
+    NK_ASSERT(buffer);
+    if (!buffer) return;
+    buffer->marker[type].active = nk_true;
+    if (type == NK_BUFFER_BACK)
+        buffer->marker[type].offset = buffer->size;
+    else buffer->marker[type].offset = buffer->allocated;
+}
+
+NK_API void
+nk_buffer_reset(struct nk_buffer *buffer, enum nk_buffer_allocation_type type)
+{
+    NK_ASSERT(buffer);
+    if (!buffer) return;
+    if (type == NK_BUFFER_BACK) {
+        /* reset back buffer either back to marker or empty */
+        buffer->needed -= (buffer->memory.size - buffer->marker[type].offset);
+        if (buffer->marker[type].active)
+            buffer->size = buffer->marker[type].offset;
+        else buffer->size = buffer->memory.size;
+        buffer->marker[type].active = nk_false;
+    } else {
+        /* reset front buffer either back to back marker or empty */
+        buffer->needed -= (buffer->allocated - buffer->marker[type].offset);
+        if (buffer->marker[type].active)
+            buffer->allocated = buffer->marker[type].offset;
+        else buffer->allocated = 0;
+        buffer->marker[type].active = nk_false;
+    }
+}
+
+NK_API void
+nk_buffer_clear(struct nk_buffer *b)
+{
+    NK_ASSERT(b);
+    if (!b) return;
+    b->allocated = 0;
+    b->size = b->memory.size;
+    b->calls = 0;
+    b->needed = 0;
+}
+
+NK_API void
+nk_buffer_free(struct nk_buffer *b)
+{
+    NK_ASSERT(b);
+    if (!b || !b->memory.ptr) return;
+    if (b->type == NK_BUFFER_FIXED) return;
+    if (!b->pool.free) return;
+    NK_ASSERT(b->pool.free);
+    b->pool.free(b->pool.userdata, b->memory.ptr);
+}
+
+NK_API void
+nk_buffer_info(struct nk_memory_status *s, struct nk_buffer *b)
+{
+    NK_ASSERT(b);
+    NK_ASSERT(s);
+    if (!s || !b) return;
+    s->allocated = b->allocated;
+    s->size =  b->memory.size;
+    s->needed = b->needed;
+    s->memory = b->memory.ptr;
+    s->calls = b->calls;
+}
+
+NK_API void*
+nk_buffer_memory(struct nk_buffer *buffer)
+{
+    NK_ASSERT(buffer);
+    if (!buffer) return 0;
+    return buffer->memory.ptr;
+}
+
+NK_API const void*
+nk_buffer_memory_const(const struct nk_buffer *buffer)
+{
+    NK_ASSERT(buffer);
+    if (!buffer) return 0;
+    return buffer->memory.ptr;
+}
+
+NK_API nk_size
+nk_buffer_total(struct nk_buffer *buffer)
+{
+    NK_ASSERT(buffer);
+    if (!buffer) return 0;
+    return buffer->memory.size;
+}
+
+/*
+ * ==============================================================
+ *
+ *                          STRING
+ *
+ * ===============================================================
+ */
+#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR
+NK_API void
+nk_str_init_default(struct nk_str *str)
+{
+    struct nk_allocator alloc;
+    alloc.userdata.ptr = 0;
+    alloc.alloc = nk_malloc;
+    alloc.free = nk_mfree;
+    nk_buffer_init(&str->buffer, &alloc, 32);
+    str->len = 0;
+}
+#endif
+
+NK_API void
+nk_str_init(struct nk_str *str, const struct nk_allocator *alloc, nk_size size)
+{
+    nk_buffer_init(&str->buffer, alloc, size);
+    str->len = 0;
+}
+
+NK_API void
+nk_str_init_fixed(struct nk_str *str, void *memory, nk_size size)
+{
+    nk_buffer_init_fixed(&str->buffer, memory, size);
+    str->len = 0;
+}
+
+NK_API int
+nk_str_append_text_char(struct nk_str *s, const char *str, int len)
+{
+    char *mem;
+    NK_ASSERT(s);
+    NK_ASSERT(str);
+    if (!s || !str || !len) return 0;
+    mem = (char*)nk_buffer_alloc(&s->buffer, NK_BUFFER_FRONT, (nk_size)len * sizeof(char), 0);
+    if (!mem) return 0;
+    NK_MEMCPY(mem, str, (nk_size)len * sizeof(char));
+    s->len += nk_utf_len(str, len);
+    return len;
+}
+
+NK_API int
+nk_str_append_str_char(struct nk_str *s, const char *str)
+{
+    return nk_str_append_text_char(s, str, nk_strlen(str));
+}
+
+NK_API int
+nk_str_append_text_utf8(struct nk_str *str, const char *text, int len)
+{
+    int i = 0;
+    int byte_len = 0;
+    nk_rune unicode;
+    if (!str || !text || !len) return 0;
+    for (i = 0; i < len; ++i)
+        byte_len += nk_utf_decode(text+byte_len, &unicode, 4);
+    nk_str_append_text_char(str, text, byte_len);
+    return len;
+}
+
+NK_API int
+nk_str_append_str_utf8(struct nk_str *str, const char *text)
+{
+    int runes = 0;
+    int byte_len = 0;
+    int num_runes = 0;
+    int glyph_len = 0;
+    nk_rune unicode;
+    if (!str || !text) return 0;
+
+    glyph_len = byte_len = nk_utf_decode(text+byte_len, &unicode, 4);
+    while (unicode != '\0' && glyph_len) {
+        glyph_len = nk_utf_decode(text+byte_len, &unicode, 4);
+        byte_len += glyph_len;
+        num_runes++;
+    }
+    nk_str_append_text_char(str, text, byte_len);
+    return runes;
+}
+
+NK_API int
+nk_str_append_text_runes(struct nk_str *str, const nk_rune *text, int len)
+{
+    int i = 0;
+    int byte_len = 0;
+    nk_glyph glyph;
+
+    NK_ASSERT(str);
+    if (!str || !text || !len) return 0;
+    for (i = 0; i < len; ++i) {
+        byte_len = nk_utf_encode(text[i], glyph, NK_UTF_SIZE);
+        if (!byte_len) break;
+        nk_str_append_text_char(str, glyph, byte_len);
+    }
+    return len;
+}
+
+NK_API int
+nk_str_append_str_runes(struct nk_str *str, const nk_rune *runes)
+{
+    int i = 0;
+    nk_glyph glyph;
+    int byte_len;
+    NK_ASSERT(str);
+    if (!str || !runes) return 0;
+    while (runes[i] != '\0') {
+        byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE);
+        nk_str_append_text_char(str, glyph, byte_len);
+        i++;
+    }
+    return i;
+}
+
+NK_API int
+nk_str_insert_at_char(struct nk_str *s, int pos, const char *str, int len)
+{
+    int i;
+    void *mem;
+    char *src;
+    char *dst;
+
+    int copylen;
+    NK_ASSERT(s);
+    NK_ASSERT(str);
+    NK_ASSERT(len >= 0);
+    if (!s || !str || !len || (nk_size)pos > s->buffer.allocated) return 0;
+    if ((s->buffer.allocated + (nk_size)len >= s->buffer.memory.size) &&
+        (s->buffer.type == NK_BUFFER_FIXED)) return 0;
+
+    copylen = (int)s->buffer.allocated - pos;
+    if (!copylen) {
+        nk_str_append_text_char(s, str, len);
+        return 1;
+    }
+    mem = nk_buffer_alloc(&s->buffer, NK_BUFFER_FRONT, (nk_size)len * sizeof(char), 0);
+    if (!mem) return 0;
+
+    /* memmove */
+    NK_ASSERT(((int)pos + (int)len + ((int)copylen - 1)) >= 0);
+    NK_ASSERT(((int)pos + ((int)copylen - 1)) >= 0);
+    dst = nk_ptr_add(char, s->buffer.memory.ptr, pos + len + (copylen - 1));
+    src = nk_ptr_add(char, s->buffer.memory.ptr, pos + (copylen-1));
+    for (i = 0; i < copylen; ++i) *dst-- = *src--;
+    mem = nk_ptr_add(void, s->buffer.memory.ptr, pos);
+    NK_MEMCPY(mem, str, (nk_size)len * sizeof(char));
+    s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated);
+    return 1;
+}
+
+NK_API int
+nk_str_insert_at_rune(struct nk_str *str, int pos, const char *cstr, int len)
+{
+    int glyph_len;
+    nk_rune unicode;
+    const char *begin;
+    const char *buffer;
+
+    NK_ASSERT(str);
+    NK_ASSERT(cstr);
+    NK_ASSERT(len);
+    if (!str || !cstr || !len) return 0;
+    begin = nk_str_at_rune(str, pos, &unicode, &glyph_len);
+    if (!str->len)
+        return nk_str_append_text_char(str, cstr, len);
+    buffer = nk_str_get_const(str);
+    if (!begin) return 0;
+    return nk_str_insert_at_char(str, (int)(begin - buffer), cstr, len);
+}
+
+NK_API int
+nk_str_insert_text_char(struct nk_str *str, int pos, const char *text, int len)
+{
+    return nk_str_insert_text_utf8(str, pos, text, len);
+}
+
+NK_API int
+nk_str_insert_str_char(struct nk_str *str, int pos, const char *text)
+{
+    return nk_str_insert_text_utf8(str, pos, text, nk_strlen(text));
+}
+
+NK_API int
+nk_str_insert_text_utf8(struct nk_str *str, int pos, const char *text, int len)
+{
+    int i = 0;
+    int byte_len = 0;
+    nk_rune unicode;
+
+    NK_ASSERT(str);
+    NK_ASSERT(text);
+    if (!str || !text || !len) return 0;
+    for (i = 0; i < len; ++i)
+        byte_len += nk_utf_decode(text+byte_len, &unicode, 4);
+    nk_str_insert_at_rune(str, pos, text, byte_len);
+    return len;
+}
+
+NK_API int
+nk_str_insert_str_utf8(struct nk_str *str, int pos, const char *text)
+{
+    int runes = 0;
+    int byte_len = 0;
+    int num_runes = 0;
+    int glyph_len = 0;
+    nk_rune unicode;
+    if (!str || !text) return 0;
+
+    glyph_len = byte_len = nk_utf_decode(text+byte_len, &unicode, 4);
+    while (unicode != '\0' && glyph_len) {
+        glyph_len = nk_utf_decode(text+byte_len, &unicode, 4);
+        byte_len += glyph_len;
+        num_runes++;
+    }
+    nk_str_insert_at_rune(str, pos, text, byte_len);
+    return runes;
+}
+
+NK_API int
+nk_str_insert_text_runes(struct nk_str *str, int pos, const nk_rune *runes, int len)
+{
+    int i = 0;
+    int byte_len = 0;
+    nk_glyph glyph;
+
+    NK_ASSERT(str);
+    if (!str || !runes || !len) return 0;
+    for (i = 0; i < len; ++i) {
+        byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE);
+        if (!byte_len) break;
+        nk_str_insert_at_rune(str, pos+i, glyph, byte_len);
+    }
+    return len;
+}
+
+NK_API int
+nk_str_insert_str_runes(struct nk_str *str, int pos, const nk_rune *runes)
+{
+    int i = 0;
+    nk_glyph glyph;
+    int byte_len;
+    NK_ASSERT(str);
+    if (!str || !runes) return 0;
+    while (runes[i] != '\0') {
+        byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE);
+        nk_str_insert_at_rune(str, pos+i, glyph, byte_len);
+        i++;
+    }
+    return i;
+}
+
+NK_API void
+nk_str_remove_chars(struct nk_str *s, int len)
+{
+    NK_ASSERT(s);
+    NK_ASSERT(len >= 0);
+    if (!s || len < 0 || (nk_size)len > s->buffer.allocated) return;
+    NK_ASSERT(((int)s->buffer.allocated - (int)len) >= 0);
+    s->buffer.allocated -= (nk_size)len;
+    s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated);
+}
+
+NK_API void
+nk_str_remove_runes(struct nk_str *str, int len)
+{
+    int index;
+    const char *begin;
+    const char *end;
+    nk_rune unicode;
+
+    NK_ASSERT(str);
+    NK_ASSERT(len >= 0);
+    if (!str || len < 0) return;
+    if (len >= str->len) {
+        str->len = 0;
+        return;
+    }
+
+    index = str->len - len;
+    begin = nk_str_at_rune(str, index, &unicode, &len);
+    end = (const char*)str->buffer.memory.ptr + str->buffer.allocated;
+    nk_str_remove_chars(str, (int)(end-begin)+1);
+}
+
+NK_API void
+nk_str_delete_chars(struct nk_str *s, int pos, int len)
+{
+    NK_ASSERT(s);
+    if (!s || !len || (nk_size)pos > s->buffer.allocated ||
+        (nk_size)(pos + len) > s->buffer.allocated) return;
+
+    if ((nk_size)(pos + len) < s->buffer.allocated) {
+        /* memmove */
+        char *dst = nk_ptr_add(char, s->buffer.memory.ptr, pos);
+        char *src = nk_ptr_add(char, s->buffer.memory.ptr, pos + len);
+        NK_MEMCPY(dst, src, s->buffer.allocated - (nk_size)(pos + len));
+        NK_ASSERT(((int)s->buffer.allocated - (int)len) >= 0);
+        s->buffer.allocated -= (nk_size)len;
+    } else nk_str_remove_chars(s, len);
+    s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated);
+}
+
+NK_API void
+nk_str_delete_runes(struct nk_str *s, int pos, int len)
+{
+    char *temp;
+    nk_rune unicode;
+    char *begin;
+    char *end;
+    int unused;
+
+    NK_ASSERT(s);
+    NK_ASSERT(s->len >= pos + len);
+    if (s->len < pos + len)
+        len = NK_CLAMP(0, (s->len - pos), s->len);
+    if (!len) return;
+
+    temp = (char *)s->buffer.memory.ptr;
+    begin = nk_str_at_rune(s, pos, &unicode, &unused);
+    if (!begin) return;
+    s->buffer.memory.ptr = begin;
+    end = nk_str_at_rune(s, len, &unicode, &unused);
+    s->buffer.memory.ptr = temp;
+    if (!end) return;
+    nk_str_delete_chars(s, (int)(begin - temp), (int)(end - begin));
+}
+
+NK_API char*
+nk_str_at_char(struct nk_str *s, int pos)
+{
+    NK_ASSERT(s);
+    if (!s || pos > (int)s->buffer.allocated) return 0;
+    return nk_ptr_add(char, s->buffer.memory.ptr, pos);
+}
+
+NK_API char*
+nk_str_at_rune(struct nk_str *str, int pos, nk_rune *unicode, int *len)
+{
+    int i = 0;
+    int src_len = 0;
+    int glyph_len = 0;
+    char *text;
+    int text_len;
+
+    NK_ASSERT(str);
+    NK_ASSERT(unicode);
+    NK_ASSERT(len);
+
+    if (!str || !unicode || !len) return 0;
+    if (pos < 0) {
+        *unicode = 0;
+        *len = 0;
+        return 0;
+    }
+
+    text = (char*)str->buffer.memory.ptr;
+    text_len = (int)str->buffer.allocated;
+    glyph_len = nk_utf_decode(text, unicode, text_len);
+    while (glyph_len) {
+        if (i == pos) {
+            *len = glyph_len;
+            break;
+        }
+
+        i++;
+        src_len = src_len + glyph_len;
+        glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len);
+    }
+    if (i != pos) return 0;
+    return text + src_len;
+}
+
+NK_API const char*
+nk_str_at_char_const(const struct nk_str *s, int pos)
+{
+    NK_ASSERT(s);
+    if (!s || pos > (int)s->buffer.allocated) return 0;
+    return nk_ptr_add(char, s->buffer.memory.ptr, pos);
+}
+
+NK_API const char*
+nk_str_at_const(const struct nk_str *str, int pos, nk_rune *unicode, int *len)
+{
+    int i = 0;
+    int src_len = 0;
+    int glyph_len = 0;
+    char *text;
+    int text_len;
+
+    NK_ASSERT(str);
+    NK_ASSERT(unicode);
+    NK_ASSERT(len);
+
+    if (!str || !unicode || !len) return 0;
+    if (pos < 0) {
+        *unicode = 0;
+        *len = 0;
+        return 0;
+    }
+
+    text = (char*)str->buffer.memory.ptr;
+    text_len = (int)str->buffer.allocated;
+    glyph_len = nk_utf_decode(text, unicode, text_len);
+    while (glyph_len) {
+        if (i == pos) {
+            *len = glyph_len;
+            break;
+        }
+
+        i++;
+        src_len = src_len + glyph_len;
+        glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len);
+    }
+    if (i != pos) return 0;
+    return text + src_len;
+}
+
+NK_API nk_rune
+nk_str_rune_at(const struct nk_str *str, int pos)
+{
+    int len;
+    nk_rune unicode = 0;
+    nk_str_at_const(str, pos, &unicode, &len);
+    return unicode;
+}
+
+NK_API char*
+nk_str_get(struct nk_str *s)
+{
+    NK_ASSERT(s);
+    if (!s || !s->len || !s->buffer.allocated) return 0;
+    return (char*)s->buffer.memory.ptr;
+}
+
+NK_API const char*
+nk_str_get_const(const struct nk_str *s)
+{
+    NK_ASSERT(s);
+    if (!s || !s->len || !s->buffer.allocated) return 0;
+    return (const char*)s->buffer.memory.ptr;
+}
+
+NK_API int
+nk_str_len(struct nk_str *s)
+{
+    NK_ASSERT(s);
+    if (!s || !s->len || !s->buffer.allocated) return 0;
+    return s->len;
+}
+
+NK_API int
+nk_str_len_char(struct nk_str *s)
+{
+    NK_ASSERT(s);
+    if (!s || !s->len || !s->buffer.allocated) return 0;
+    return (int)s->buffer.allocated;
+}
+
+NK_API void
+nk_str_clear(struct nk_str *str)
+{
+    NK_ASSERT(str);
+    nk_buffer_clear(&str->buffer);
+    str->len = 0;
+}
+
+NK_API void
+nk_str_free(struct nk_str *str)
+{
+    NK_ASSERT(str);
+    nk_buffer_free(&str->buffer);
+    str->len = 0;
+}
+
+/*
+ * ==============================================================
+ *
+ *                      Command buffer
+ *
+ * ===============================================================
+*/
+NK_INTERN void
+nk_command_buffer_init(struct nk_command_buffer *cmdbuf,
+    struct nk_buffer *buffer, enum nk_command_clipping clip)
+{
+    NK_ASSERT(cmdbuf);
+    NK_ASSERT(buffer);
+    if (!cmdbuf || !buffer) return;
+    cmdbuf->base = buffer;
+    cmdbuf->use_clipping = clip;
+    cmdbuf->begin = buffer->allocated;
+    cmdbuf->end = buffer->allocated;
+    cmdbuf->last = buffer->allocated;
+}
+
+NK_INTERN void
+nk_command_buffer_reset(struct nk_command_buffer *buffer)
+{
+    NK_ASSERT(buffer);
+    if (!buffer) return;
+    buffer->begin = 0;
+    buffer->end = 0;
+    buffer->last = 0;
+    buffer->clip = nk_null_rect;
+#ifdef NK_INCLUDE_COMMAND_USERDATA
+    buffer->userdata.ptr = 0;
+#endif
+}
+
+NK_INTERN void*
+nk_command_buffer_push(struct nk_command_buffer* b,
+    enum nk_command_type t, nk_size size)
+{
+    NK_STORAGE const nk_size align = NK_ALIGNOF(struct nk_command);
+    struct nk_command *cmd;
+    nk_size alignment;
+    void *unaligned;
+    void *memory;
+
+    NK_ASSERT(b);
+    NK_ASSERT(b->base);
+    if (!b) return 0;
+    cmd = (struct nk_command*)nk_buffer_alloc(b->base,NK_BUFFER_FRONT,size,align);
+    if (!cmd) return 0;
+
+    /* make sure the offset to the next command is aligned */
+    b->last = (nk_size)((nk_byte*)cmd - (nk_byte*)b->base->memory.ptr);
+    unaligned = (nk_byte*)cmd + size;
+    memory = NK_ALIGN_PTR(unaligned, align);
+    alignment = (nk_size)((nk_byte*)memory - (nk_byte*)unaligned);
+#ifdef NK_ZERO_COMMAND_MEMORY
+    NK_MEMSET(cmd, 0, size + alignment);
+#endif
+
+    cmd->type = t;
+    cmd->next = b->base->allocated + alignment;
+#ifdef NK_INCLUDE_COMMAND_USERDATA
+    cmd->userdata = b->userdata;
+#endif
+    b->end = cmd->next;
+    return cmd;
+}
+
+NK_API void
+nk_push_scissor(struct nk_command_buffer *b, struct nk_rect r)
+{
+    struct nk_command_scissor *cmd;
+    NK_ASSERT(b);
+    if (!b) return;
+
+    b->clip.x = r.x;
+    b->clip.y = r.y;
+    b->clip.w = r.w;
+    b->clip.h = r.h;
+    cmd = (struct nk_command_scissor*)
+        nk_command_buffer_push(b, NK_COMMAND_SCISSOR, sizeof(*cmd));
+
+    if (!cmd) return;
+    cmd->x = (short)r.x;
+    cmd->y = (short)r.y;
+    cmd->w = (unsigned short)NK_MAX(0, r.w);
+    cmd->h = (unsigned short)NK_MAX(0, r.h);
+}
+
+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 c)
+{
+    struct nk_command_line *cmd;
+    NK_ASSERT(b);
+    if (!b || line_thickness <= 0) return;
+    cmd = (struct nk_command_line*)
+        nk_command_buffer_push(b, NK_COMMAND_LINE, sizeof(*cmd));
+    if (!cmd) return;
+    cmd->line_thickness = (unsigned short)line_thickness;
+    cmd->begin.x = (short)x0;
+    cmd->begin.y = (short)y0;
+    cmd->end.x = (short)x1;
+    cmd->end.y = (short)y1;
+    cmd->color = c;
+}
+
+NK_API void
+nk_stroke_curve(struct nk_command_buffer *b, float ax, float ay,
+    float ctrl0x, float ctrl0y, float ctrl1x, float ctrl1y,
+    float bx, float by, float line_thickness, struct nk_color col)
+{
+    struct nk_command_curve *cmd;
+    NK_ASSERT(b);
+    if (!b || col.a == 0 || line_thickness <= 0) return;
+
+    cmd = (struct nk_command_curve*)
+        nk_command_buffer_push(b, NK_COMMAND_CURVE, sizeof(*cmd));
+    if (!cmd) return;
+    cmd->line_thickness = (unsigned short)line_thickness;
+    cmd->begin.x = (short)ax;
+    cmd->begin.y = (short)ay;
+    cmd->ctrl[0].x = (short)ctrl0x;
+    cmd->ctrl[0].y = (short)ctrl0y;
+    cmd->ctrl[1].x = (short)ctrl1x;
+    cmd->ctrl[1].y = (short)ctrl1y;
+    cmd->end.x = (short)bx;
+    cmd->end.y = (short)by;
+    cmd->color = col;
+}
+
+NK_API void
+nk_stroke_rect(struct nk_command_buffer *b, struct nk_rect rect,
+    float rounding, float line_thickness, struct nk_color c)
+{
+    struct nk_command_rect *cmd;
+    NK_ASSERT(b);
+    if (!b || c.a == 0 || rect.w == 0 || rect.h == 0 || line_thickness <= 0) return;
+    if (b->use_clipping) {
+        const struct nk_rect *clip = &b->clip;
+        if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h,
+            clip->x, clip->y, clip->w, clip->h)) return;
+    }
+    cmd = (struct nk_command_rect*)
+        nk_command_buffer_push(b, NK_COMMAND_RECT, sizeof(*cmd));
+    if (!cmd) return;
+    cmd->rounding = (unsigned short)rounding;
+    cmd->line_thickness = (unsigned short)line_thickness;
+    cmd->x = (short)rect.x;
+    cmd->y = (short)rect.y;
+    cmd->w = (unsigned short)NK_MAX(0, rect.w);
+    cmd->h = (unsigned short)NK_MAX(0, rect.h);
+    cmd->color = c;
+}
+
+NK_API void
+nk_fill_rect(struct nk_command_buffer *b, struct nk_rect rect,
+    float rounding, struct nk_color c)
+{
+    struct nk_command_rect_filled *cmd;
+    NK_ASSERT(b);
+    if (!b || c.a == 0 || rect.w == 0 || rect.h == 0) return;
+    if (b->use_clipping) {
+        const struct nk_rect *clip = &b->clip;
+        if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h,
+            clip->x, clip->y, clip->w, clip->h)) return;
+    }
+
+    cmd = (struct nk_command_rect_filled*)
+        nk_command_buffer_push(b, NK_COMMAND_RECT_FILLED, sizeof(*cmd));
+    if (!cmd) return;
+    cmd->rounding = (unsigned short)rounding;
+    cmd->x = (short)rect.x;
+    cmd->y = (short)rect.y;
+    cmd->w = (unsigned short)NK_MAX(0, rect.w);
+    cmd->h = (unsigned short)NK_MAX(0, rect.h);
+    cmd->color = c;
+}
+
+NK_API void
+nk_fill_rect_multi_color(struct nk_command_buffer *b, struct nk_rect rect,
+    struct nk_color left, struct nk_color top, struct nk_color right,
+    struct nk_color bottom)
+{
+    struct nk_command_rect_multi_color *cmd;
+    NK_ASSERT(b);
+    if (!b || rect.w == 0 || rect.h == 0) return;
+    if (b->use_clipping) {
+        const struct nk_rect *clip = &b->clip;
+        if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h,
+            clip->x, clip->y, clip->w, clip->h)) return;
+    }
+
+    cmd = (struct nk_command_rect_multi_color*)
+        nk_command_buffer_push(b, NK_COMMAND_RECT_MULTI_COLOR, sizeof(*cmd));
+    if (!cmd) return;
+    cmd->x = (short)rect.x;
+    cmd->y = (short)rect.y;
+    cmd->w = (unsigned short)NK_MAX(0, rect.w);
+    cmd->h = (unsigned short)NK_MAX(0, rect.h);
+    cmd->left = left;
+    cmd->top = top;
+    cmd->right = right;
+    cmd->bottom = bottom;
+}
+
+NK_API void
+nk_stroke_circle(struct nk_command_buffer *b, struct nk_rect r,
+    float line_thickness, struct nk_color c)
+{
+    struct nk_command_circle *cmd;
+    if (!b || r.w == 0 || r.h == 0 || line_thickness <= 0) return;
+    if (b->use_clipping) {
+        const struct nk_rect *clip = &b->clip;
+        if (!NK_INTERSECT(r.x, r.y, r.w, r.h, clip->x, clip->y, clip->w, clip->h))
+            return;
+    }
+
+    cmd = (struct nk_command_circle*)
+        nk_command_buffer_push(b, NK_COMMAND_CIRCLE, sizeof(*cmd));
+    if (!cmd) return;
+    cmd->line_thickness = (unsigned short)line_thickness;
+    cmd->x = (short)r.x;
+    cmd->y = (short)r.y;
+    cmd->w = (unsigned short)NK_MAX(r.w, 0);
+    cmd->h = (unsigned short)NK_MAX(r.h, 0);
+    cmd->color = c;
+}
+
+NK_API void
+nk_fill_circle(struct nk_command_buffer *b, struct nk_rect r, struct nk_color c)
+{
+    struct nk_command_circle_filled *cmd;
+    NK_ASSERT(b);
+    if (!b || c.a == 0 || r.w == 0 || r.h == 0) return;
+    if (b->use_clipping) {
+        const struct nk_rect *clip = &b->clip;
+        if (!NK_INTERSECT(r.x, r.y, r.w, r.h, clip->x, clip->y, clip->w, clip->h))
+            return;
+    }
+
+    cmd = (struct nk_command_circle_filled*)
+        nk_command_buffer_push(b, NK_COMMAND_CIRCLE_FILLED, sizeof(*cmd));
+    if (!cmd) return;
+    cmd->x = (short)r.x;
+    cmd->y = (short)r.y;
+    cmd->w = (unsigned short)NK_MAX(r.w, 0);
+    cmd->h = (unsigned short)NK_MAX(r.h, 0);
+    cmd->color = c;
+}
+
+NK_API void
+nk_stroke_arc(struct nk_command_buffer *b, float cx, float cy, float radius,
+    float a_min, float a_max, float line_thickness, struct nk_color c)
+{
+    struct nk_command_arc *cmd;
+    if (!b || c.a == 0 || line_thickness <= 0) return;
+    cmd = (struct nk_command_arc*)
+        nk_command_buffer_push(b, NK_COMMAND_ARC, sizeof(*cmd));
+    if (!cmd) return;
+    cmd->line_thickness = (unsigned short)line_thickness;
+    cmd->cx = (short)cx;
+    cmd->cy = (short)cy;
+    cmd->r = (unsigned short)radius;
+    cmd->a[0] = a_min;
+    cmd->a[1] = a_max;
+    cmd->color = c;
+}
+
+NK_API void
+nk_fill_arc(struct nk_command_buffer *b, float cx, float cy, float radius,
+    float a_min, float a_max, struct nk_color c)
+{
+    struct nk_command_arc_filled *cmd;
+    NK_ASSERT(b);
+    if (!b || c.a == 0) return;
+    cmd = (struct nk_command_arc_filled*)
+        nk_command_buffer_push(b, NK_COMMAND_ARC_FILLED, sizeof(*cmd));
+    if (!cmd) return;
+    cmd->cx = (short)cx;
+    cmd->cy = (short)cy;
+    cmd->r = (unsigned short)radius;
+    cmd->a[0] = a_min;
+    cmd->a[1] = a_max;
+    cmd->color = c;
+}
+
+NK_API void
+nk_stroke_triangle(struct nk_command_buffer *b, float x0, float y0, float x1,
+    float y1, float x2, float y2, float line_thickness, struct nk_color c)
+{
+    struct nk_command_triangle *cmd;
+    NK_ASSERT(b);
+    if (!b || c.a == 0 || line_thickness <= 0) return;
+    if (b->use_clipping) {
+        const struct nk_rect *clip = &b->clip;
+        if (!NK_INBOX(x0, y0, clip->x, clip->y, clip->w, clip->h) &&
+            !NK_INBOX(x1, y1, clip->x, clip->y, clip->w, clip->h) &&
+            !NK_INBOX(x2, y2, clip->x, clip->y, clip->w, clip->h))
+            return;
+    }
+
+    cmd = (struct nk_command_triangle*)
+        nk_command_buffer_push(b, NK_COMMAND_TRIANGLE, sizeof(*cmd));
+    if (!cmd) return;
+    cmd->line_thickness = (unsigned short)line_thickness;
+    cmd->a.x = (short)x0;
+    cmd->a.y = (short)y0;
+    cmd->b.x = (short)x1;
+    cmd->b.y = (short)y1;
+    cmd->c.x = (short)x2;
+    cmd->c.y = (short)y2;
+    cmd->color = c;
+}
+
+NK_API void
+nk_fill_triangle(struct nk_command_buffer *b, float x0, float y0, float x1,
+    float y1, float x2, float y2, struct nk_color c)
+{
+    struct nk_command_triangle_filled *cmd;
+    NK_ASSERT(b);
+    if (!b || c.a == 0) return;
+    if (!b) return;
+    if (b->use_clipping) {
+        const struct nk_rect *clip = &b->clip;
+        if (!NK_INBOX(x0, y0, clip->x, clip->y, clip->w, clip->h) &&
+            !NK_INBOX(x1, y1, clip->x, clip->y, clip->w, clip->h) &&
+            !NK_INBOX(x2, y2, clip->x, clip->y, clip->w, clip->h))
+            return;
+    }
+
+    cmd = (struct nk_command_triangle_filled*)
+        nk_command_buffer_push(b, NK_COMMAND_TRIANGLE_FILLED, sizeof(*cmd));
+    if (!cmd) return;
+    cmd->a.x = (short)x0;
+    cmd->a.y = (short)y0;
+    cmd->b.x = (short)x1;
+    cmd->b.y = (short)y1;
+    cmd->c.x = (short)x2;
+    cmd->c.y = (short)y2;
+    cmd->color = c;
+}
+
+NK_API void
+nk_stroke_polygon(struct nk_command_buffer *b,  float *points, int point_count,
+    float line_thickness, struct nk_color col)
+{
+    int i;
+    nk_size size = 0;
+    struct nk_command_polygon *cmd;
+
+    NK_ASSERT(b);
+    if (!b || col.a == 0 || line_thickness <= 0) return;
+    size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count;
+    cmd = (struct nk_command_polygon*) nk_command_buffer_push(b, NK_COMMAND_POLYGON, size);
+    if (!cmd) return;
+    cmd->color = col;
+    cmd->line_thickness = (unsigned short)line_thickness;
+    cmd->point_count = (unsigned short)point_count;
+    for (i = 0; i < point_count; ++i) {
+        cmd->points[i].x = (short)points[i*2];
+        cmd->points[i].y = (short)points[i*2+1];
+    }
+}
+
+NK_API void
+nk_fill_polygon(struct nk_command_buffer *b, float *points, int point_count,
+    struct nk_color col)
+{
+    int i;
+    nk_size size = 0;
+    struct nk_command_polygon_filled *cmd;
+
+    NK_ASSERT(b);
+    if (!b || col.a == 0) return;
+    size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count;
+    cmd = (struct nk_command_polygon_filled*)
+        nk_command_buffer_push(b, NK_COMMAND_POLYGON_FILLED, size);
+    if (!cmd) return;
+    cmd->color = col;
+    cmd->point_count = (unsigned short)point_count;
+    for (i = 0; i < point_count; ++i) {
+        cmd->points[i].x = (short)points[i*2+0];
+        cmd->points[i].y = (short)points[i*2+1];
+    }
+}
+
+NK_API void
+nk_stroke_polyline(struct nk_command_buffer *b, float *points, int point_count,
+    float line_thickness, struct nk_color col)
+{
+    int i;
+    nk_size size = 0;
+    struct nk_command_polyline *cmd;
+
+    NK_ASSERT(b);
+    if (!b || col.a == 0 || line_thickness <= 0) return;
+    size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count;
+    cmd = (struct nk_command_polyline*) nk_command_buffer_push(b, NK_COMMAND_POLYLINE, size);
+    if (!cmd) return;
+    cmd->color = col;
+    cmd->point_count = (unsigned short)point_count;
+    cmd->line_thickness = (unsigned short)line_thickness;
+    for (i = 0; i < point_count; ++i) {
+        cmd->points[i].x = (short)points[i*2];
+        cmd->points[i].y = (short)points[i*2+1];
+    }
+}
+
+NK_API void
+nk_draw_image(struct nk_command_buffer *b, struct nk_rect r,
+    const struct nk_image *img, struct nk_color col)
+{
+    struct nk_command_image *cmd;
+    NK_ASSERT(b);
+    if (!b) return;
+    if (b->use_clipping) {
+        const struct nk_rect *c = &b->clip;
+        if (c->w == 0 || c->h == 0 || !NK_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h))
+            return;
+    }
+
+    cmd = (struct nk_command_image*)
+        nk_command_buffer_push(b, NK_COMMAND_IMAGE, sizeof(*cmd));
+    if (!cmd) return;
+    cmd->x = (short)r.x;
+    cmd->y = (short)r.y;
+    cmd->w = (unsigned short)NK_MAX(0, r.w);
+    cmd->h = (unsigned short)NK_MAX(0, r.h);
+    cmd->img = *img;
+    cmd->col = col;
+}
+
+NK_API void
+nk_push_custom(struct nk_command_buffer *b, struct nk_rect r,
+    nk_command_custom_callback cb, nk_handle usr)
+{
+    struct nk_command_custom *cmd;
+    NK_ASSERT(b);
+    if (!b) return;
+    if (b->use_clipping) {
+        const struct nk_rect *c = &b->clip;
+        if (c->w == 0 || c->h == 0 || !NK_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h))
+            return;
+    }
+
+    cmd = (struct nk_command_custom*)
+        nk_command_buffer_push(b, NK_COMMAND_CUSTOM, sizeof(*cmd));
+    if (!cmd) return;
+    cmd->x = (short)r.x;
+    cmd->y = (short)r.y;
+    cmd->w = (unsigned short)NK_MAX(0, r.w);
+    cmd->h = (unsigned short)NK_MAX(0, r.h);
+    cmd->callback_data = usr;
+    cmd->callback = cb;
+}
+
+NK_API void
+nk_draw_text(struct nk_command_buffer *b, struct nk_rect r,
+    const char *string, int length, const struct nk_user_font *font,
+    struct nk_color bg, struct nk_color fg)
+{
+    float text_width = 0;
+    struct nk_command_text *cmd;
+
+    NK_ASSERT(b);
+    NK_ASSERT(font);
+    if (!b || !string || !length || (bg.a == 0 && fg.a == 0)) return;
+    if (b->use_clipping) {
+        const struct nk_rect *c = &b->clip;
+        if (c->w == 0 || c->h == 0 || !NK_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h))
+            return;
+    }
+
+    /* make sure text fits inside bounds */
+    text_width = font->width(font->userdata, font->height, string, length);
+    if (text_width > r.w){
+        int glyphs = 0;
+        float txt_width = (float)text_width;
+        length = nk_text_clamp(font, string, length, r.w, &glyphs, &txt_width, 0,0);
+    }
+
+    if (!length) return;
+    cmd = (struct nk_command_text*)
+        nk_command_buffer_push(b, NK_COMMAND_TEXT, sizeof(*cmd) + (nk_size)(length + 1));
+    if (!cmd) return;
+    cmd->x = (short)r.x;
+    cmd->y = (short)r.y;
+    cmd->w = (unsigned short)r.w;
+    cmd->h = (unsigned short)r.h;
+    cmd->background = bg;
+    cmd->foreground = fg;
+    cmd->font = font;
+    cmd->length = length;
+    cmd->height = font->height;
+    NK_MEMCPY(cmd->string, string, (nk_size)length);
+    cmd->string[length] = '\0';
+}
+
+/* ==============================================================
+ *
+ *                          DRAW LIST
+ *
+ * ===============================================================*/
+#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT
+NK_API void
+nk_draw_list_init(struct nk_draw_list *list)
+{
+    nk_size i = 0;
+    NK_ASSERT(list);
+    if (!list) return;
+    nk_zero(list, sizeof(*list));
+    for (i = 0; i < NK_LEN(list->circle_vtx); ++i) {
+        const float a = ((float)i / (float)NK_LEN(list->circle_vtx)) * 2 * NK_PI;
+        list->circle_vtx[i].x = (float)NK_COS(a);
+        list->circle_vtx[i].y = (float)NK_SIN(a);
+    }
+}
+
+NK_API void
+nk_draw_list_setup(struct nk_draw_list *canvas, const struct nk_convert_config *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)
+{
+    NK_ASSERT(canvas);
+    NK_ASSERT(config);
+    NK_ASSERT(cmds);
+    NK_ASSERT(vertices);
+    NK_ASSERT(elements);
+    if (!canvas || !config || !cmds || !vertices || !elements)
+        return;
+
+    canvas->buffer = cmds;
+    canvas->config = *config;
+    canvas->elements = elements;
+    canvas->vertices = vertices;
+    canvas->line_AA = line_aa;
+    canvas->shape_AA = shape_aa;
+    canvas->clip_rect = nk_null_rect;
+}
+
+NK_API const struct nk_draw_command*
+nk__draw_list_begin(const struct nk_draw_list *canvas, const struct nk_buffer *buffer)
+{
+    nk_byte *memory;
+    nk_size offset;
+    const struct nk_draw_command *cmd;
+
+    NK_ASSERT(buffer);
+    if (!buffer || !buffer->size || !canvas->cmd_count)
+        return 0;
+
+    memory = (nk_byte*)buffer->memory.ptr;
+    offset = buffer->memory.size - canvas->cmd_offset;
+    cmd = nk_ptr_add(const struct nk_draw_command, memory, offset);
+    return cmd;
+}
+
+NK_API const struct nk_draw_command*
+nk__draw_list_end(const struct nk_draw_list *canvas, const struct nk_buffer *buffer)
+{
+    nk_size size;
+    nk_size offset;
+    nk_byte *memory;
+    const struct nk_draw_command *end;
+
+    NK_ASSERT(buffer);
+    NK_ASSERT(canvas);
+    if (!buffer || !canvas)
+        return 0;
+
+    memory = (nk_byte*)buffer->memory.ptr;
+    size = buffer->memory.size;
+    offset = size - canvas->cmd_offset;
+    end = nk_ptr_add(const struct nk_draw_command, memory, offset);
+    end -= (canvas->cmd_count-1);
+    return end;
+}
+
+NK_API const struct nk_draw_command*
+nk__draw_list_next(const struct nk_draw_command *cmd,
+    const struct nk_buffer *buffer, const struct nk_draw_list *canvas)
+{
+    const struct nk_draw_command *end;
+    NK_ASSERT(buffer);
+    NK_ASSERT(canvas);
+    if (!cmd || !buffer || !canvas)
+        return 0;
+
+    end = nk__draw_list_end(canvas, buffer);
+    if (cmd <= end) return 0;
+    return (cmd-1);
+}
+
+NK_API void
+nk_draw_list_clear(struct nk_draw_list *list)
+{
+    NK_ASSERT(list);
+    if (!list) return;
+    if (list->buffer)
+        nk_buffer_clear(list->buffer);
+    if (list->vertices)
+        nk_buffer_clear(list->vertices);
+    if (list->elements)
+        nk_buffer_clear(list->elements);
+
+    list->element_count = 0;
+    list->vertex_count = 0;
+    list->cmd_offset = 0;
+    list->cmd_count = 0;
+    list->path_count = 0;
+    list->vertices = 0;
+    list->elements = 0;
+    list->clip_rect = nk_null_rect;
+}
+
+NK_INTERN struct nk_vec2*
+nk_draw_list_alloc_path(struct nk_draw_list *list, int count)
+{
+    struct nk_vec2 *points;
+    NK_STORAGE const nk_size point_align = NK_ALIGNOF(struct nk_vec2);
+    NK_STORAGE const nk_size point_size = sizeof(struct nk_vec2);
+    points = (struct nk_vec2*)
+        nk_buffer_alloc(list->buffer, NK_BUFFER_FRONT,
+                        point_size * (nk_size)count, point_align);
+
+    if (!points) return 0;
+    if (!list->path_offset) {
+        void *memory = nk_buffer_memory(list->buffer);
+        list->path_offset = (unsigned int)((nk_byte*)points - (nk_byte*)memory);
+    }
+    list->path_count += (unsigned int)count;
+    return points;
+}
+
+NK_INTERN struct nk_vec2
+nk_draw_list_path_last(struct nk_draw_list *list)
+{
+    void *memory;
+    struct nk_vec2 *point;
+    NK_ASSERT(list->path_count);
+    memory = nk_buffer_memory(list->buffer);
+    point = nk_ptr_add(struct nk_vec2, memory, list->path_offset);
+    point += (list->path_count-1);
+    return *point;
+}
+
+NK_INTERN struct nk_draw_command*
+nk_draw_list_push_command(struct nk_draw_list *list, struct nk_rect clip,
+    nk_handle texture)
+{
+    NK_STORAGE const nk_size cmd_align = NK_ALIGNOF(struct nk_draw_command);
+    NK_STORAGE const nk_size cmd_size = sizeof(struct nk_draw_command);
+    struct nk_draw_command *cmd;
+
+    NK_ASSERT(list);
+    cmd = (struct nk_draw_command*)
+        nk_buffer_alloc(list->buffer, NK_BUFFER_BACK, cmd_size, cmd_align);
+
+    if (!cmd) return 0;
+    if (!list->cmd_count) {
+        nk_byte *memory = (nk_byte*)nk_buffer_memory(list->buffer);
+        nk_size total = nk_buffer_total(list->buffer);
+        memory = nk_ptr_add(nk_byte, memory, total);
+        list->cmd_offset = (nk_size)(memory - (nk_byte*)cmd);
+    }
+
+    cmd->elem_count = 0;
+    cmd->clip_rect = clip;
+    cmd->texture = texture;
+#ifdef NK_INCLUDE_COMMAND_USERDATA
+    cmd->userdata = list->userdata;
+#endif
+
+    list->cmd_count++;
+    list->clip_rect = clip;
+    return cmd;
+}
+
+NK_INTERN struct nk_draw_command*
+nk_draw_list_command_last(struct nk_draw_list *list)
+{
+    void *memory;
+    nk_size size;
+    struct nk_draw_command *cmd;
+    NK_ASSERT(list->cmd_count);
+
+    memory = nk_buffer_memory(list->buffer);
+    size = nk_buffer_total(list->buffer);
+    cmd = nk_ptr_add(struct nk_draw_command, memory, size - list->cmd_offset);
+    return (cmd - (list->cmd_count-1));
+}
+
+NK_INTERN void
+nk_draw_list_add_clip(struct nk_draw_list *list, struct nk_rect rect)
+{
+    NK_ASSERT(list);
+    if (!list) return;
+    if (!list->cmd_count) {
+        nk_draw_list_push_command(list, rect, list->config.null.texture);
+    } else {
+        struct nk_draw_command *prev = nk_draw_list_command_last(list);
+        if (prev->elem_count == 0)
+            prev->clip_rect = rect;
+        nk_draw_list_push_command(list, rect, prev->texture);
+    }
+}
+
+NK_INTERN void
+nk_draw_list_push_image(struct nk_draw_list *list, nk_handle texture)
+{
+    NK_ASSERT(list);
+    if (!list) return;
+    if (!list->cmd_count) {
+        nk_draw_list_push_command(list, nk_null_rect, texture);
+    } else {
+        struct nk_draw_command *prev = nk_draw_list_command_last(list);
+        if (prev->elem_count == 0)
+            prev->texture = texture;
+        else if (prev->texture.id != texture.id)
+            nk_draw_list_push_command(list, prev->clip_rect, texture);
+    }
+}
+
+#ifdef NK_INCLUDE_COMMAND_USERDATA
+NK_API void
+nk_draw_list_push_userdata(struct nk_draw_list *list, nk_handle userdata)
+{
+    list->userdata = userdata;
+}
+#endif
+
+NK_INTERN void*
+nk_draw_list_alloc_vertices(struct nk_draw_list *list, nk_size count)
+{
+    void *vtx;
+    NK_ASSERT(list);
+    if (!list) return 0;
+    vtx = nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT,
+        list->config.vertex_size*count, list->config.vertex_alignment);
+    if (!vtx) return 0;
+    list->vertex_count += (unsigned int)count;
+    return vtx;
+}
+
+NK_INTERN nk_draw_index*
+nk_draw_list_alloc_elements(struct nk_draw_list *list, nk_size count)
+{
+    nk_draw_index *ids;
+    struct nk_draw_command *cmd;
+    NK_STORAGE const nk_size elem_align = NK_ALIGNOF(nk_draw_index);
+    NK_STORAGE const nk_size elem_size = sizeof(nk_draw_index);
+    NK_ASSERT(list);
+    if (!list) return 0;
+
+    ids = (nk_draw_index*)
+        nk_buffer_alloc(list->elements, NK_BUFFER_FRONT, elem_size*count, elem_align);
+    if (!ids) return 0;
+    cmd = nk_draw_list_command_last(list);
+    list->element_count += (unsigned int)count;
+    cmd->elem_count += (unsigned int)count;
+    return ids;
+}
+
+NK_INTERN int
+nk_draw_vertex_layout_element_is_end_of_layout(
+    const struct nk_draw_vertex_layout_element *element)
+{
+    return (element->attribute == NK_VERTEX_ATTRIBUTE_COUNT ||
+            element->format == NK_FORMAT_COUNT);
+}
+
+NK_INTERN void
+nk_draw_vertex_color(void *attribute, const float *values,
+    enum nk_draw_vertex_layout_format format)
+{
+    /* if this triggers you tried to provide a value format for a color */
+    NK_ASSERT(format >= NK_FORMAT_COLOR_BEGIN);
+    NK_ASSERT(format <= NK_FORMAT_COLOR_END);
+    if (format < NK_FORMAT_COLOR_BEGIN || format > NK_FORMAT_COLOR_END) return;
+
+    switch (format) {
+    default: NK_ASSERT(0 && "Invalid vertex layout color format"); break;
+    case NK_FORMAT_R8G8B8A8:
+    case NK_FORMAT_R8G8B8: {
+        struct nk_color col = nk_rgba_fv(values);
+        NK_MEMCPY(attribute, &col.r, sizeof(col));
+    } break;
+    case NK_FORMAT_B8G8R8A8: {
+        struct nk_color col = nk_rgba_fv(values);
+        struct nk_color bgra = nk_rgba(col.b, col.g, col.r, col.a);
+        NK_MEMCPY(attribute, &bgra, sizeof(bgra));
+    } break;
+    case NK_FORMAT_R16G15B16: {
+        nk_ushort col[3];
+        col[0] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[0] * NK_USHORT_MAX, NK_USHORT_MAX);
+        col[1] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[1] * NK_USHORT_MAX, NK_USHORT_MAX);
+        col[2] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[2] * NK_USHORT_MAX, NK_USHORT_MAX);
+        NK_MEMCPY(attribute, col, sizeof(col));
+    } break;
+    case NK_FORMAT_R16G15B16A16: {
+        nk_ushort col[4];
+        col[0] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[0] * NK_USHORT_MAX, NK_USHORT_MAX);
+        col[1] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[1] * NK_USHORT_MAX, NK_USHORT_MAX);
+        col[2] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[2] * NK_USHORT_MAX, NK_USHORT_MAX);
+        col[3] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[3] * NK_USHORT_MAX, NK_USHORT_MAX);
+        NK_MEMCPY(attribute, col, sizeof(col));
+    } break;
+    case NK_FORMAT_R32G32B32: {
+        nk_uint col[3];
+        col[0] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[0] * NK_UINT_MAX, NK_UINT_MAX);
+        col[1] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[1] * NK_UINT_MAX, NK_UINT_MAX);
+        col[2] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[2] * NK_UINT_MAX, NK_UINT_MAX);
+        NK_MEMCPY(attribute, col, sizeof(col));
+    } break;
+    case NK_FORMAT_R32G32B32A32: {
+        nk_uint col[4];
+        col[0] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[0] * NK_UINT_MAX, NK_UINT_MAX);
+        col[1] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[1] * NK_UINT_MAX, NK_UINT_MAX);
+        col[2] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[2] * NK_UINT_MAX, NK_UINT_MAX);
+        col[3] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[3] * NK_UINT_MAX, NK_UINT_MAX);
+        NK_MEMCPY(attribute, col, sizeof(col));
+    } break;
+    case NK_FORMAT_R32G32B32A32_FLOAT:
+        NK_MEMCPY(attribute, values, sizeof(float)*4);
+        break;
+    case NK_FORMAT_R32G32B32A32_DOUBLE: {
+        double col[4];
+        col[0] = (double)NK_SATURATE(values[0]);
+        col[1] = (double)NK_SATURATE(values[1]);
+        col[2] = (double)NK_SATURATE(values[2]);
+        col[3] = (double)NK_SATURATE(values[3]);
+        NK_MEMCPY(attribute, col, sizeof(col));
+    } break;
+    case NK_FORMAT_RGB32:
+    case NK_FORMAT_RGBA32: {
+        struct nk_color col = nk_rgba_fv(values);
+        nk_uint color = nk_color_u32(col);
+        NK_MEMCPY(attribute, &color, sizeof(color));
+    } break;
+    }
+}
+
+NK_INTERN void
+nk_draw_vertex_element(void *dst, const float *values, int value_count,
+    enum nk_draw_vertex_layout_format format)
+{
+    int value_index;
+    void *attribute = dst;
+    /* if this triggers you tried to provide a color format for a value */
+    NK_ASSERT(format < NK_FORMAT_COLOR_BEGIN);
+    if (format >= NK_FORMAT_COLOR_BEGIN && format <= NK_FORMAT_COLOR_END) return;
+    for (value_index = 0; value_index < value_count; ++value_index) {
+        switch (format) {
+        default: NK_ASSERT(0 && "invalid vertex layout format"); break;
+        case NK_FORMAT_SCHAR: {
+            char value = (char)NK_CLAMP(NK_SCHAR_MIN, values[value_index], NK_SCHAR_MAX);
+            NK_MEMCPY(attribute, &value, sizeof(value));
+            attribute = (void*)((char*)attribute + sizeof(char));
+        } break;
+        case NK_FORMAT_SSHORT: {
+            nk_short value = (nk_short)NK_CLAMP(NK_SSHORT_MIN, values[value_index], NK_SSHORT_MAX);
+            NK_MEMCPY(attribute, &value, sizeof(value));
+            attribute = (void*)((char*)attribute + sizeof(value));
+        } break;
+        case NK_FORMAT_SINT: {
+            nk_int value = (nk_int)NK_CLAMP(NK_SINT_MIN, values[value_index], NK_SINT_MAX);
+            NK_MEMCPY(attribute, &value, sizeof(value));
+            attribute = (void*)((char*)attribute + sizeof(nk_int));
+        } break;
+        case NK_FORMAT_UCHAR: {
+            unsigned char value = (unsigned char)NK_CLAMP(NK_UCHAR_MIN, values[value_index], NK_UCHAR_MAX);
+            NK_MEMCPY(attribute, &value, sizeof(value));
+            attribute = (void*)((char*)attribute + sizeof(unsigned char));
+        } break;
+        case NK_FORMAT_USHORT: {
+            nk_ushort value = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[value_index], NK_USHORT_MAX);
+            NK_MEMCPY(attribute, &value, sizeof(value));
+            attribute = (void*)((char*)attribute + sizeof(value));
+            } break;
+        case NK_FORMAT_UINT: {
+            nk_uint value = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[value_index], NK_UINT_MAX);
+            NK_MEMCPY(attribute, &value, sizeof(value));
+            attribute = (void*)((char*)attribute + sizeof(nk_uint));
+        } break;
+        case NK_FORMAT_FLOAT:
+            NK_MEMCPY(attribute, &values[value_index], sizeof(values[value_index]));
+            attribute = (void*)((char*)attribute + sizeof(float));
+            break;
+        case NK_FORMAT_DOUBLE: {
+            double value = (double)values[value_index];
+            NK_MEMCPY(attribute, &value, sizeof(value));
+            attribute = (void*)((char*)attribute + sizeof(double));
+            } break;
+        }
+    }
+}
+
+NK_INTERN void*
+nk_draw_vertex(void *dst, const struct nk_convert_config *config,
+    struct nk_vec2 pos, struct nk_vec2 uv, struct nk_colorf color)
+{
+    void *result = (void*)((char*)dst + config->vertex_size);
+    const struct nk_draw_vertex_layout_element *elem_iter = config->vertex_layout;
+    while (!nk_draw_vertex_layout_element_is_end_of_layout(elem_iter)) {
+        void *address = (void*)((char*)dst + elem_iter->offset);
+        switch (elem_iter->attribute) {
+        case NK_VERTEX_ATTRIBUTE_COUNT:
+        default: NK_ASSERT(0 && "wrong element attribute");
+        case NK_VERTEX_POSITION: nk_draw_vertex_element(address, &pos.x, 2, elem_iter->format); break;
+        case NK_VERTEX_TEXCOORD: nk_draw_vertex_element(address, &uv.x, 2, elem_iter->format); break;
+        case NK_VERTEX_COLOR: nk_draw_vertex_color(address, &color.r, elem_iter->format); break;
+        }
+        elem_iter++;
+    }
+    return result;
+}
+
+NK_API void
+nk_draw_list_stroke_poly_line(struct nk_draw_list *list, const struct nk_vec2 *points,
+    const unsigned int points_count, struct nk_color color, enum nk_draw_list_stroke closed,
+    float thickness, enum nk_anti_aliasing aliasing)
+{
+    nk_size count;
+    int thick_line;
+    struct nk_colorf col;
+    struct nk_colorf col_trans;
+    NK_ASSERT(list);
+    if (!list || points_count < 2) return;
+
+    color.a = (nk_byte)((float)color.a * list->config.global_alpha);
+    count = points_count;
+    if (!closed) count = points_count-1;
+    thick_line = thickness > 1.0f;
+
+#ifdef NK_INCLUDE_COMMAND_USERDATA
+    nk_draw_list_push_userdata(list, list->userdata);
+#endif
+
+    color.a = (nk_byte)((float)color.a * list->config.global_alpha);
+    nk_color_fv(&col.r, color);
+    col_trans = col;
+    col_trans.a = 0;
+
+    if (aliasing == NK_ANTI_ALIASING_ON) {
+        /* ANTI-ALIASED STROKE */
+        const float AA_SIZE = 1.0f;
+        NK_STORAGE const nk_size pnt_align = NK_ALIGNOF(struct nk_vec2);
+        NK_STORAGE const nk_size pnt_size = sizeof(struct nk_vec2);
+
+        /* allocate vertices and elements  */
+        nk_size i1 = 0;
+        nk_size vertex_offset;
+        nk_size index = list->vertex_count;
+
+        const nk_size idx_count = (thick_line) ?  (count * 18) : (count * 12);
+        const nk_size vtx_count = (thick_line) ? (points_count * 4): (points_count *3);
+
+        void *vtx = nk_draw_list_alloc_vertices(list, vtx_count);
+        nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count);
+
+        nk_size size;
+        struct nk_vec2 *normals, *temp;
+        if (!vtx || !ids) return;
+
+        /* temporary allocate normals + points */
+        vertex_offset = (nk_size)((nk_byte*)vtx - (nk_byte*)list->vertices->memory.ptr);
+        nk_buffer_mark(list->vertices, NK_BUFFER_FRONT);
+        size = pnt_size * ((thick_line) ? 5 : 3) * points_count;
+        normals = (struct nk_vec2*) nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, size, pnt_align);
+        NK_ASSERT(normals);
+        if (!normals) return;
+        temp = normals + points_count;
+
+        /* make sure vertex pointer is still correct */
+        vtx = (void*)((nk_byte*)list->vertices->memory.ptr + vertex_offset);
+
+        /* calculate normals */
+        for (i1 = 0; i1 < count; ++i1) {
+            const nk_size i2 = ((i1 + 1) == points_count) ? 0 : (i1 + 1);
+            struct nk_vec2 diff = nk_vec2_sub(points[i2], points[i1]);
+            float len;
+
+            /* vec2 inverted length  */
+            len = nk_vec2_len_sqr(diff);
+            if (len != 0.0f)
+                len = nk_inv_sqrt(len);
+            else len = 1.0f;
+
+            diff = nk_vec2_muls(diff, len);
+            normals[i1].x = diff.y;
+            normals[i1].y = -diff.x;
+        }
+
+        if (!closed)
+            normals[points_count-1] = normals[points_count-2];
+
+        if (!thick_line) {
+            nk_size idx1, i;
+            if (!closed) {
+                struct nk_vec2 d;
+                temp[0] = nk_vec2_add(points[0], nk_vec2_muls(normals[0], AA_SIZE));
+                temp[1] = nk_vec2_sub(points[0], nk_vec2_muls(normals[0], AA_SIZE));
+                d = nk_vec2_muls(normals[points_count-1], AA_SIZE);
+                temp[(points_count-1) * 2 + 0] = nk_vec2_add(points[points_count-1], d);
+                temp[(points_count-1) * 2 + 1] = nk_vec2_sub(points[points_count-1], d);
+            }
+
+            /* fill elements */
+            idx1 = index;
+            for (i1 = 0; i1 < count; i1++) {
+                struct nk_vec2 dm;
+                float dmr2;
+                nk_size i2 = ((i1 + 1) == points_count) ? 0 : (i1 + 1);
+                nk_size idx2 = ((i1+1) == points_count) ? index: (idx1 + 3);
+
+                /* average normals */
+                dm = nk_vec2_muls(nk_vec2_add(normals[i1], normals[i2]), 0.5f);
+                dmr2 = dm.x * dm.x + dm.y* dm.y;
+                if (dmr2 > 0.000001f) {
+                    float scale = 1.0f/dmr2;
+                    scale = NK_MIN(100.0f, scale);
+                    dm = nk_vec2_muls(dm, scale);
+                }
+
+                dm = nk_vec2_muls(dm, AA_SIZE);
+                temp[i2*2+0] = nk_vec2_add(points[i2], dm);
+                temp[i2*2+1] = nk_vec2_sub(points[i2], dm);
+
+                ids[0] = (nk_draw_index)(idx2 + 0); ids[1] = (nk_draw_index)(idx1+0);
+                ids[2] = (nk_draw_index)(idx1 + 2); ids[3] = (nk_draw_index)(idx1+2);
+                ids[4] = (nk_draw_index)(idx2 + 2); ids[5] = (nk_draw_index)(idx2+0);
+                ids[6] = (nk_draw_index)(idx2 + 1); ids[7] = (nk_draw_index)(idx1+1);
+                ids[8] = (nk_draw_index)(idx1 + 0); ids[9] = (nk_draw_index)(idx1+0);
+                ids[10]= (nk_draw_index)(idx2 + 0); ids[11]= (nk_draw_index)(idx2+1);
+                ids += 12;
+                idx1 = idx2;
+            }
+
+            /* fill vertices */
+            for (i = 0; i < points_count; ++i) {
+                const struct nk_vec2 uv = list->config.null.uv;
+                vtx = nk_draw_vertex(vtx, &list->config, points[i], uv, col);
+                vtx = nk_draw_vertex(vtx, &list->config, temp[i*2+0], uv, col_trans);
+                vtx = nk_draw_vertex(vtx, &list->config, temp[i*2+1], uv, col_trans);
+            }
+        } else {
+            nk_size idx1, i;
+            const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f;
+            if (!closed) {
+                struct nk_vec2 d1 = nk_vec2_muls(normals[0], half_inner_thickness + AA_SIZE);
+                struct nk_vec2 d2 = nk_vec2_muls(normals[0], half_inner_thickness);
+
+                temp[0] = nk_vec2_add(points[0], d1);
+                temp[1] = nk_vec2_add(points[0], d2);
+                temp[2] = nk_vec2_sub(points[0], d2);
+                temp[3] = nk_vec2_sub(points[0], d1);
+
+                d1 = nk_vec2_muls(normals[points_count-1], half_inner_thickness + AA_SIZE);
+                d2 = nk_vec2_muls(normals[points_count-1], half_inner_thickness);
+
+                temp[(points_count-1)*4+0] = nk_vec2_add(points[points_count-1], d1);
+                temp[(points_count-1)*4+1] = nk_vec2_add(points[points_count-1], d2);
+                temp[(points_count-1)*4+2] = nk_vec2_sub(points[points_count-1], d2);
+                temp[(points_count-1)*4+3] = nk_vec2_sub(points[points_count-1], d1);
+            }
+
+            /* add all elements */
+            idx1 = index;
+            for (i1 = 0; i1 < count; ++i1) {
+                struct nk_vec2 dm_out, dm_in;
+                const nk_size i2 = ((i1+1) == points_count) ? 0: (i1 + 1);
+                nk_size idx2 = ((i1+1) == points_count) ? index: (idx1 + 4);
+
+                /* average normals */
+                struct nk_vec2 dm = nk_vec2_muls(nk_vec2_add(normals[i1], normals[i2]), 0.5f);
+                float dmr2 = dm.x * dm.x + dm.y* dm.y;
+                if (dmr2 > 0.000001f) {
+                    float scale = 1.0f/dmr2;
+                    scale = NK_MIN(100.0f, scale);
+                    dm = nk_vec2_muls(dm, scale);
+                }
+
+                dm_out = nk_vec2_muls(dm, ((half_inner_thickness) + AA_SIZE));
+                dm_in = nk_vec2_muls(dm, half_inner_thickness);
+                temp[i2*4+0] = nk_vec2_add(points[i2], dm_out);
+                temp[i2*4+1] = nk_vec2_add(points[i2], dm_in);
+                temp[i2*4+2] = nk_vec2_sub(points[i2], dm_in);
+                temp[i2*4+3] = nk_vec2_sub(points[i2], dm_out);
+
+                /* add indexes */
+                ids[0] = (nk_draw_index)(idx2 + 1); ids[1] = (nk_draw_index)(idx1+1);
+                ids[2] = (nk_draw_index)(idx1 + 2); ids[3] = (nk_draw_index)(idx1+2);
+                ids[4] = (nk_draw_index)(idx2 + 2); ids[5] = (nk_draw_index)(idx2+1);
+                ids[6] = (nk_draw_index)(idx2 + 1); ids[7] = (nk_draw_index)(idx1+1);
+                ids[8] = (nk_draw_index)(idx1 + 0); ids[9] = (nk_draw_index)(idx1+0);
+                ids[10]= (nk_draw_index)(idx2 + 0); ids[11] = (nk_draw_index)(idx2+1);
+                ids[12]= (nk_draw_index)(idx2 + 2); ids[13] = (nk_draw_index)(idx1+2);
+                ids[14]= (nk_draw_index)(idx1 + 3); ids[15] = (nk_draw_index)(idx1+3);
+                ids[16]= (nk_draw_index)(idx2 + 3); ids[17] = (nk_draw_index)(idx2+2);
+                ids += 18;
+                idx1 = idx2;
+            }
+
+            /* add vertices */
+            for (i = 0; i < points_count; ++i) {
+                const struct nk_vec2 uv = list->config.null.uv;
+                vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+0], uv, col_trans);
+                vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+1], uv, col);
+                vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+2], uv, col);
+                vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+3], uv, col_trans);
+            }
+        }
+        /* free temporary normals + points */
+        nk_buffer_reset(list->vertices, NK_BUFFER_FRONT);
+    } else {
+        /* NON ANTI-ALIASED STROKE */
+        nk_size i1 = 0;
+        nk_size idx = list->vertex_count;
+        const nk_size idx_count = count * 6;
+        const nk_size vtx_count = count * 4;
+        void *vtx = nk_draw_list_alloc_vertices(list, vtx_count);
+        nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count);
+        if (!vtx || !ids) return;
+
+        for (i1 = 0; i1 < count; ++i1) {
+            float dx, dy;
+            const struct nk_vec2 uv = list->config.null.uv;
+            const nk_size i2 = ((i1+1) == points_count) ? 0 : i1 + 1;
+            const struct nk_vec2 p1 = points[i1];
+            const struct nk_vec2 p2 = points[i2];
+            struct nk_vec2 diff = nk_vec2_sub(p2, p1);
+            float len;
+
+            /* vec2 inverted length  */
+            len = nk_vec2_len_sqr(diff);
+            if (len != 0.0f)
+                len = nk_inv_sqrt(len);
+            else len = 1.0f;
+            diff = nk_vec2_muls(diff, len);
+
+            /* add vertices */
+            dx = diff.x * (thickness * 0.5f);
+            dy = diff.y * (thickness * 0.5f);
+
+            vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p1.x + dy, p1.y - dx), uv, col);
+            vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p2.x + dy, p2.y - dx), uv, col);
+            vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p2.x - dy, p2.y + dx), uv, col);
+            vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p1.x - dy, p1.y + dx), uv, col);
+
+            ids[0] = (nk_draw_index)(idx+0); ids[1] = (nk_draw_index)(idx+1);
+            ids[2] = (nk_draw_index)(idx+2); ids[3] = (nk_draw_index)(idx+0);
+            ids[4] = (nk_draw_index)(idx+2); ids[5] = (nk_draw_index)(idx+3);
+
+            ids += 6;
+            idx += 4;
+        }
+    }
+}
+
+NK_API void
+nk_draw_list_fill_poly_convex(struct nk_draw_list *list,
+    const struct nk_vec2 *points, const unsigned int points_count,
+    struct nk_color color, enum nk_anti_aliasing aliasing)
+{
+    struct nk_colorf col;
+    struct nk_colorf col_trans;
+
+    NK_STORAGE const nk_size pnt_align = NK_ALIGNOF(struct nk_vec2);
+    NK_STORAGE const nk_size pnt_size = sizeof(struct nk_vec2);
+    NK_ASSERT(list);
+    if (!list || points_count < 3) return;
+
+#ifdef NK_INCLUDE_COMMAND_USERDATA
+    nk_draw_list_push_userdata(list, list->userdata);
+#endif
+
+    color.a = (nk_byte)((float)color.a * list->config.global_alpha);
+    nk_color_fv(&col.r, color);
+    col_trans = col;
+    col_trans.a = 0;
+
+    if (aliasing == NK_ANTI_ALIASING_ON) {
+        nk_size i = 0;
+        nk_size i0 = 0;
+        nk_size i1 = 0;
+
+        const float AA_SIZE = 1.0f;
+        nk_size vertex_offset = 0;
+        nk_size index = list->vertex_count;
+
+        const nk_size idx_count = (points_count-2)*3 + points_count*6;
+        const nk_size vtx_count = (points_count*2);
+
+        void *vtx = nk_draw_list_alloc_vertices(list, vtx_count);
+        nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count);
+
+        nk_size size = 0;
+        struct nk_vec2 *normals = 0;
+        unsigned int vtx_inner_idx = (unsigned int)(index + 0);
+        unsigned int vtx_outer_idx = (unsigned int)(index + 1);
+        if (!vtx || !ids) return;
+
+        /* temporary allocate normals */
+        vertex_offset = (nk_size)((nk_byte*)vtx - (nk_byte*)list->vertices->memory.ptr);
+        nk_buffer_mark(list->vertices, NK_BUFFER_FRONT);
+        size = pnt_size * points_count;
+        normals = (struct nk_vec2*) nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, size, pnt_align);
+        NK_ASSERT(normals);
+        if (!normals) return;
+        vtx = (void*)((nk_byte*)list->vertices->memory.ptr + vertex_offset);
+
+        /* add elements */
+        for (i = 2; i < points_count; i++) {
+            ids[0] = (nk_draw_index)(vtx_inner_idx);
+            ids[1] = (nk_draw_index)(vtx_inner_idx + ((i-1) << 1));
+            ids[2] = (nk_draw_index)(vtx_inner_idx + (i << 1));
+            ids += 3;
+        }
+
+        /* compute normals */
+        for (i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) {
+            struct nk_vec2 p0 = points[i0];
+            struct nk_vec2 p1 = points[i1];
+            struct nk_vec2 diff = nk_vec2_sub(p1, p0);
+
+            /* vec2 inverted length  */
+            float len = nk_vec2_len_sqr(diff);
+            if (len != 0.0f)
+                len = nk_inv_sqrt(len);
+            else len = 1.0f;
+            diff = nk_vec2_muls(diff, len);
+
+            normals[i0].x = diff.y;
+            normals[i0].y = -diff.x;
+        }
+
+        /* add vertices + indexes */
+        for (i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) {
+            const struct nk_vec2 uv = list->config.null.uv;
+            struct nk_vec2 n0 = normals[i0];
+            struct nk_vec2 n1 = normals[i1];
+            struct nk_vec2 dm = nk_vec2_muls(nk_vec2_add(n0, n1), 0.5f);
+            float dmr2 = dm.x*dm.x + dm.y*dm.y;
+            if (dmr2 > 0.000001f) {
+                float scale = 1.0f / dmr2;
+                scale = NK_MIN(scale, 100.0f);
+                dm = nk_vec2_muls(dm, scale);
+            }
+            dm = nk_vec2_muls(dm, AA_SIZE * 0.5f);
+
+            /* add vertices */
+            vtx = nk_draw_vertex(vtx, &list->config, nk_vec2_sub(points[i1], dm), uv, col);
+            vtx = nk_draw_vertex(vtx, &list->config, nk_vec2_add(points[i1], dm), uv, col_trans);
+
+            /* add indexes */
+            ids[0] = (nk_draw_index)(vtx_inner_idx+(i1<<1));
+            ids[1] = (nk_draw_index)(vtx_inner_idx+(i0<<1));
+            ids[2] = (nk_draw_index)(vtx_outer_idx+(i0<<1));
+            ids[3] = (nk_draw_index)(vtx_outer_idx+(i0<<1));
+            ids[4] = (nk_draw_index)(vtx_outer_idx+(i1<<1));
+            ids[5] = (nk_draw_index)(vtx_inner_idx+(i1<<1));
+            ids += 6;
+        }
+        /* free temporary normals + points */
+        nk_buffer_reset(list->vertices, NK_BUFFER_FRONT);
+    } else {
+        nk_size i = 0;
+        nk_size index = list->vertex_count;
+        const nk_size idx_count = (points_count-2)*3;
+        const nk_size vtx_count = points_count;
+        void *vtx = nk_draw_list_alloc_vertices(list, vtx_count);
+        nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count);
+
+        if (!vtx || !ids) return;
+        for (i = 0; i < vtx_count; ++i)
+            vtx = nk_draw_vertex(vtx, &list->config, points[i], list->config.null.uv, col);
+        for (i = 2; i < points_count; ++i) {
+            ids[0] = (nk_draw_index)index;
+            ids[1] = (nk_draw_index)(index+ i - 1);
+            ids[2] = (nk_draw_index)(index+i);
+            ids += 3;
+        }
+    }
+}
+
+NK_API void
+nk_draw_list_path_clear(struct nk_draw_list *list)
+{
+    NK_ASSERT(list);
+    if (!list) return;
+    nk_buffer_reset(list->buffer, NK_BUFFER_FRONT);
+    list->path_count = 0;
+    list->path_offset = 0;
+}
+
+NK_API void
+nk_draw_list_path_line_to(struct nk_draw_list *list, struct nk_vec2 pos)
+{
+    struct nk_vec2 *points = 0;
+    struct nk_draw_command *cmd = 0;
+    NK_ASSERT(list);
+    if (!list) return;
+    if (!list->cmd_count)
+        nk_draw_list_add_clip(list, nk_null_rect);
+
+    cmd = nk_draw_list_command_last(list);
+    if (cmd && cmd->texture.ptr != list->config.null.texture.ptr)
+        nk_draw_list_push_image(list, list->config.null.texture);
+
+    points = nk_draw_list_alloc_path(list, 1);
+    if (!points) return;
+    points[0] = pos;
+}
+
+NK_API void
+nk_draw_list_path_arc_to_fast(struct nk_draw_list *list, struct nk_vec2 center,
+    float radius, int a_min, int a_max)
+{
+    int a = 0;
+    NK_ASSERT(list);
+    if (!list) return;
+    if (a_min <= a_max) {
+        for (a = a_min; a <= a_max; a++) {
+            const struct nk_vec2 c = list->circle_vtx[(nk_size)a % NK_LEN(list->circle_vtx)];
+            const float x = center.x + c.x * radius;
+            const float y = center.y + c.y * radius;
+            nk_draw_list_path_line_to(list, nk_vec2(x, y));
+        }
+    }
+}
+
+NK_API void
+nk_draw_list_path_arc_to(struct nk_draw_list *list, struct nk_vec2 center,
+    float radius, float a_min, float a_max, unsigned int segments)
+{
+    unsigned int i = 0;
+    NK_ASSERT(list);
+    if (!list) return;
+    if (radius == 0.0f) return;
+
+    /*  This algorithm for arc drawing relies on these two trigonometric identities[1]:
+            sin(a + b) = sin(a) * cos(b) + cos(a) * sin(b)
+            cos(a + b) = cos(a) * cos(b) - sin(a) * sin(b)
+
+        Two coordinates (x, y) of a point on a circle centered on
+        the origin can be written in polar form as:
+            x = r * cos(a)
+            y = r * sin(a)
+        where r is the radius of the circle,
+            a is the angle between (x, y) and the origin.
+
+        This allows us to rotate the coordinates around the
+        origin by an angle b using the following transformation:
+            x' = r * cos(a + b) = x * cos(b) - y * sin(b)
+            y' = r * sin(a + b) = y * cos(b) + x * sin(b)
+
+        [1] https://en.wikipedia.org/wiki/List_of_trigonometric_identities#Angle_sum_and_difference_identities
+    */
+    const float d_angle = (a_max - a_min) / (float)segments;
+    const float sin_d = (float)NK_SIN(d_angle);
+    const float cos_d = (float)NK_COS(d_angle);
+
+    float cx = (float)NK_COS(a_min) * radius;
+    float cy = (float)NK_SIN(a_min) * radius;
+    for(i = 0; i <= segments; ++i) {
+        const float x = center.x + cx;
+        const float y = center.y + cy;
+        nk_draw_list_path_line_to(list, nk_vec2(x, y));
+
+        const float new_cx = cx * cos_d - cy * sin_d;
+        const float new_cy = cy * cos_d + cx * sin_d;
+        cx = new_cx;
+        cy = new_cy;
+    }
+}
+
+NK_API void
+nk_draw_list_path_rect_to(struct nk_draw_list *list, struct nk_vec2 a,
+    struct nk_vec2 b, float rounding)
+{
+    float r;
+    NK_ASSERT(list);
+    if (!list) return;
+    r = rounding;
+    r = NK_MIN(r, ((b.x-a.x) < 0) ? -(b.x-a.x): (b.x-a.x));
+    r = NK_MIN(r, ((b.y-a.y) < 0) ? -(b.y-a.y): (b.y-a.y));
+
+    if (r == 0.0f) {
+        nk_draw_list_path_line_to(list, a);
+        nk_draw_list_path_line_to(list, nk_vec2(b.x,a.y));
+        nk_draw_list_path_line_to(list, b);
+        nk_draw_list_path_line_to(list, nk_vec2(a.x,b.y));
+    } else {
+        nk_draw_list_path_arc_to_fast(list, nk_vec2(a.x + r, a.y + r), r, 6, 9);
+        nk_draw_list_path_arc_to_fast(list, nk_vec2(b.x - r, a.y + r), r, 9, 12);
+        nk_draw_list_path_arc_to_fast(list, nk_vec2(b.x - r, b.y - r), r, 0, 3);
+        nk_draw_list_path_arc_to_fast(list, nk_vec2(a.x + r, b.y - r), r, 3, 6);
+    }
+}
+
+NK_API void
+nk_draw_list_path_curve_to(struct nk_draw_list *list, struct nk_vec2 p2,
+    struct nk_vec2 p3, struct nk_vec2 p4, unsigned int num_segments)
+{
+    float t_step;
+    unsigned int i_step;
+    struct nk_vec2 p1;
+
+    NK_ASSERT(list);
+    NK_ASSERT(list->path_count);
+    if (!list || !list->path_count) return;
+    num_segments = NK_MAX(num_segments, 1);
+
+    p1 = nk_draw_list_path_last(list);
+    t_step = 1.0f/(float)num_segments;
+    for (i_step = 1; i_step <= num_segments; ++i_step) {
+        float t = t_step * (float)i_step;
+        float u = 1.0f - t;
+        float w1 = u*u*u;
+        float w2 = 3*u*u*t;
+        float w3 = 3*u*t*t;
+        float w4 = t * t *t;
+        float x = w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x;
+        float y = w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y;
+        nk_draw_list_path_line_to(list, nk_vec2(x,y));
+    }
+}
+
+NK_API void
+nk_draw_list_path_fill(struct nk_draw_list *list, struct nk_color color)
+{
+    struct nk_vec2 *points;
+    NK_ASSERT(list);
+    if (!list) return;
+    points = (struct nk_vec2*)nk_buffer_memory(list->buffer);
+    nk_draw_list_fill_poly_convex(list, points, list->path_count, color, list->config.shape_AA);
+    nk_draw_list_path_clear(list);
+}
+
+NK_API void
+nk_draw_list_path_stroke(struct nk_draw_list *list, struct nk_color color,
+    enum nk_draw_list_stroke closed, float thickness)
+{
+    struct nk_vec2 *points;
+    NK_ASSERT(list);
+    if (!list) return;
+    points = (struct nk_vec2*)nk_buffer_memory(list->buffer);
+    nk_draw_list_stroke_poly_line(list, points, list->path_count, color,
+        closed, thickness, list->config.line_AA);
+    nk_draw_list_path_clear(list);
+}
+
+NK_API void
+nk_draw_list_stroke_line(struct nk_draw_list *list, struct nk_vec2 a,
+    struct nk_vec2 b, struct nk_color col, float thickness)
+{
+    NK_ASSERT(list);
+    if (!list || !col.a) return;
+    if (list->line_AA == NK_ANTI_ALIASING_ON) {
+        nk_draw_list_path_line_to(list, a);
+        nk_draw_list_path_line_to(list, b);
+    } else {
+        nk_draw_list_path_line_to(list, nk_vec2_sub(a,nk_vec2(0.5f,0.5f)));
+        nk_draw_list_path_line_to(list, nk_vec2_sub(b,nk_vec2(0.5f,0.5f)));
+    }
+    nk_draw_list_path_stroke(list,  col, NK_STROKE_OPEN, thickness);
+}
+
+NK_API void
+nk_draw_list_fill_rect(struct nk_draw_list *list, struct nk_rect rect,
+    struct nk_color col, float rounding)
+{
+    NK_ASSERT(list);
+    if (!list || !col.a) return;
+
+    if (list->line_AA == NK_ANTI_ALIASING_ON) {
+        nk_draw_list_path_rect_to(list, nk_vec2(rect.x, rect.y),
+            nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding);
+    } else {
+        nk_draw_list_path_rect_to(list, nk_vec2(rect.x-0.5f, rect.y-0.5f),
+            nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding);
+    } nk_draw_list_path_fill(list,  col);
+}
+
+NK_API void
+nk_draw_list_stroke_rect(struct nk_draw_list *list, struct nk_rect rect,
+    struct nk_color col, float rounding, float thickness)
+{
+    NK_ASSERT(list);
+    if (!list || !col.a) return;
+    if (list->line_AA == NK_ANTI_ALIASING_ON) {
+        nk_draw_list_path_rect_to(list, nk_vec2(rect.x, rect.y),
+            nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding);
+    } else {
+        nk_draw_list_path_rect_to(list, nk_vec2(rect.x-0.5f, rect.y-0.5f),
+            nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding);
+    } nk_draw_list_path_stroke(list,  col, NK_STROKE_CLOSED, thickness);
+}
+
+NK_API void
+nk_draw_list_fill_rect_multi_color(struct nk_draw_list *list, struct nk_rect rect,
+    struct nk_color left, struct nk_color top, struct nk_color right,
+    struct nk_color bottom)
+{
+    void *vtx;
+    struct nk_colorf col_left, col_top;
+    struct nk_colorf col_right, col_bottom;
+    nk_draw_index *idx;
+    nk_draw_index index;
+
+    nk_color_fv(&col_left.r, left);
+    nk_color_fv(&col_right.r, right);
+    nk_color_fv(&col_top.r, top);
+    nk_color_fv(&col_bottom.r, bottom);
+
+    NK_ASSERT(list);
+    if (!list) return;
+
+    nk_draw_list_push_image(list, list->config.null.texture);
+    index = (nk_draw_index)list->vertex_count;
+    vtx = nk_draw_list_alloc_vertices(list, 4);
+    idx = nk_draw_list_alloc_elements(list, 6);
+    if (!vtx || !idx) return;
+
+    idx[0] = (nk_draw_index)(index+0); idx[1] = (nk_draw_index)(index+1);
+    idx[2] = (nk_draw_index)(index+2); idx[3] = (nk_draw_index)(index+0);
+    idx[4] = (nk_draw_index)(index+2); idx[5] = (nk_draw_index)(index+3);
+
+    vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x, rect.y), list->config.null.uv, col_left);
+    vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x + rect.w, rect.y), list->config.null.uv, col_top);
+    vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x + rect.w, rect.y + rect.h), list->config.null.uv, col_right);
+    vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x, rect.y + rect.h), list->config.null.uv, col_bottom);
+}
+
+NK_API void
+nk_draw_list_fill_triangle(struct nk_draw_list *list, struct nk_vec2 a,
+    struct nk_vec2 b, struct nk_vec2 c, struct nk_color col)
+{
+    NK_ASSERT(list);
+    if (!list || !col.a) return;
+    nk_draw_list_path_line_to(list, a);
+    nk_draw_list_path_line_to(list, b);
+    nk_draw_list_path_line_to(list, c);
+    nk_draw_list_path_fill(list, col);
+}
+
+NK_API void
+nk_draw_list_stroke_triangle(struct nk_draw_list *list, struct nk_vec2 a,
+    struct nk_vec2 b, struct nk_vec2 c, struct nk_color col, float thickness)
+{
+    NK_ASSERT(list);
+    if (!list || !col.a) return;
+    nk_draw_list_path_line_to(list, a);
+    nk_draw_list_path_line_to(list, b);
+    nk_draw_list_path_line_to(list, c);
+    nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness);
+}
+
+NK_API void
+nk_draw_list_fill_circle(struct nk_draw_list *list, struct nk_vec2 center,
+    float radius, struct nk_color col, unsigned int segs)
+{
+    float a_max;
+    NK_ASSERT(list);
+    if (!list || !col.a) return;
+    a_max = NK_PI * 2.0f * ((float)segs - 1.0f) / (float)segs;
+    nk_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs);
+    nk_draw_list_path_fill(list, col);
+}
+
+NK_API void
+nk_draw_list_stroke_circle(struct nk_draw_list *list, struct nk_vec2 center,
+    float radius, struct nk_color col, unsigned int segs, float thickness)
+{
+    float a_max;
+    NK_ASSERT(list);
+    if (!list || !col.a) return;
+    a_max = NK_PI * 2.0f * ((float)segs - 1.0f) / (float)segs;
+    nk_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs);
+    nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness);
+}
+
+NK_API void
+nk_draw_list_stroke_curve(struct nk_draw_list *list, struct nk_vec2 p0,
+    struct nk_vec2 cp0, struct nk_vec2 cp1, struct nk_vec2 p1,
+    struct nk_color col, unsigned int segments, float thickness)
+{
+    NK_ASSERT(list);
+    if (!list || !col.a) return;
+    nk_draw_list_path_line_to(list, p0);
+    nk_draw_list_path_curve_to(list, cp0, cp1, p1, segments);
+    nk_draw_list_path_stroke(list, col, NK_STROKE_OPEN, thickness);
+}
+
+NK_INTERN void
+nk_draw_list_push_rect_uv(struct nk_draw_list *list, struct nk_vec2 a,
+    struct nk_vec2 c, struct nk_vec2 uva, struct nk_vec2 uvc,
+    struct nk_color color)
+{
+    void *vtx;
+    struct nk_vec2 uvb;
+    struct nk_vec2 uvd;
+    struct nk_vec2 b;
+    struct nk_vec2 d;
+
+    struct nk_colorf col;
+    nk_draw_index *idx;
+    nk_draw_index index;
+    NK_ASSERT(list);
+    if (!list) return;
+
+    nk_color_fv(&col.r, color);
+    uvb = nk_vec2(uvc.x, uva.y);
+    uvd = nk_vec2(uva.x, uvc.y);
+    b = nk_vec2(c.x, a.y);
+    d = nk_vec2(a.x, c.y);
+
+    index = (nk_draw_index)list->vertex_count;
+    vtx = nk_draw_list_alloc_vertices(list, 4);
+    idx = nk_draw_list_alloc_elements(list, 6);
+    if (!vtx || !idx) return;
+
+    idx[0] = (nk_draw_index)(index+0); idx[1] = (nk_draw_index)(index+1);
+    idx[2] = (nk_draw_index)(index+2); idx[3] = (nk_draw_index)(index+0);
+    idx[4] = (nk_draw_index)(index+2); idx[5] = (nk_draw_index)(index+3);
+
+    vtx = nk_draw_vertex(vtx, &list->config, a, uva, col);
+    vtx = nk_draw_vertex(vtx, &list->config, b, uvb, col);
+    vtx = nk_draw_vertex(vtx, &list->config, c, uvc, col);
+    vtx = nk_draw_vertex(vtx, &list->config, d, uvd, col);
+}
+
+NK_API void
+nk_draw_list_add_image(struct nk_draw_list *list, struct nk_image texture,
+    struct nk_rect rect, struct nk_color color)
+{
+    NK_ASSERT(list);
+    if (!list) return;
+    /* push new command with given texture */
+    nk_draw_list_push_image(list, texture.handle);
+    if (nk_image_is_subimage(&texture)) {
+        /* add region inside of the texture  */
+        struct nk_vec2 uv[2];
+        uv[0].x = (float)texture.region[0]/(float)texture.w;
+        uv[0].y = (float)texture.region[1]/(float)texture.h;
+        uv[1].x = (float)(texture.region[0] + texture.region[2])/(float)texture.w;
+        uv[1].y = (float)(texture.region[1] + texture.region[3])/(float)texture.h;
+        nk_draw_list_push_rect_uv(list, nk_vec2(rect.x, rect.y),
+            nk_vec2(rect.x + rect.w, rect.y + rect.h),  uv[0], uv[1], color);
+    } else nk_draw_list_push_rect_uv(list, nk_vec2(rect.x, rect.y),
+            nk_vec2(rect.x + rect.w, rect.y + rect.h),
+            nk_vec2(0.0f, 0.0f), nk_vec2(1.0f, 1.0f),color);
+}
+
+NK_API void
+nk_draw_list_add_text(struct nk_draw_list *list, const struct nk_user_font *font,
+    struct nk_rect rect, const char *text, int len, float font_height,
+    struct nk_color fg)
+{
+    float x = 0;
+    int text_len = 0;
+    nk_rune unicode = 0;
+    nk_rune next = 0;
+    int glyph_len = 0;
+    int next_glyph_len = 0;
+    struct nk_user_font_glyph g;
+
+    NK_ASSERT(list);
+    if (!list || !len || !text) return;
+    if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h,
+        list->clip_rect.x, list->clip_rect.y, list->clip_rect.w, list->clip_rect.h)) return;
+
+    nk_draw_list_push_image(list, font->texture);
+    x = rect.x;
+    glyph_len = nk_utf_decode(text, &unicode, len);
+    if (!glyph_len) return;
+
+    /* draw every glyph image */
+    fg.a = (nk_byte)((float)fg.a * list->config.global_alpha);
+    while (text_len < len && glyph_len) {
+        float gx, gy, gh, gw;
+        float char_width = 0;
+        if (unicode == NK_UTF_INVALID) break;
+
+        /* query currently drawn glyph information */
+        next_glyph_len = nk_utf_decode(text + text_len + glyph_len, &next, (int)len - text_len);
+        font->query(font->userdata, font_height, &g, unicode,
+                    (next == NK_UTF_INVALID) ? '\0' : next);
+
+        /* calculate and draw glyph drawing rectangle and image */
+        gx = x + g.offset.x;
+        gy = rect.y + g.offset.y;
+        gw = g.width; gh = g.height;
+        char_width = g.xadvance;
+        nk_draw_list_push_rect_uv(list, nk_vec2(gx,gy), nk_vec2(gx + gw, gy+ gh),
+            g.uv[0], g.uv[1], fg);
+
+        /* offset next glyph */
+        text_len += glyph_len;
+        x += char_width;
+        glyph_len = next_glyph_len;
+        unicode = next;
+    }
+}
+
+NK_API 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 *config)
+{
+    nk_flags res = NK_CONVERT_SUCCESS;
+    const struct nk_command *cmd;
+    NK_ASSERT(ctx);
+    NK_ASSERT(cmds);
+    NK_ASSERT(vertices);
+    NK_ASSERT(elements);
+    NK_ASSERT(config);
+    NK_ASSERT(config->vertex_layout);
+    NK_ASSERT(config->vertex_size);
+    if (!ctx || !cmds || !vertices || !elements || !config || !config->vertex_layout)
+        return NK_CONVERT_INVALID_PARAM;
+
+    nk_draw_list_setup(&ctx->draw_list, config, cmds, vertices, elements,
+        config->line_AA, config->shape_AA);
+    nk_foreach(cmd, ctx)
+    {
+#ifdef NK_INCLUDE_COMMAND_USERDATA
+        ctx->draw_list.userdata = cmd->userdata;
+#endif
+        switch (cmd->type) {
+        case NK_COMMAND_NOP: break;
+        case NK_COMMAND_SCISSOR: {
+            const struct nk_command_scissor *s = (const struct nk_command_scissor*)cmd;
+            nk_draw_list_add_clip(&ctx->draw_list, nk_rect(s->x, s->y, s->w, s->h));
+        } break;
+        case NK_COMMAND_LINE: {
+            const struct nk_command_line *l = (const struct nk_command_line*)cmd;
+            nk_draw_list_stroke_line(&ctx->draw_list, nk_vec2(l->begin.x, l->begin.y),
+                nk_vec2(l->end.x, l->end.y), l->color, l->line_thickness);
+        } break;
+        case NK_COMMAND_CURVE: {
+            const struct nk_command_curve *q = (const struct nk_command_curve*)cmd;
+            nk_draw_list_stroke_curve(&ctx->draw_list, nk_vec2(q->begin.x, q->begin.y),
+                nk_vec2(q->ctrl[0].x, q->ctrl[0].y), nk_vec2(q->ctrl[1].x,
+                q->ctrl[1].y), nk_vec2(q->end.x, q->end.y), q->color,
+                config->curve_segment_count, q->line_thickness);
+        } break;
+        case NK_COMMAND_RECT: {
+            const struct nk_command_rect *r = (const struct nk_command_rect*)cmd;
+            nk_draw_list_stroke_rect(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h),
+                r->color, (float)r->rounding, r->line_thickness);
+        } break;
+        case NK_COMMAND_RECT_FILLED: {
+            const struct nk_command_rect_filled *r = (const struct nk_command_rect_filled*)cmd;
+            nk_draw_list_fill_rect(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h),
+                r->color, (float)r->rounding);
+        } break;
+        case NK_COMMAND_RECT_MULTI_COLOR: {
+            const struct nk_command_rect_multi_color *r = (const struct nk_command_rect_multi_color*)cmd;
+            nk_draw_list_fill_rect_multi_color(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h),
+                r->left, r->top, r->right, r->bottom);
+        } break;
+        case NK_COMMAND_CIRCLE: {
+            const struct nk_command_circle *c = (const struct nk_command_circle*)cmd;
+            nk_draw_list_stroke_circle(&ctx->draw_list, nk_vec2((float)c->x + (float)c->w/2,
+                (float)c->y + (float)c->h/2), (float)c->w/2, c->color,
+                config->circle_segment_count, c->line_thickness);
+        } break;
+        case NK_COMMAND_CIRCLE_FILLED: {
+            const struct nk_command_circle_filled *c = (const struct nk_command_circle_filled *)cmd;
+            nk_draw_list_fill_circle(&ctx->draw_list, nk_vec2((float)c->x + (float)c->w/2,
+                (float)c->y + (float)c->h/2), (float)c->w/2, c->color,
+                config->circle_segment_count);
+        } break;
+        case NK_COMMAND_ARC: {
+            const struct nk_command_arc *c = (const struct nk_command_arc*)cmd;
+            nk_draw_list_path_line_to(&ctx->draw_list, nk_vec2(c->cx, c->cy));
+            nk_draw_list_path_arc_to(&ctx->draw_list, nk_vec2(c->cx, c->cy), c->r,
+                c->a[0], c->a[1], config->arc_segment_count);
+            nk_draw_list_path_stroke(&ctx->draw_list, c->color, NK_STROKE_CLOSED, c->line_thickness);
+        } break;
+        case NK_COMMAND_ARC_FILLED: {
+            const struct nk_command_arc_filled *c = (const struct nk_command_arc_filled*)cmd;
+            nk_draw_list_path_line_to(&ctx->draw_list, nk_vec2(c->cx, c->cy));
+            nk_draw_list_path_arc_to(&ctx->draw_list, nk_vec2(c->cx, c->cy), c->r,
+                c->a[0], c->a[1], config->arc_segment_count);
+            nk_draw_list_path_fill(&ctx->draw_list, c->color);
+        } break;
+        case NK_COMMAND_TRIANGLE: {
+            const struct nk_command_triangle *t = (const struct nk_command_triangle*)cmd;
+            nk_draw_list_stroke_triangle(&ctx->draw_list, nk_vec2(t->a.x, t->a.y),
+                nk_vec2(t->b.x, t->b.y), nk_vec2(t->c.x, t->c.y), t->color,
+                t->line_thickness);
+        } break;
+        case NK_COMMAND_TRIANGLE_FILLED: {
+            const struct nk_command_triangle_filled *t = (const struct nk_command_triangle_filled*)cmd;
+            nk_draw_list_fill_triangle(&ctx->draw_list, nk_vec2(t->a.x, t->a.y),
+                nk_vec2(t->b.x, t->b.y), nk_vec2(t->c.x, t->c.y), t->color);
+        } break;
+        case NK_COMMAND_POLYGON: {
+            int i;
+            const struct nk_command_polygon*p = (const struct nk_command_polygon*)cmd;
+            for (i = 0; i < p->point_count; ++i) {
+                struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y);
+                nk_draw_list_path_line_to(&ctx->draw_list, pnt);
+            }
+            nk_draw_list_path_stroke(&ctx->draw_list, p->color, NK_STROKE_CLOSED, p->line_thickness);
+        } break;
+        case NK_COMMAND_POLYGON_FILLED: {
+            int i;
+            const struct nk_command_polygon_filled *p = (const struct nk_command_polygon_filled*)cmd;
+            for (i = 0; i < p->point_count; ++i) {
+                struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y);
+                nk_draw_list_path_line_to(&ctx->draw_list, pnt);
+            }
+            nk_draw_list_path_fill(&ctx->draw_list, p->color);
+        } break;
+        case NK_COMMAND_POLYLINE: {
+            int i;
+            const struct nk_command_polyline *p = (const struct nk_command_polyline*)cmd;
+            for (i = 0; i < p->point_count; ++i) {
+                struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y);
+                nk_draw_list_path_line_to(&ctx->draw_list, pnt);
+            }
+            nk_draw_list_path_stroke(&ctx->draw_list, p->color, NK_STROKE_OPEN, p->line_thickness);
+        } break;
+        case NK_COMMAND_TEXT: {
+            const struct nk_command_text *t = (const struct nk_command_text*)cmd;
+            nk_draw_list_add_text(&ctx->draw_list, t->font, nk_rect(t->x, t->y, t->w, t->h),
+                t->string, t->length, t->height, t->foreground);
+        } break;
+        case NK_COMMAND_IMAGE: {
+            const struct nk_command_image *i = (const struct nk_command_image*)cmd;
+            nk_draw_list_add_image(&ctx->draw_list, i->img, nk_rect(i->x, i->y, i->w, i->h), i->col);
+        } break;
+        case NK_COMMAND_CUSTOM: {
+            const struct nk_command_custom *c = (const struct nk_command_custom*)cmd;
+            c->callback(&ctx->draw_list, c->x, c->y, c->w, c->h, c->callback_data);
+        } break;
+        default: break;
+        }
+    }
+    res |= (cmds->needed > cmds->allocated + (cmds->memory.size - cmds->size)) ? NK_CONVERT_COMMAND_BUFFER_FULL: 0;
+    res |= (vertices->needed > vertices->allocated) ? NK_CONVERT_VERTEX_BUFFER_FULL: 0;
+    res |= (elements->needed > elements->allocated) ? NK_CONVERT_ELEMENT_BUFFER_FULL: 0;
+    return res;
+}
+NK_API const struct nk_draw_command*
+nk__draw_begin(const struct nk_context *ctx,
+    const struct nk_buffer *buffer)
+{return nk__draw_list_begin(&ctx->draw_list, buffer);}
+
+NK_API const struct nk_draw_command*
+nk__draw_end(const struct nk_context *ctx, const struct nk_buffer *buffer)
+{return nk__draw_list_end(&ctx->draw_list, buffer);}
+
+NK_API const struct nk_draw_command*
+nk__draw_next(const struct nk_draw_command *cmd,
+    const struct nk_buffer *buffer, const struct nk_context *ctx)
+{return nk__draw_list_next(cmd, buffer, &ctx->draw_list);}
+
+#endif
+
+/*
+ * ==============================================================
+ *
+ *                          FONT HANDLING
+ *
+ * ===============================================================
+ */
+#ifdef NK_INCLUDE_FONT_BAKING
+/* -------------------------------------------------------------
+ *
+ *                          RECT PACK
+ *
+ * --------------------------------------------------------------*/
+/* stb_rect_pack.h - v0.05 - public domain - rectangle packing */
+/* Sean Barrett 2014 */
+#define NK_RP__MAXVAL  0xffff
+typedef unsigned short nk_rp_coord;
+
+struct nk_rp_rect {
+    /* reserved for your use: */
+    int id;
+    /* input: */
+    nk_rp_coord w, h;
+    /* output: */
+    nk_rp_coord x, y;
+    int was_packed;
+    /* non-zero if valid packing */
+}; /* 16 bytes, nominally */
+
+struct nk_rp_node {
+    nk_rp_coord  x,y;
+    struct nk_rp_node  *next;
+};
+
+struct nk_rp_context {
+    int width;
+    int height;
+    int align;
+    int init_mode;
+    int heuristic;
+    int num_nodes;
+    struct nk_rp_node *active_head;
+    struct nk_rp_node *free_head;
+    struct nk_rp_node extra[2];
+    /* we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' */
+};
+
+struct nk_rp__findresult {
+    int x,y;
+    struct nk_rp_node **prev_link;
+};
+
+enum NK_RP_HEURISTIC {
+    NK_RP_HEURISTIC_Skyline_default=0,
+    NK_RP_HEURISTIC_Skyline_BL_sortHeight = NK_RP_HEURISTIC_Skyline_default,
+    NK_RP_HEURISTIC_Skyline_BF_sortHeight
+};
+enum NK_RP_INIT_STATE{NK_RP__INIT_skyline = 1};
+
+NK_INTERN void
+nk_rp_setup_allow_out_of_mem(struct nk_rp_context *context, int allow_out_of_mem)
+{
+    if (allow_out_of_mem)
+        /* if it's ok to run out of memory, then don't bother aligning them; */
+        /* this gives better packing, but may fail due to OOM (even though */
+        /* the rectangles easily fit). @TODO a smarter approach would be to only */
+        /* quantize once we've hit OOM, then we could get rid of this parameter. */
+        context->align = 1;
+    else {
+        /* if it's not ok to run out of memory, then quantize the widths */
+        /* so that num_nodes is always enough nodes. */
+        /* */
+        /* I.e. num_nodes * align >= width */
+        /*                  align >= width / num_nodes */
+        /*                  align = ceil(width/num_nodes) */
+        context->align = (context->width + context->num_nodes-1) / context->num_nodes;
+    }
+}
+
+NK_INTERN void
+nk_rp_init_target(struct nk_rp_context *context, int width, int height,
+    struct nk_rp_node *nodes, int num_nodes)
+{
+    int i;
+#ifndef STBRP_LARGE_RECTS
+    NK_ASSERT(width <= 0xffff && height <= 0xffff);
+#endif
+
+    for (i=0; i < num_nodes-1; ++i)
+        nodes[i].next = &nodes[i+1];
+    nodes[i].next = 0;
+    context->init_mode = NK_RP__INIT_skyline;
+    context->heuristic = NK_RP_HEURISTIC_Skyline_default;
+    context->free_head = &nodes[0];
+    context->active_head = &context->extra[0];
+    context->width = width;
+    context->height = height;
+    context->num_nodes = num_nodes;
+    nk_rp_setup_allow_out_of_mem(context, 0);
+
+    /* node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) */
+    context->extra[0].x = 0;
+    context->extra[0].y = 0;
+    context->extra[0].next = &context->extra[1];
+    context->extra[1].x = (nk_rp_coord) width;
+    context->extra[1].y = 65535;
+    context->extra[1].next = 0;
+}
+
+/* find minimum y position if it starts at x1 */
+NK_INTERN int
+nk_rp__skyline_find_min_y(struct nk_rp_context *c, struct nk_rp_node *first,
+    int x0, int width, int *pwaste)
+{
+    struct nk_rp_node *node = first;
+    int x1 = x0 + width;
+    int min_y, visited_width, waste_area;
+    NK_ASSERT(first->x <= x0);
+    NK_UNUSED(c);
+
+    NK_ASSERT(node->next->x > x0);
+    /* we ended up handling this in the caller for efficiency */
+    NK_ASSERT(node->x <= x0);
+
+    min_y = 0;
+    waste_area = 0;
+    visited_width = 0;
+    while (node->x < x1)
+    {
+        if (node->y > min_y) {
+            /* raise min_y higher. */
+            /* we've accounted for all waste up to min_y, */
+            /* but we'll now add more waste for everything we've visited */
+            waste_area += visited_width * (node->y - min_y);
+            min_y = node->y;
+            /* the first time through, visited_width might be reduced */
+            if (node->x < x0)
+            visited_width += node->next->x - x0;
+            else
+            visited_width += node->next->x - node->x;
+        } else {
+            /* add waste area */
+            int under_width = node->next->x - node->x;
+            if (under_width + visited_width > width)
+            under_width = width - visited_width;
+            waste_area += under_width * (min_y - node->y);
+            visited_width += under_width;
+        }
+        node = node->next;
+    }
+    *pwaste = waste_area;
+    return min_y;
+}
+
+NK_INTERN struct nk_rp__findresult
+nk_rp__skyline_find_best_pos(struct nk_rp_context *c, int width, int height)
+{
+    int best_waste = (1<<30), best_x, best_y = (1 << 30);
+    struct nk_rp__findresult fr;
+    struct nk_rp_node **prev, *node, *tail, **best = 0;
+
+    /* align to multiple of c->align */
+    width = (width + c->align - 1);
+    width -= width % c->align;
+    NK_ASSERT(width % c->align == 0);
+
+    node = c->active_head;
+    prev = &c->active_head;
+    while (node->x + width <= c->width) {
+        int y,waste;
+        y = nk_rp__skyline_find_min_y(c, node, node->x, width, &waste);
+        /* actually just want to test BL */
+        if (c->heuristic == NK_RP_HEURISTIC_Skyline_BL_sortHeight) {
+            /* bottom left */
+            if (y < best_y) {
+            best_y = y;
+            best = prev;
+            }
+        } else {
+            /* best-fit */
+            if (y + height <= c->height) {
+                /* can only use it if it first vertically */
+                if (y < best_y || (y == best_y && waste < best_waste)) {
+                    best_y = y;
+                    best_waste = waste;
+                    best = prev;
+                }
+            }
+        }
+        prev = &node->next;
+        node = node->next;
+    }
+    best_x = (best == 0) ? 0 : (*best)->x;
+
+    /* if doing best-fit (BF), we also have to try aligning right edge to each node position */
+    /* */
+    /* e.g, if fitting */
+    /* */
+    /*     ____________________ */
+    /*    |____________________| */
+    /* */
+    /*            into */
+    /* */
+    /*   |                         | */
+    /*   |             ____________| */
+    /*   |____________| */
+    /* */
+    /* then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned */
+    /* */
+    /* This makes BF take about 2x the time */
+    if (c->heuristic == NK_RP_HEURISTIC_Skyline_BF_sortHeight)
+    {
+        tail = c->active_head;
+        node = c->active_head;
+        prev = &c->active_head;
+        /* find first node that's admissible */
+        while (tail->x < width)
+            tail = tail->next;
+        while (tail)
+        {
+            int xpos = tail->x - width;
+            int y,waste;
+            NK_ASSERT(xpos >= 0);
+            /* find the left position that matches this */
+            while (node->next->x <= xpos) {
+                prev = &node->next;
+                node = node->next;
+            }
+            NK_ASSERT(node->next->x > xpos && node->x <= xpos);
+            y = nk_rp__skyline_find_min_y(c, node, xpos, width, &waste);
+            if (y + height < c->height) {
+                if (y <= best_y) {
+                    if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
+                        best_x = xpos;
+                        NK_ASSERT(y <= best_y);
+                        best_y = y;
+                        best_waste = waste;
+                        best = prev;
+                    }
+                }
+            }
+            tail = tail->next;
+        }
+    }
+    fr.prev_link = best;
+    fr.x = best_x;
+    fr.y = best_y;
+    return fr;
+}
+
+NK_INTERN struct nk_rp__findresult
+nk_rp__skyline_pack_rectangle(struct nk_rp_context *context, int width, int height)
+{
+    /* find best position according to heuristic */
+    struct nk_rp__findresult res = nk_rp__skyline_find_best_pos(context, width, height);
+    struct nk_rp_node *node, *cur;
+
+    /* bail if: */
+    /*    1. it failed */
+    /*    2. the best node doesn't fit (we don't always check this) */
+    /*    3. we're out of memory */
+    if (res.prev_link == 0 || res.y + height > context->height || context->free_head == 0) {
+        res.prev_link = 0;
+        return res;
+    }
+
+    /* on success, create new node */
+    node = context->free_head;
+    node->x = (nk_rp_coord) res.x;
+    node->y = (nk_rp_coord) (res.y + height);
+
+    context->free_head = node->next;
+
+    /* insert the new node into the right starting point, and */
+    /* let 'cur' point to the remaining nodes needing to be */
+    /* stitched back in */
+    cur = *res.prev_link;
+    if (cur->x < res.x) {
+        /* preserve the existing one, so start testing with the next one */
+        struct nk_rp_node *next = cur->next;
+        cur->next = node;
+        cur = next;
+    } else {
+        *res.prev_link = node;
+    }
+
+    /* from here, traverse cur and free the nodes, until we get to one */
+    /* that shouldn't be freed */
+    while (cur->next && cur->next->x <= res.x + width) {
+        struct nk_rp_node *next = cur->next;
+        /* move the current node to the free list */
+        cur->next = context->free_head;
+        context->free_head = cur;
+        cur = next;
+    }
+    /* stitch the list back in */
+    node->next = cur;
+
+    if (cur->x < res.x + width)
+        cur->x = (nk_rp_coord) (res.x + width);
+    return res;
+}
+
+NK_INTERN int
+nk_rect_height_compare(const void *a, const void *b)
+{
+    const struct nk_rp_rect *p = (const struct nk_rp_rect *) a;
+    const struct nk_rp_rect *q = (const struct nk_rp_rect *) b;
+    if (p->h > q->h)
+        return -1;
+    if (p->h < q->h)
+        return  1;
+    return (p->w > q->w) ? -1 : (p->w < q->w);
+}
+
+NK_INTERN int
+nk_rect_original_order(const void *a, const void *b)
+{
+    const struct nk_rp_rect *p = (const struct nk_rp_rect *) a;
+    const struct nk_rp_rect *q = (const struct nk_rp_rect *) b;
+    return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
+}
+
+NK_INTERN void
+nk_rp_qsort(struct nk_rp_rect *array, unsigned int len, int(*cmp)(const void*,const void*))
+{
+    /* iterative quick sort */
+    #define NK_MAX_SORT_STACK 64
+    unsigned right, left = 0, stack[NK_MAX_SORT_STACK], pos = 0;
+    unsigned seed = len/2 * 69069+1;
+    for (;;) {
+        for (; left+1 < len; len++) {
+            struct nk_rp_rect pivot, tmp;
+            if (pos == NK_MAX_SORT_STACK) len = stack[pos = 0];
+            pivot = array[left+seed%(len-left)];
+            seed = seed * 69069 + 1;
+            stack[pos++] = len;
+            for (right = left-1;;) {
+                while (cmp(&array[++right], &pivot) < 0);
+                while (cmp(&pivot, &array[--len]) < 0);
+                if (right >= len) break;
+                tmp = array[right];
+                array[right] = array[len];
+                array[len] = tmp;
+            }
+        }
+        if (pos == 0) break;
+        left = len;
+        len = stack[--pos];
+    }
+    #undef NK_MAX_SORT_STACK
+}
+
+NK_INTERN void
+nk_rp_pack_rects(struct nk_rp_context *context, struct nk_rp_rect *rects, int num_rects)
+{
+    int i;
+    /* we use the 'was_packed' field internally to allow sorting/unsorting */
+    for (i=0; i < num_rects; ++i) {
+        rects[i].was_packed = i;
+    }
+
+    /* sort according to heuristic */
+    nk_rp_qsort(rects, (unsigned)num_rects, nk_rect_height_compare);
+
+    for (i=0; i < num_rects; ++i) {
+        struct nk_rp__findresult fr = nk_rp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
+        if (fr.prev_link) {
+            rects[i].x = (nk_rp_coord) fr.x;
+            rects[i].y = (nk_rp_coord) fr.y;
+        } else {
+            rects[i].x = rects[i].y = NK_RP__MAXVAL;
+        }
+    }
+
+    /* unsort */
+    nk_rp_qsort(rects, (unsigned)num_rects, nk_rect_original_order);
+
+    /* set was_packed flags */
+    for (i=0; i < num_rects; ++i)
+        rects[i].was_packed = !(rects[i].x == NK_RP__MAXVAL && rects[i].y == NK_RP__MAXVAL);
+}
+
+/*
+ * ==============================================================
+ *
+ *                          TRUETYPE
+ *
+ * ===============================================================
+ */
+/* stb_truetype.h - v1.07 - public domain */
+#define NK_TT_MAX_OVERSAMPLE   8
+#define NK_TT__OVER_MASK  (NK_TT_MAX_OVERSAMPLE-1)
+
+struct nk_tt_bakedchar {
+    unsigned short x0,y0,x1,y1;
+    /* coordinates of bbox in bitmap */
+    float xoff,yoff,xadvance;
+};
+
+struct nk_tt_aligned_quad{
+    float x0,y0,s0,t0; /* top-left */
+    float x1,y1,s1,t1; /* bottom-right */
+};
+
+struct nk_tt_packedchar {
+    unsigned short x0,y0,x1,y1;
+    /* coordinates of bbox in bitmap */
+    float xoff,yoff,xadvance;
+    float xoff2,yoff2;
+};
+
+struct nk_tt_pack_range {
+    float font_size;
+    int first_unicode_codepoint_in_range;
+    /* if non-zero, then the chars are continuous, and this is the first codepoint */
+    int *array_of_unicode_codepoints;
+    /* if non-zero, then this is an array of unicode codepoints */
+    int num_chars;
+    struct nk_tt_packedchar *chardata_for_range; /* output */
+    unsigned char h_oversample, v_oversample;
+    /* don't set these, they're used internally */
+};
+
+struct nk_tt_pack_context {
+    void *pack_info;
+    int   width;
+    int   height;
+    int   stride_in_bytes;
+    int   padding;
+    unsigned int   h_oversample, v_oversample;
+    unsigned char *pixels;
+    void  *nodes;
+};
+
+struct nk_tt_fontinfo {
+    const unsigned char* data; /* pointer to .ttf file */
+    int fontstart;/* offset of start of font */
+    int numGlyphs;/* number of glyphs, needed for range checking */
+    int loca,head,glyf,hhea,hmtx,kern; /* table locations as offset from start of .ttf */
+    int index_map; /* a cmap mapping for our chosen character encoding */
+    int indexToLocFormat; /* format needed to map from glyph index to glyph */
+};
+
+enum {
+  NK_TT_vmove=1,
+  NK_TT_vline,
+  NK_TT_vcurve
+};
+
+struct nk_tt_vertex {
+    short x,y,cx,cy;
+    unsigned char type,padding;
+};
+
+struct nk_tt__bitmap{
+   int w,h,stride;
+   unsigned char *pixels;
+};
+
+struct nk_tt__hheap_chunk {
+    struct nk_tt__hheap_chunk *next;
+};
+struct nk_tt__hheap {
+    struct nk_allocator alloc;
+    struct nk_tt__hheap_chunk *head;
+    void   *first_free;
+    int    num_remaining_in_head_chunk;
+};
+
+struct nk_tt__edge {
+    float x0,y0, x1,y1;
+    int invert;
+};
+
+struct nk_tt__active_edge {
+    struct nk_tt__active_edge *next;
+    float fx,fdx,fdy;
+    float direction;
+    float sy;
+    float ey;
+};
+struct nk_tt__point {float x,y;};
+
+#define NK_TT_MACSTYLE_DONTCARE     0
+#define NK_TT_MACSTYLE_BOLD         1
+#define NK_TT_MACSTYLE_ITALIC       2
+#define NK_TT_MACSTYLE_UNDERSCORE   4
+#define NK_TT_MACSTYLE_NONE         8
+/* <= not same as 0, this makes us check the bitfield is 0 */
+
+enum { /* platformID */
+   NK_TT_PLATFORM_ID_UNICODE   =0,
+   NK_TT_PLATFORM_ID_MAC       =1,
+   NK_TT_PLATFORM_ID_ISO       =2,
+   NK_TT_PLATFORM_ID_MICROSOFT =3
+};
+
+enum { /* encodingID for NK_TT_PLATFORM_ID_UNICODE */
+   NK_TT_UNICODE_EID_UNICODE_1_0    =0,
+   NK_TT_UNICODE_EID_UNICODE_1_1    =1,
+   NK_TT_UNICODE_EID_ISO_10646      =2,
+   NK_TT_UNICODE_EID_UNICODE_2_0_BMP=3,
+   NK_TT_UNICODE_EID_UNICODE_2_0_FULL=4
+};
+
+enum { /* encodingID for NK_TT_PLATFORM_ID_MICROSOFT */
+   NK_TT_MS_EID_SYMBOL        =0,
+   NK_TT_MS_EID_UNICODE_BMP   =1,
+   NK_TT_MS_EID_SHIFTJIS      =2,
+   NK_TT_MS_EID_UNICODE_FULL  =10
+};
+
+enum { /* encodingID for NK_TT_PLATFORM_ID_MAC; same as Script Manager codes */
+   NK_TT_MAC_EID_ROMAN        =0,   NK_TT_MAC_EID_ARABIC       =4,
+   NK_TT_MAC_EID_JAPANESE     =1,   NK_TT_MAC_EID_HEBREW       =5,
+   NK_TT_MAC_EID_CHINESE_TRAD =2,   NK_TT_MAC_EID_GREEK        =6,
+   NK_TT_MAC_EID_KOREAN       =3,   NK_TT_MAC_EID_RUSSIAN      =7
+};
+
+enum { /* languageID for NK_TT_PLATFORM_ID_MICROSOFT; same as LCID... */
+       /* problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs */
+   NK_TT_MS_LANG_ENGLISH     =0x0409,   NK_TT_MS_LANG_ITALIAN     =0x0410,
+   NK_TT_MS_LANG_CHINESE     =0x0804,   NK_TT_MS_LANG_JAPANESE    =0x0411,
+   NK_TT_MS_LANG_DUTCH       =0x0413,   NK_TT_MS_LANG_KOREAN      =0x0412,
+   NK_TT_MS_LANG_FRENCH      =0x040c,   NK_TT_MS_LANG_RUSSIAN     =0x0419,
+   NK_TT_MS_LANG_GERMAN      =0x0407,   NK_TT_MS_LANG_SPANISH     =0x0409,
+   NK_TT_MS_LANG_HEBREW      =0x040d,   NK_TT_MS_LANG_SWEDISH     =0x041D
+};
+
+enum { /* languageID for NK_TT_PLATFORM_ID_MAC */
+   NK_TT_MAC_LANG_ENGLISH      =0 ,   NK_TT_MAC_LANG_JAPANESE     =11,
+   NK_TT_MAC_LANG_ARABIC       =12,   NK_TT_MAC_LANG_KOREAN       =23,
+   NK_TT_MAC_LANG_DUTCH        =4 ,   NK_TT_MAC_LANG_RUSSIAN      =32,
+   NK_TT_MAC_LANG_FRENCH       =1 ,   NK_TT_MAC_LANG_SPANISH      =6 ,
+   NK_TT_MAC_LANG_GERMAN       =2 ,   NK_TT_MAC_LANG_SWEDISH      =5 ,
+   NK_TT_MAC_LANG_HEBREW       =10,   NK_TT_MAC_LANG_CHINESE_SIMPLIFIED =33,
+   NK_TT_MAC_LANG_ITALIAN      =3 ,   NK_TT_MAC_LANG_CHINESE_TRAD =19
+};
+
+#define nk_ttBYTE(p)     (* (const nk_byte *) (p))
+#define nk_ttCHAR(p)     (* (const char *) (p))
+
+#if defined(NK_BIGENDIAN) && !defined(NK_ALLOW_UNALIGNED_TRUETYPE)
+   #define nk_ttUSHORT(p)   (* (nk_ushort *) (p))
+   #define nk_ttSHORT(p)    (* (nk_short *) (p))
+   #define nk_ttULONG(p)    (* (nk_uint *) (p))
+   #define nk_ttLONG(p)     (* (nk_int *) (p))
+#else
+    static nk_ushort nk_ttUSHORT(const nk_byte *p) { return (nk_ushort)(p[0]*256 + p[1]); }
+    static nk_short nk_ttSHORT(const nk_byte *p)   { return (nk_short)(p[0]*256 + p[1]); }
+    static nk_uint nk_ttULONG(const nk_byte *p)  { return (nk_uint)((p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]); }
+#endif
+
+#define nk_tt_tag4(p,c0,c1,c2,c3)\
+    ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))
+#define nk_tt_tag(p,str) nk_tt_tag4(p,str[0],str[1],str[2],str[3])
+
+NK_INTERN int nk_tt_GetGlyphShape(const struct nk_tt_fontinfo *info, struct nk_allocator *alloc,
+                                int glyph_index, struct nk_tt_vertex **pvertices);
+
+NK_INTERN nk_uint
+nk_tt__find_table(const nk_byte *data, nk_uint fontstart, const char *tag)
+{
+    /* @OPTIMIZE: binary search */
+    nk_int num_tables = nk_ttUSHORT(data+fontstart+4);
+    nk_uint tabledir = fontstart + 12;
+    nk_int i;
+    for (i = 0; i < num_tables; ++i) {
+        nk_uint loc = tabledir + (nk_uint)(16*i);
+        if (nk_tt_tag(data+loc+0, tag))
+            return nk_ttULONG(data+loc+8);
+    }
+    return 0;
+}
+
+NK_INTERN int
+nk_tt_InitFont(struct nk_tt_fontinfo *info, const unsigned char *data2, int fontstart)
+{
+    nk_uint cmap, t;
+    nk_int i,numTables;
+    const nk_byte *data = (const nk_byte *) data2;
+
+    info->data = data;
+    info->fontstart = fontstart;
+
+    cmap = nk_tt__find_table(data, (nk_uint)fontstart, "cmap");       /* required */
+    info->loca = (int)nk_tt__find_table(data, (nk_uint)fontstart, "loca"); /* required */
+    info->head = (int)nk_tt__find_table(data, (nk_uint)fontstart, "head"); /* required */
+    info->glyf = (int)nk_tt__find_table(data, (nk_uint)fontstart, "glyf"); /* required */
+    info->hhea = (int)nk_tt__find_table(data, (nk_uint)fontstart, "hhea"); /* required */
+    info->hmtx = (int)nk_tt__find_table(data, (nk_uint)fontstart, "hmtx"); /* required */
+    info->kern = (int)nk_tt__find_table(data, (nk_uint)fontstart, "kern"); /* not required */
+    if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx)
+        return 0;
+
+    t = nk_tt__find_table(data, (nk_uint)fontstart, "maxp");
+    if (t) info->numGlyphs = nk_ttUSHORT(data+t+4);
+    else info->numGlyphs = 0xffff;
+
+    /* find a cmap encoding table we understand *now* to avoid searching */
+    /* later. (todo: could make this installable) */
+    /* the same regardless of glyph. */
+    numTables = nk_ttUSHORT(data + cmap + 2);
+    info->index_map = 0;
+    for (i=0; i < numTables; ++i)
+    {
+        nk_uint encoding_record = cmap + 4 + 8 * (nk_uint)i;
+        /* find an encoding we understand: */
+        switch(nk_ttUSHORT(data+encoding_record)) {
+        case NK_TT_PLATFORM_ID_MICROSOFT:
+            switch (nk_ttUSHORT(data+encoding_record+2)) {
+            case NK_TT_MS_EID_UNICODE_BMP:
+            case NK_TT_MS_EID_UNICODE_FULL:
+                /* MS/Unicode */
+                info->index_map = (int)(cmap + nk_ttULONG(data+encoding_record+4));
+                break;
+            default: break;
+            } break;
+        case NK_TT_PLATFORM_ID_UNICODE:
+            /* Mac/iOS has these */
+            /* all the encodingIDs are unicode, so we don't bother to check it */
+            info->index_map = (int)(cmap + nk_ttULONG(data+encoding_record+4));
+            break;
+        default: break;
+        }
+    }
+    if (info->index_map == 0)
+        return 0;
+    info->indexToLocFormat = nk_ttUSHORT(data+info->head + 50);
+    return 1;
+}
+
+NK_INTERN int
+nk_tt_FindGlyphIndex(const struct nk_tt_fontinfo *info, int unicode_codepoint)
+{
+    const nk_byte *data = info->data;
+    nk_uint index_map = (nk_uint)info->index_map;
+
+    nk_ushort format = nk_ttUSHORT(data + index_map + 0);
+    if (format == 0) { /* apple byte encoding */
+        nk_int bytes = nk_ttUSHORT(data + index_map + 2);
+        if (unicode_codepoint < bytes-6)
+            return nk_ttBYTE(data + index_map + 6 + unicode_codepoint);
+        return 0;
+    } else if (format == 6) {
+        nk_uint first = nk_ttUSHORT(data + index_map + 6);
+        nk_uint count = nk_ttUSHORT(data + index_map + 8);
+        if ((nk_uint) unicode_codepoint >= first && (nk_uint) unicode_codepoint < first+count)
+            return nk_ttUSHORT(data + index_map + 10 + (unicode_codepoint - (int)first)*2);
+        return 0;
+    } else if (format == 2) {
+        NK_ASSERT(0); /* @TODO: high-byte mapping for japanese/chinese/korean */
+        return 0;
+    } else if (format == 4) { /* standard mapping for windows fonts: binary search collection of ranges */
+        nk_ushort segcount = nk_ttUSHORT(data+index_map+6) >> 1;
+        nk_ushort searchRange = nk_ttUSHORT(data+index_map+8) >> 1;
+        nk_ushort entrySelector = nk_ttUSHORT(data+index_map+10);
+        nk_ushort rangeShift = nk_ttUSHORT(data+index_map+12) >> 1;
+
+        /* do a binary search of the segments */
+        nk_uint endCount = index_map + 14;
+        nk_uint search = endCount;
+
+        if (unicode_codepoint > 0xffff)
+            return 0;
+
+        /* they lie from endCount .. endCount + segCount */
+        /* but searchRange is the nearest power of two, so... */
+        if (unicode_codepoint >= nk_ttUSHORT(data + search + rangeShift*2))
+            search += (nk_uint)(rangeShift*2);
+
+        /* now decrement to bias correctly to find smallest */
+        search -= 2;
+        while (entrySelector) {
+            nk_ushort end;
+            searchRange >>= 1;
+            end = nk_ttUSHORT(data + search + searchRange*2);
+            if (unicode_codepoint > end)
+                search += (nk_uint)(searchRange*2);
+            --entrySelector;
+        }
+        search += 2;
+
+      {
+         nk_ushort offset, start;
+         nk_ushort item = (nk_ushort) ((search - endCount) >> 1);
+
+         NK_ASSERT(unicode_codepoint <= nk_ttUSHORT(data + endCount + 2*item));
+         start = nk_ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);
+         if (unicode_codepoint < start)
+            return 0;
+
+         offset = nk_ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
+         if (offset == 0)
+            return (nk_ushort) (unicode_codepoint + nk_ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item));
+
+         return nk_ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);
+      }
+   } else if (format == 12 || format == 13) {
+        nk_uint ngroups = nk_ttULONG(data+index_map+12);
+        nk_int low,high;
+        low = 0; high = (nk_int)ngroups;
+        /* Binary search the right group. */
+        while (low < high) {
+            nk_int mid = low + ((high-low) >> 1); /* rounds down, so low <= mid < high */
+            nk_uint start_char = nk_ttULONG(data+index_map+16+mid*12);
+            nk_uint end_char = nk_ttULONG(data+index_map+16+mid*12+4);
+            if ((nk_uint) unicode_codepoint < start_char)
+                high = mid;
+            else if ((nk_uint) unicode_codepoint > end_char)
+                low = mid+1;
+            else {
+                nk_uint start_glyph = nk_ttULONG(data+index_map+16+mid*12+8);
+                if (format == 12)
+                    return (int)start_glyph + (int)unicode_codepoint - (int)start_char;
+                else /* format == 13 */
+                    return (int)start_glyph;
+            }
+        }
+        return 0; /* not found */
+    }
+    /* @TODO */
+    NK_ASSERT(0);
+    return 0;
+}
+
+NK_INTERN void
+nk_tt_setvertex(struct nk_tt_vertex *v, nk_byte type, nk_int x, nk_int y, nk_int cx, nk_int cy)
+{
+    v->type = type;
+    v->x = (nk_short) x;
+    v->y = (nk_short) y;
+    v->cx = (nk_short) cx;
+    v->cy = (nk_short) cy;
+}
+
+NK_INTERN int
+nk_tt__GetGlyfOffset(const struct nk_tt_fontinfo *info, int glyph_index)
+{
+    int g1,g2;
+    if (glyph_index >= info->numGlyphs) return -1; /* glyph index out of range */
+    if (info->indexToLocFormat >= 2)    return -1; /* unknown index->glyph map format */
+
+    if (info->indexToLocFormat == 0) {
+        g1 = info->glyf + nk_ttUSHORT(info->data + info->loca + glyph_index * 2) * 2;
+        g2 = info->glyf + nk_ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2;
+    } else {
+        g1 = info->glyf + (int)nk_ttULONG (info->data + info->loca + glyph_index * 4);
+        g2 = info->glyf + (int)nk_ttULONG (info->data + info->loca + glyph_index * 4 + 4);
+    }
+    return g1==g2 ? -1 : g1; /* if length is 0, return -1 */
+}
+
+NK_INTERN int
+nk_tt_GetGlyphBox(const struct nk_tt_fontinfo *info, int glyph_index,
+    int *x0, int *y0, int *x1, int *y1)
+{
+    int g = nk_tt__GetGlyfOffset(info, glyph_index);
+    if (g < 0) return 0;
+
+    if (x0) *x0 = nk_ttSHORT(info->data + g + 2);
+    if (y0) *y0 = nk_ttSHORT(info->data + g + 4);
+    if (x1) *x1 = nk_ttSHORT(info->data + g + 6);
+    if (y1) *y1 = nk_ttSHORT(info->data + g + 8);
+    return 1;
+}
+
+NK_INTERN int
+stbtt__close_shape(struct nk_tt_vertex *vertices, int num_vertices, int was_off,
+    int start_off, nk_int sx, nk_int sy, nk_int scx, nk_int scy, nk_int cx, nk_int cy)
+{
+   if (start_off) {
+      if (was_off)
+         nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy);
+      nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve, sx,sy,scx,scy);
+   } else {
+      if (was_off)
+         nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve,sx,sy,cx,cy);
+      else
+         nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vline,sx,sy,0,0);
+   }
+   return num_vertices;
+}
+
+NK_INTERN int
+nk_tt_GetGlyphShape(const struct nk_tt_fontinfo *info, struct nk_allocator *alloc,
+    int glyph_index, struct nk_tt_vertex **pvertices)
+{
+    nk_short numberOfContours;
+    const nk_byte *endPtsOfContours;
+    const nk_byte *data = info->data;
+    struct nk_tt_vertex *vertices=0;
+    int num_vertices=0;
+    int g = nk_tt__GetGlyfOffset(info, glyph_index);
+    *pvertices = 0;
+
+    if (g < 0) return 0;
+    numberOfContours = nk_ttSHORT(data + g);
+    if (numberOfContours > 0) {
+        nk_byte flags=0,flagcount;
+        nk_int ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0;
+        nk_int x,y,cx,cy,sx,sy, scx,scy;
+        const nk_byte *points;
+        endPtsOfContours = (data + g + 10);
+        ins = nk_ttUSHORT(data + g + 10 + numberOfContours * 2);
+        points = data + g + 10 + numberOfContours * 2 + 2 + ins;
+
+        n = 1+nk_ttUSHORT(endPtsOfContours + numberOfContours*2-2);
+        m = n + 2*numberOfContours;  /* a loose bound on how many vertices we might need */
+        vertices = (struct nk_tt_vertex *)alloc->alloc(alloc->userdata, 0, (nk_size)m * sizeof(vertices[0]));
+        if (vertices == 0)
+            return 0;
+
+        next_move = 0;
+        flagcount=0;
+
+        /* in first pass, we load uninterpreted data into the allocated array */
+        /* above, shifted to the end of the array so we won't overwrite it when */
+        /* we create our final data starting from the front */
+        off = m - n; /* starting offset for uninterpreted data, regardless of how m ends up being calculated */
+
+        /* first load flags */
+        for (i=0; i < n; ++i) {
+            if (flagcount == 0) {
+                flags = *points++;
+                if (flags & 8)
+                    flagcount = *points++;
+            } else --flagcount;
+            vertices[off+i].type = flags;
+        }
+
+        /* now load x coordinates */
+        x=0;
+        for (i=0; i < n; ++i) {
+            flags = vertices[off+i].type;
+            if (flags & 2) {
+                nk_short dx = *points++;
+                x += (flags & 16) ? dx : -dx; /* ??? */
+            } else {
+                if (!(flags & 16)) {
+                    x = x + (nk_short) (points[0]*256 + points[1]);
+                    points += 2;
+                }
+            }
+            vertices[off+i].x = (nk_short) x;
+        }
+
+        /* now load y coordinates */
+        y=0;
+        for (i=0; i < n; ++i) {
+            flags = vertices[off+i].type;
+            if (flags & 4) {
+                nk_short dy = *points++;
+                y += (flags & 32) ? dy : -dy; /* ??? */
+            } else {
+                if (!(flags & 32)) {
+                    y = y + (nk_short) (points[0]*256 + points[1]);
+                    points += 2;
+                }
+            }
+            vertices[off+i].y = (nk_short) y;
+        }
+
+        /* now convert them to our format */
+        num_vertices=0;
+        sx = sy = cx = cy = scx = scy = 0;
+        for (i=0; i < n; ++i)
+        {
+            flags = vertices[off+i].type;
+            x     = (nk_short) vertices[off+i].x;
+            y     = (nk_short) vertices[off+i].y;
+
+            if (next_move == i) {
+                if (i != 0)
+                    num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
+
+                /* now start the new one                */
+                start_off = !(flags & 1);
+                if (start_off) {
+                    /* if we start off with an off-curve point, then when we need to find a point on the curve */
+                    /* where we can start, and we need to save some state for when we wraparound. */
+                    scx = x;
+                    scy = y;
+                    if (!(vertices[off+i+1].type & 1)) {
+                        /* next point is also a curve point, so interpolate an on-point curve */
+                        sx = (x + (nk_int) vertices[off+i+1].x) >> 1;
+                        sy = (y + (nk_int) vertices[off+i+1].y) >> 1;
+                    } else {
+                        /* otherwise just use the next point as our start point */
+                        sx = (nk_int) vertices[off+i+1].x;
+                        sy = (nk_int) vertices[off+i+1].y;
+                        ++i; /* we're using point i+1 as the starting point, so skip it */
+                    }
+                } else {
+                    sx = x;
+                    sy = y;
+                }
+                nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vmove,sx,sy,0,0);
+                was_off = 0;
+                next_move = 1 + nk_ttUSHORT(endPtsOfContours+j*2);
+                ++j;
+            } else {
+                if (!(flags & 1))
+                { /* if it's a curve */
+                    if (was_off) /* two off-curve control points in a row means interpolate an on-curve midpoint */
+                        nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy);
+                    cx = x;
+                    cy = y;
+                    was_off = 1;
+                } else {
+                    if (was_off)
+                        nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve, x,y, cx, cy);
+                    else nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vline, x,y,0,0);
+                    was_off = 0;
+                }
+            }
+        }
+        num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
+    } else if (numberOfContours == -1) {
+        /* Compound shapes. */
+        int more = 1;
+        const nk_byte *comp = data + g + 10;
+        num_vertices = 0;
+        vertices = 0;
+
+        while (more)
+        {
+            nk_ushort flags, gidx;
+            int comp_num_verts = 0, i;
+            struct nk_tt_vertex *comp_verts = 0, *tmp = 0;
+            float mtx[6] = {1,0,0,1,0,0}, m, n;
+
+            flags = (nk_ushort)nk_ttSHORT(comp); comp+=2;
+            gidx = (nk_ushort)nk_ttSHORT(comp); comp+=2;
+
+            if (flags & 2) { /* XY values */
+                if (flags & 1) { /* shorts */
+                    mtx[4] = nk_ttSHORT(comp); comp+=2;
+                    mtx[5] = nk_ttSHORT(comp); comp+=2;
+                } else {
+                    mtx[4] = nk_ttCHAR(comp); comp+=1;
+                    mtx[5] = nk_ttCHAR(comp); comp+=1;
+                }
+            } else {
+                /* @TODO handle matching point */
+                NK_ASSERT(0);
+            }
+            if (flags & (1<<3)) { /* WE_HAVE_A_SCALE */
+                mtx[0] = mtx[3] = nk_ttSHORT(comp)/16384.0f; comp+=2;
+                mtx[1] = mtx[2] = 0;
+            } else if (flags & (1<<6)) { /* WE_HAVE_AN_X_AND_YSCALE */
+                mtx[0] = nk_ttSHORT(comp)/16384.0f; comp+=2;
+                mtx[1] = mtx[2] = 0;
+                mtx[3] = nk_ttSHORT(comp)/16384.0f; comp+=2;
+            } else if (flags & (1<<7)) { /* WE_HAVE_A_TWO_BY_TWO */
+                mtx[0] = nk_ttSHORT(comp)/16384.0f; comp+=2;
+                mtx[1] = nk_ttSHORT(comp)/16384.0f; comp+=2;
+                mtx[2] = nk_ttSHORT(comp)/16384.0f; comp+=2;
+                mtx[3] = nk_ttSHORT(comp)/16384.0f; comp+=2;
+            }
+
+             /* Find transformation scales. */
+            m = (float) NK_SQRT(mtx[0]*mtx[0] + mtx[1]*mtx[1]);
+            n = (float) NK_SQRT(mtx[2]*mtx[2] + mtx[3]*mtx[3]);
+
+             /* Get indexed glyph. */
+            comp_num_verts = nk_tt_GetGlyphShape(info, alloc, gidx, &comp_verts);
+            if (comp_num_verts > 0)
+            {
+                /* Transform vertices. */
+                for (i = 0; i < comp_num_verts; ++i) {
+                    struct nk_tt_vertex* v = &comp_verts[i];
+                    short x,y;
+                    x=v->x; y=v->y;
+                    v->x = (short)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
+                    v->y = (short)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
+                    x=v->cx; y=v->cy;
+                    v->cx = (short)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
+                    v->cy = (short)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
+                }
+                /* Append vertices. */
+                tmp = (struct nk_tt_vertex*)alloc->alloc(alloc->userdata, 0,
+                    (nk_size)(num_vertices+comp_num_verts)*sizeof(struct nk_tt_vertex));
+                if (!tmp) {
+                    if (vertices) alloc->free(alloc->userdata, vertices);
+                    if (comp_verts) alloc->free(alloc->userdata, comp_verts);
+                    return 0;
+                }
+                if (num_vertices > 0) NK_MEMCPY(tmp, vertices, (nk_size)num_vertices*sizeof(struct nk_tt_vertex));
+                NK_MEMCPY(tmp+num_vertices, comp_verts, (nk_size)comp_num_verts*sizeof(struct nk_tt_vertex));
+                if (vertices) alloc->free(alloc->userdata,vertices);
+                vertices = tmp;
+                alloc->free(alloc->userdata,comp_verts);
+                num_vertices += comp_num_verts;
+            }
+            /* More components ? */
+            more = flags & (1<<5);
+        }
+    } else if (numberOfContours < 0) {
+        /* @TODO other compound variations? */
+        NK_ASSERT(0);
+    } else {
+        /* numberOfCounters == 0, do nothing */
+    }
+    *pvertices = vertices;
+    return num_vertices;
+}
+
+NK_INTERN void
+nk_tt_GetGlyphHMetrics(const struct nk_tt_fontinfo *info, int glyph_index,
+    int *advanceWidth, int *leftSideBearing)
+{
+    nk_ushort numOfLongHorMetrics = nk_ttUSHORT(info->data+info->hhea + 34);
+    if (glyph_index < numOfLongHorMetrics) {
+        if (advanceWidth)
+            *advanceWidth    = nk_ttSHORT(info->data + info->hmtx + 4*glyph_index);
+        if (leftSideBearing)
+            *leftSideBearing = nk_ttSHORT(info->data + info->hmtx + 4*glyph_index + 2);
+    } else {
+        if (advanceWidth)
+            *advanceWidth    = nk_ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1));
+        if (leftSideBearing)
+            *leftSideBearing = nk_ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics));
+    }
+}
+
+NK_INTERN void
+nk_tt_GetFontVMetrics(const struct nk_tt_fontinfo *info,
+    int *ascent, int *descent, int *lineGap)
+{
+   if (ascent ) *ascent  = nk_ttSHORT(info->data+info->hhea + 4);
+   if (descent) *descent = nk_ttSHORT(info->data+info->hhea + 6);
+   if (lineGap) *lineGap = nk_ttSHORT(info->data+info->hhea + 8);
+}
+
+NK_INTERN float
+nk_tt_ScaleForPixelHeight(const struct nk_tt_fontinfo *info, float height)
+{
+   int fheight = nk_ttSHORT(info->data + info->hhea + 4) - nk_ttSHORT(info->data + info->hhea + 6);
+   return (float) height / (float)fheight;
+}
+
+NK_INTERN float
+nk_tt_ScaleForMappingEmToPixels(const struct nk_tt_fontinfo *info, float pixels)
+{
+   int unitsPerEm = nk_ttUSHORT(info->data + info->head + 18);
+   return pixels / (float)unitsPerEm;
+}
+
+/*-------------------------------------------------------------
+ *            antialiasing software rasterizer
+ * --------------------------------------------------------------*/
+NK_INTERN void
+nk_tt_GetGlyphBitmapBoxSubpixel(const struct nk_tt_fontinfo *font,
+    int glyph, float scale_x, float scale_y,float shift_x, float shift_y,
+    int *ix0, int *iy0, int *ix1, int *iy1)
+{
+    int x0,y0,x1,y1;
+    if (!nk_tt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) {
+        /* e.g. space character */
+        if (ix0) *ix0 = 0;
+        if (iy0) *iy0 = 0;
+        if (ix1) *ix1 = 0;
+        if (iy1) *iy1 = 0;
+    } else {
+        /* move to integral bboxes (treating pixels as little squares, what pixels get touched)? */
+        if (ix0) *ix0 = nk_ifloorf((float)x0 * scale_x + shift_x);
+        if (iy0) *iy0 = nk_ifloorf((float)-y1 * scale_y + shift_y);
+        if (ix1) *ix1 = nk_iceilf ((float)x1 * scale_x + shift_x);
+        if (iy1) *iy1 = nk_iceilf ((float)-y0 * scale_y + shift_y);
+    }
+}
+
+NK_INTERN void
+nk_tt_GetGlyphBitmapBox(const struct nk_tt_fontinfo *font, int glyph,
+    float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
+{
+   nk_tt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1);
+}
+
+/*-------------------------------------------------------------
+ *                          Rasterizer
+ * --------------------------------------------------------------*/
+NK_INTERN void*
+nk_tt__hheap_alloc(struct nk_tt__hheap *hh, nk_size size)
+{
+    if (hh->first_free) {
+        void *p = hh->first_free;
+        hh->first_free = * (void **) p;
+        return p;
+    } else {
+        if (hh->num_remaining_in_head_chunk == 0) {
+            int count = (size < 32 ? 2000 : size < 128 ? 800 : 100);
+            struct nk_tt__hheap_chunk *c = (struct nk_tt__hheap_chunk *)
+                hh->alloc.alloc(hh->alloc.userdata, 0,
+                sizeof(struct nk_tt__hheap_chunk) + size * (nk_size)count);
+            if (c == 0) return 0;
+            c->next = hh->head;
+            hh->head = c;
+            hh->num_remaining_in_head_chunk = count;
+        }
+        --hh->num_remaining_in_head_chunk;
+        return (char *) (hh->head) + size * (nk_size)hh->num_remaining_in_head_chunk;
+    }
+}
+
+NK_INTERN void
+nk_tt__hheap_free(struct nk_tt__hheap *hh, void *p)
+{
+    *(void **) p = hh->first_free;
+    hh->first_free = p;
+}
+
+NK_INTERN void
+nk_tt__hheap_cleanup(struct nk_tt__hheap *hh)
+{
+    struct nk_tt__hheap_chunk *c = hh->head;
+    while (c) {
+        struct nk_tt__hheap_chunk *n = c->next;
+        hh->alloc.free(hh->alloc.userdata, c);
+        c = n;
+    }
+}
+
+NK_INTERN struct nk_tt__active_edge*
+nk_tt__new_active(struct nk_tt__hheap *hh, struct nk_tt__edge *e,
+    int off_x, float start_point)
+{
+    struct nk_tt__active_edge *z = (struct nk_tt__active_edge *)
+        nk_tt__hheap_alloc(hh, sizeof(*z));
+    float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
+    /*STBTT_assert(e->y0 <= start_point); */
+    if (!z) return z;
+    z->fdx = dxdy;
+    z->fdy = (dxdy != 0) ? (1/dxdy): 0;
+    z->fx = e->x0 + dxdy * (start_point - e->y0);
+    z->fx -= (float)off_x;
+    z->direction = e->invert ? 1.0f : -1.0f;
+    z->sy = e->y0;
+    z->ey = e->y1;
+    z->next = 0;
+    return z;
+}
+
+NK_INTERN void
+nk_tt__handle_clipped_edge(float *scanline, int x, struct nk_tt__active_edge *e,
+    float x0, float y0, float x1, float y1)
+{
+    if (y0 == y1) return;
+    NK_ASSERT(y0 < y1);
+    NK_ASSERT(e->sy <= e->ey);
+    if (y0 > e->ey) return;
+    if (y1 < e->sy) return;
+    if (y0 < e->sy) {
+        x0 += (x1-x0) * (e->sy - y0) / (y1-y0);
+        y0 = e->sy;
+    }
+    if (y1 > e->ey) {
+        x1 += (x1-x0) * (e->ey - y1) / (y1-y0);
+        y1 = e->ey;
+    }
+
+    if (x0 == x) NK_ASSERT(x1 <= x+1);
+    else if (x0 == x+1) NK_ASSERT(x1 >= x);
+    else if (x0 <= x) NK_ASSERT(x1 <= x);
+    else if (x0 >= x+1) NK_ASSERT(x1 >= x+1);
+    else NK_ASSERT(x1 >= x && x1 <= x+1);
+
+    if (x0 <= x && x1 <= x)
+        scanline[x] += e->direction * (y1-y0);
+    else if (x0 >= x+1 && x1 >= x+1);
+    else {
+        NK_ASSERT(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1);
+        /* coverage = 1 - average x position */
+        scanline[x] += (float)e->direction * (float)(y1-y0) * (1.0f-((x0-(float)x)+(x1-(float)x))/2.0f);
+    }
+}
+
+NK_INTERN void
+nk_tt__fill_active_edges_new(float *scanline, float *scanline_fill, int len,
+    struct nk_tt__active_edge *e, float y_top)
+{
+    float y_bottom = y_top+1;
+    while (e)
+    {
+        /* brute force every pixel */
+        /* compute intersection points with top & bottom */
+        NK_ASSERT(e->ey >= y_top);
+        if (e->fdx == 0) {
+            float x0 = e->fx;
+            if (x0 < len) {
+                if (x0 >= 0) {
+                    nk_tt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom);
+                    nk_tt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom);
+                } else {
+                    nk_tt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom);
+                }
+            }
+        } else {
+            float x0 = e->fx;
+            float dx = e->fdx;
+            float xb = x0 + dx;
+            float x_top, x_bottom;
+            float y0,y1;
+            float dy = e->fdy;
+            NK_ASSERT(e->sy <= y_bottom && e->ey >= y_top);
+
+            /* compute endpoints of line segment clipped to this scanline (if the */
+            /* line segment starts on this scanline. x0 is the intersection of the */
+            /* line with y_top, but that may be off the line segment. */
+            if (e->sy > y_top) {
+                x_top = x0 + dx * (e->sy - y_top);
+                y0 = e->sy;
+            } else {
+                x_top = x0;
+                y0 = y_top;
+            }
+
+            if (e->ey < y_bottom) {
+                x_bottom = x0 + dx * (e->ey - y_top);
+                y1 = e->ey;
+            } else {
+                x_bottom = xb;
+                y1 = y_bottom;
+            }
+
+            if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len)
+            {
+                /* from here on, we don't have to range check x values */
+                if ((int) x_top == (int) x_bottom) {
+                    float height;
+                    /* simple case, only spans one pixel */
+                    int x = (int) x_top;
+                    height = y1 - y0;
+                    NK_ASSERT(x >= 0 && x < len);
+                    scanline[x] += e->direction * (1.0f-(((float)x_top - (float)x) + ((float)x_bottom-(float)x))/2.0f)  * (float)height;
+                    scanline_fill[x] += e->direction * (float)height; /* everything right of this pixel is filled */
+                } else {
+                    int x,x1,x2;
+                    float y_crossing, step, sign, area;
+                    /* covers 2+ pixels */
+                    if (x_top > x_bottom)
+                    {
+                        /* flip scanline vertically; signed area is the same */
+                        float t;
+                        y0 = y_bottom - (y0 - y_top);
+                        y1 = y_bottom - (y1 - y_top);
+                        t = y0; y0 = y1; y1 = t;
+                        t = x_bottom; x_bottom = x_top; x_top = t;
+                        dx = -dx;
+                        dy = -dy;
+                        t = x0; x0 = xb; xb = t;
+                    }
+
+                    x1 = (int) x_top;
+                    x2 = (int) x_bottom;
+                    /* compute intersection with y axis at x1+1 */
+                    y_crossing = ((float)x1+1 - (float)x0) * (float)dy + (float)y_top;
+
+                    sign = e->direction;
+                    /* area of the rectangle covered from y0..y_crossing */
+                    area = sign * (y_crossing-y0);
+                    /* area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) */
+                    scanline[x1] += area * (1.0f-((float)((float)x_top - (float)x1)+(float)(x1+1-x1))/2.0f);
+
+                    step = sign * dy;
+                    for (x = x1+1; x < x2; ++x) {
+                        scanline[x] += area + step/2;
+                        area += step;
+                    }
+                    y_crossing += (float)dy * (float)(x2 - (x1+1));
+
+                    scanline[x2] += area + sign * (1.0f-((float)(x2-x2)+((float)x_bottom-(float)x2))/2.0f) * (y1-y_crossing);
+                    scanline_fill[x2] += sign * (y1-y0);
+                }
+            }
+            else
+            {
+                /* if edge goes outside of box we're drawing, we require */
+                /* clipping logic. since this does not match the intended use */
+                /* of this library, we use a different, very slow brute */
+                /* force implementation */
+                int x;
+                for (x=0; x < len; ++x)
+                {
+                    /* cases: */
+                    /* */
+                    /* there can be up to two intersections with the pixel. any intersection */
+                    /* with left or right edges can be handled by splitting into two (or three) */
+                    /* regions. intersections with top & bottom do not necessitate case-wise logic. */
+                    /* */
+                    /* the old way of doing this found the intersections with the left & right edges, */
+                    /* then used some simple logic to produce up to three segments in sorted order */
+                    /* from top-to-bottom. however, this had a problem: if an x edge was epsilon */
+                    /* across the x border, then the corresponding y position might not be distinct */
+                    /* from the other y segment, and it might ignored as an empty segment. to avoid */
+                    /* that, we need to explicitly produce segments based on x positions. */
+
+                    /* rename variables to clear pairs */
+                    float ya = y_top;
+                    float x1 = (float) (x);
+                    float x2 = (float) (x+1);
+                    float x3 = xb;
+                    float y3 = y_bottom;
+                    float yb,y2;
+
+                    yb = ((float)x - x0) / dx + y_top;
+                    y2 = ((float)x+1 - x0) / dx + y_top;
+
+                    if (x0 < x1 && x3 > x2) {         /* three segments descending down-right */
+                        nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x1,yb);
+                        nk_tt__handle_clipped_edge(scanline,x,e, x1,yb, x2,y2);
+                        nk_tt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
+                    } else if (x3 < x1 && x0 > x2) {  /* three segments descending down-left */
+                        nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x2,y2);
+                        nk_tt__handle_clipped_edge(scanline,x,e, x2,y2, x1,yb);
+                        nk_tt__handle_clipped_edge(scanline,x,e, x1,yb, x3,y3);
+                    } else if (x0 < x1 && x3 > x1) {  /* two segments across x, down-right */
+                        nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x1,yb);
+                        nk_tt__handle_clipped_edge(scanline,x,e, x1,yb, x3,y3);
+                    } else if (x3 < x1 && x0 > x1) {  /* two segments across x, down-left */
+                        nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x1,yb);
+                        nk_tt__handle_clipped_edge(scanline,x,e, x1,yb, x3,y3);
+                    } else if (x0 < x2 && x3 > x2) {  /* two segments across x+1, down-right */
+                        nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x2,y2);
+                        nk_tt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
+                    } else if (x3 < x2 && x0 > x2) {  /* two segments across x+1, down-left */
+                        nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x2,y2);
+                        nk_tt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
+                    } else {  /* one segment */
+                        nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x3,y3);
+                    }
+                }
+            }
+        }
+        e = e->next;
+    }
+}
+
+/* directly AA rasterize edges w/o supersampling */
+NK_INTERN void
+nk_tt__rasterize_sorted_edges(struct nk_tt__bitmap *result, struct nk_tt__edge *e,
+    int n, int vsubsample, int off_x, int off_y, struct nk_allocator *alloc)
+{
+    struct nk_tt__hheap hh;
+    struct nk_tt__active_edge *active = 0;
+    int y,j=0, i;
+    float scanline_data[129], *scanline, *scanline2;
+
+    NK_UNUSED(vsubsample);
+    nk_zero_struct(hh);
+    hh.alloc = *alloc;
+
+    if (result->w > 64)
+        scanline = (float *) alloc->alloc(alloc->userdata,0, (nk_size)(result->w*2+1) * sizeof(float));
+    else scanline = scanline_data;
+
+    scanline2 = scanline + result->w;
+    y = off_y;
+    e[n].y0 = (float) (off_y + result->h) + 1;
+
+    while (j < result->h)
+    {
+        /* find center of pixel for this scanline */
+        float scan_y_top    = (float)y + 0.0f;
+        float scan_y_bottom = (float)y + 1.0f;
+        struct nk_tt__active_edge **step = &active;
+
+        NK_MEMSET(scanline , 0, (nk_size)result->w*sizeof(scanline[0]));
+        NK_MEMSET(scanline2, 0, (nk_size)(result->w+1)*sizeof(scanline[0]));
+
+        /* update all active edges; */
+        /* remove all active edges that terminate before the top of this scanline */
+        while (*step) {
+            struct nk_tt__active_edge * z = *step;
+            if (z->ey <= scan_y_top) {
+                *step = z->next; /* delete from list */
+                NK_ASSERT(z->direction);
+                z->direction = 0;
+                nk_tt__hheap_free(&hh, z);
+            } else {
+                step = &((*step)->next); /* advance through list */
+            }
+        }
+
+        /* insert all edges that start before the bottom of this scanline */
+        while (e->y0 <= scan_y_bottom) {
+            if (e->y0 != e->y1) {
+                struct nk_tt__active_edge *z = nk_tt__new_active(&hh, e, off_x, scan_y_top);
+                if (z != 0) {
+                    NK_ASSERT(z->ey >= scan_y_top);
+                    /* insert at front */
+                    z->next = active;
+                    active = z;
+                }
+            }
+            ++e;
+        }
+
+        /* now process all active edges */
+        if (active)
+            nk_tt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top);
+
+        {
+            float sum = 0;
+            for (i=0; i < result->w; ++i) {
+                float k;
+                int m;
+                sum += scanline2[i];
+                k = scanline[i] + sum;
+                k = (float) NK_ABS(k) * 255.0f + 0.5f;
+                m = (int) k;
+                if (m > 255) m = 255;
+                result->pixels[j*result->stride + i] = (unsigned char) m;
+            }
+        }
+        /* advance all the edges */
+        step = &active;
+        while (*step) {
+            struct nk_tt__active_edge *z = *step;
+            z->fx += z->fdx; /* advance to position for current scanline */
+            step = &((*step)->next); /* advance through list */
+        }
+        ++y;
+        ++j;
+    }
+    nk_tt__hheap_cleanup(&hh);
+    if (scanline != scanline_data)
+        alloc->free(alloc->userdata, scanline);
+}
+
+#define NK_TT__COMPARE(a,b)  ((a)->y0 < (b)->y0)
+NK_INTERN void
+nk_tt__sort_edges_ins_sort(struct nk_tt__edge *p, int n)
+{
+    int i,j;
+    for (i=1; i < n; ++i) {
+        struct nk_tt__edge t = p[i], *a = &t;
+        j = i;
+        while (j > 0) {
+            struct nk_tt__edge *b = &p[j-1];
+            int c = NK_TT__COMPARE(a,b);
+            if (!c) break;
+            p[j] = p[j-1];
+            --j;
+        }
+        if (i != j)
+            p[j] = t;
+    }
+}
+
+NK_INTERN void
+nk_tt__sort_edges_quicksort(struct nk_tt__edge *p, int n)
+{
+    /* threshold for transitioning to insertion sort */
+    while (n > 12) {
+        struct nk_tt__edge t;
+        int c01,c12,c,m,i,j;
+
+        /* compute median of three */
+        m = n >> 1;
+        c01 = NK_TT__COMPARE(&p[0],&p[m]);
+        c12 = NK_TT__COMPARE(&p[m],&p[n-1]);
+
+        /* if 0 >= mid >= end, or 0 < mid < end, then use mid */
+        if (c01 != c12) {
+            /* otherwise, we'll need to swap something else to middle */
+            int z;
+            c = NK_TT__COMPARE(&p[0],&p[n-1]);
+            /* 0>mid && mid<n:  0>n => n; 0<n => 0 */
+            /* 0<mid && mid>n:  0>n => 0; 0<n => n */
+            z = (c == c12) ? 0 : n-1;
+            t = p[z];
+            p[z] = p[m];
+            p[m] = t;
+        }
+
+        /* now p[m] is the median-of-three */
+        /* swap it to the beginning so it won't move around */
+        t = p[0];
+        p[0] = p[m];
+        p[m] = t;
+
+        /* partition loop */
+        i=1;
+        j=n-1;
+        for(;;) {
+            /* handling of equality is crucial here */
+            /* for sentinels & efficiency with duplicates */
+            for (;;++i) {
+                if (!NK_TT__COMPARE(&p[i], &p[0])) break;
+            }
+            for (;;--j) {
+                if (!NK_TT__COMPARE(&p[0], &p[j])) break;
+            }
+
+            /* make sure we haven't crossed */
+             if (i >= j) break;
+             t = p[i];
+             p[i] = p[j];
+             p[j] = t;
+
+            ++i;
+            --j;
+
+        }
+
+        /* recurse on smaller side, iterate on larger */
+        if (j < (n-i)) {
+            nk_tt__sort_edges_quicksort(p,j);
+            p = p+i;
+            n = n-i;
+        } else {
+            nk_tt__sort_edges_quicksort(p+i, n-i);
+            n = j;
+        }
+    }
+}
+
+NK_INTERN void
+nk_tt__sort_edges(struct nk_tt__edge *p, int n)
+{
+   nk_tt__sort_edges_quicksort(p, n);
+   nk_tt__sort_edges_ins_sort(p, n);
+}
+
+NK_INTERN void
+nk_tt__rasterize(struct nk_tt__bitmap *result, struct nk_tt__point *pts,
+    int *wcount, int windings, float scale_x, float scale_y,
+    float shift_x, float shift_y, int off_x, int off_y, int invert,
+    struct nk_allocator *alloc)
+{
+    float y_scale_inv = invert ? -scale_y : scale_y;
+    struct nk_tt__edge *e;
+    int n,i,j,k,m;
+    int vsubsample = 1;
+    /* vsubsample should divide 255 evenly; otherwise we won't reach full opacity */
+
+    /* now we have to blow out the windings into explicit edge lists */
+    n = 0;
+    for (i=0; i < windings; ++i)
+        n += wcount[i];
+
+    e = (struct nk_tt__edge*)
+       alloc->alloc(alloc->userdata, 0,(sizeof(*e) * (nk_size)(n+1)));
+    if (e == 0) return;
+    n = 0;
+
+    m=0;
+    for (i=0; i < windings; ++i)
+    {
+        struct nk_tt__point *p = pts + m;
+        m += wcount[i];
+        j = wcount[i]-1;
+        for (k=0; k < wcount[i]; j=k++) {
+            int a=k,b=j;
+            /* skip the edge if horizontal */
+            if (p[j].y == p[k].y)
+                continue;
+
+            /* add edge from j to k to the list */
+            e[n].invert = 0;
+            if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) {
+                e[n].invert = 1;
+                a=j,b=k;
+            }
+            e[n].x0 = p[a].x * scale_x + shift_x;
+            e[n].y0 = (p[a].y * y_scale_inv + shift_y) * (float)vsubsample;
+            e[n].x1 = p[b].x * scale_x + shift_x;
+            e[n].y1 = (p[b].y * y_scale_inv + shift_y) * (float)vsubsample;
+            ++n;
+        }
+    }
+
+    /* now sort the edges by their highest point (should snap to integer, and then by x) */
+    /*STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); */
+    nk_tt__sort_edges(e, n);
+    /* now, traverse the scanlines and find the intersections on each scanline, use xor winding rule */
+    nk_tt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, alloc);
+    alloc->free(alloc->userdata, e);
+}
+
+NK_INTERN void
+nk_tt__add_point(struct nk_tt__point *points, int n, float x, float y)
+{
+    if (!points) return; /* during first pass, it's unallocated */
+    points[n].x = x;
+    points[n].y = y;
+}
+
+NK_INTERN int
+nk_tt__tesselate_curve(struct nk_tt__point *points, int *num_points,
+    float x0, float y0, float x1, float y1, float x2, float y2,
+    float objspace_flatness_squared, int n)
+{
+    /* tesselate until threshold p is happy...
+     * @TODO warped to compensate for non-linear stretching */
+    /* midpoint */
+    float mx = (x0 + 2*x1 + x2)/4;
+    float my = (y0 + 2*y1 + y2)/4;
+    /* versus directly drawn line */
+    float dx = (x0+x2)/2 - mx;
+    float dy = (y0+y2)/2 - my;
+    if (n > 16) /* 65536 segments on one curve better be enough! */
+        return 1;
+
+    /* half-pixel error allowed... need to be smaller if AA */
+    if (dx*dx+dy*dy > objspace_flatness_squared) {
+        nk_tt__tesselate_curve(points, num_points, x0,y0,
+            (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);
+        nk_tt__tesselate_curve(points, num_points, mx,my,
+            (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);
+    } else {
+        nk_tt__add_point(points, *num_points,x2,y2);
+        *num_points = *num_points+1;
+    }
+    return 1;
+}
+
+/* returns number of contours */
+NK_INTERN struct nk_tt__point*
+nk_tt_FlattenCurves(struct nk_tt_vertex *vertices, int num_verts,
+    float objspace_flatness, int **contour_lengths, int *num_contours,
+    struct nk_allocator *alloc)
+{
+    struct nk_tt__point *points=0;
+    int num_points=0;
+    float objspace_flatness_squared = objspace_flatness * objspace_flatness;
+    int i;
+    int n=0;
+    int start=0;
+    int pass;
+
+    /* count how many "moves" there are to get the contour count */
+    for (i=0; i < num_verts; ++i)
+        if (vertices[i].type == NK_TT_vmove) ++n;
+
+    *num_contours = n;
+    if (n == 0) return 0;
+
+    *contour_lengths = (int *)
+        alloc->alloc(alloc->userdata,0, (sizeof(**contour_lengths) * (nk_size)n));
+    if (*contour_lengths == 0) {
+        *num_contours = 0;
+        return 0;
+    }
+
+    /* make two passes through the points so we don't need to realloc */
+    for (pass=0; pass < 2; ++pass)
+    {
+        float x=0,y=0;
+        if (pass == 1) {
+            points = (struct nk_tt__point *)
+                alloc->alloc(alloc->userdata,0, (nk_size)num_points * sizeof(points[0]));
+            if (points == 0) goto error;
+        }
+        num_points = 0;
+        n= -1;
+
+        for (i=0; i < num_verts; ++i)
+        {
+            switch (vertices[i].type) {
+            case NK_TT_vmove:
+                /* start the next contour */
+                if (n >= 0)
+                (*contour_lengths)[n] = num_points - start;
+                ++n;
+                start = num_points;
+
+                x = vertices[i].x, y = vertices[i].y;
+                nk_tt__add_point(points, num_points++, x,y);
+                break;
+            case NK_TT_vline:
+               x = vertices[i].x, y = vertices[i].y;
+               nk_tt__add_point(points, num_points++, x, y);
+               break;
+            case NK_TT_vcurve:
+               nk_tt__tesselate_curve(points, &num_points, x,y,
+                                        vertices[i].cx, vertices[i].cy,
+                                        vertices[i].x,  vertices[i].y,
+                                        objspace_flatness_squared, 0);
+               x = vertices[i].x, y = vertices[i].y;
+               break;
+            default: break;
+         }
+      }
+      (*contour_lengths)[n] = num_points - start;
+   }
+   return points;
+
+error:
+   alloc->free(alloc->userdata, points);
+   alloc->free(alloc->userdata, *contour_lengths);
+   *contour_lengths = 0;
+   *num_contours = 0;
+   return 0;
+}
+
+NK_INTERN void
+nk_tt_Rasterize(struct nk_tt__bitmap *result, float flatness_in_pixels,
+    struct nk_tt_vertex *vertices, int num_verts,
+    float scale_x, float scale_y, float shift_x, float shift_y,
+    int x_off, int y_off, int invert, struct nk_allocator *alloc)
+{
+    float scale = scale_x > scale_y ? scale_y : scale_x;
+    int winding_count, *winding_lengths;
+    struct nk_tt__point *windings = nk_tt_FlattenCurves(vertices, num_verts,
+        flatness_in_pixels / scale, &winding_lengths, &winding_count, alloc);
+
+    NK_ASSERT(alloc);
+    if (windings) {
+        nk_tt__rasterize(result, windings, winding_lengths, winding_count,
+            scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, alloc);
+        alloc->free(alloc->userdata, winding_lengths);
+        alloc->free(alloc->userdata, windings);
+    }
+}
+
+NK_INTERN void
+nk_tt_MakeGlyphBitmapSubpixel(const struct nk_tt_fontinfo *info, unsigned char *output,
+    int out_w, int out_h, int out_stride, float scale_x, float scale_y,
+    float shift_x, float shift_y, int glyph, struct nk_allocator *alloc)
+{
+    int ix0,iy0;
+    struct nk_tt_vertex *vertices;
+    int num_verts = nk_tt_GetGlyphShape(info, alloc, glyph, &vertices);
+    struct nk_tt__bitmap gbm;
+
+    nk_tt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x,
+        shift_y, &ix0,&iy0,0,0);
+    gbm.pixels = output;
+    gbm.w = out_w;
+    gbm.h = out_h;
+    gbm.stride = out_stride;
+
+    if (gbm.w && gbm.h)
+        nk_tt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y,
+            shift_x, shift_y, ix0,iy0, 1, alloc);
+    alloc->free(alloc->userdata, vertices);
+}
+
+/*-------------------------------------------------------------
+ *                          Bitmap baking
+ * --------------------------------------------------------------*/
+NK_INTERN int
+nk_tt_PackBegin(struct nk_tt_pack_context *spc, unsigned char *pixels,
+    int pw, int ph, int stride_in_bytes, int padding, struct nk_allocator *alloc)
+{
+    int num_nodes = pw - padding;
+    struct nk_rp_context *context = (struct nk_rp_context *)
+        alloc->alloc(alloc->userdata,0, sizeof(*context));
+    struct nk_rp_node *nodes = (struct nk_rp_node*)
+        alloc->alloc(alloc->userdata,0, (sizeof(*nodes  ) * (nk_size)num_nodes));
+
+    if (context == 0 || nodes == 0) {
+        if (context != 0) alloc->free(alloc->userdata, context);
+        if (nodes   != 0) alloc->free(alloc->userdata, nodes);
+        return 0;
+    }
+
+    spc->width = pw;
+    spc->height = ph;
+    spc->pixels = pixels;
+    spc->pack_info = context;
+    spc->nodes = nodes;
+    spc->padding = padding;
+    spc->stride_in_bytes = (stride_in_bytes != 0) ? stride_in_bytes : pw;
+    spc->h_oversample = 1;
+    spc->v_oversample = 1;
+
+    nk_rp_init_target(context, pw-padding, ph-padding, nodes, num_nodes);
+    if (pixels)
+        NK_MEMSET(pixels, 0, (nk_size)(pw*ph)); /* background of 0 around pixels */
+    return 1;
+}
+
+NK_INTERN void
+nk_tt_PackEnd(struct nk_tt_pack_context *spc, struct nk_allocator *alloc)
+{
+    alloc->free(alloc->userdata, spc->nodes);
+    alloc->free(alloc->userdata, spc->pack_info);
+}
+
+NK_INTERN void
+nk_tt_PackSetOversampling(struct nk_tt_pack_context *spc,
+    unsigned int h_oversample, unsigned int v_oversample)
+{
+   NK_ASSERT(h_oversample <= NK_TT_MAX_OVERSAMPLE);
+   NK_ASSERT(v_oversample <= NK_TT_MAX_OVERSAMPLE);
+   if (h_oversample <= NK_TT_MAX_OVERSAMPLE)
+      spc->h_oversample = h_oversample;
+   if (v_oversample <= NK_TT_MAX_OVERSAMPLE)
+      spc->v_oversample = v_oversample;
+}
+
+NK_INTERN void
+nk_tt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes,
+    int kernel_width)
+{
+    unsigned char buffer[NK_TT_MAX_OVERSAMPLE];
+    int safe_w = w - kernel_width;
+    int j;
+
+    for (j=0; j < h; ++j)
+    {
+        int i;
+        unsigned int total;
+        NK_MEMSET(buffer, 0, (nk_size)kernel_width);
+
+        total = 0;
+
+        /* make kernel_width a constant in common cases so compiler can optimize out the divide */
+        switch (kernel_width) {
+        case 2:
+            for (i=0; i <= safe_w; ++i) {
+                total += (unsigned int)(pixels[i] - buffer[i & NK_TT__OVER_MASK]);
+                buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i];
+                pixels[i] = (unsigned char) (total / 2);
+            }
+            break;
+        case 3:
+            for (i=0; i <= safe_w; ++i) {
+                total += (unsigned int)(pixels[i] - buffer[i & NK_TT__OVER_MASK]);
+                buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i];
+                pixels[i] = (unsigned char) (total / 3);
+            }
+            break;
+        case 4:
+            for (i=0; i <= safe_w; ++i) {
+                total += (unsigned int)pixels[i] - buffer[i & NK_TT__OVER_MASK];
+                buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i];
+                pixels[i] = (unsigned char) (total / 4);
+            }
+            break;
+        case 5:
+            for (i=0; i <= safe_w; ++i) {
+                total += (unsigned int)(pixels[i] - buffer[i & NK_TT__OVER_MASK]);
+                buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i];
+                pixels[i] = (unsigned char) (total / 5);
+            }
+            break;
+        default:
+            for (i=0; i <= safe_w; ++i) {
+                total += (unsigned int)(pixels[i] - buffer[i & NK_TT__OVER_MASK]);
+                buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i];
+                pixels[i] = (unsigned char) (total / (unsigned int)kernel_width);
+            }
+            break;
+        }
+
+        for (; i < w; ++i) {
+            NK_ASSERT(pixels[i] == 0);
+            total -= (unsigned int)(buffer[i & NK_TT__OVER_MASK]);
+            pixels[i] = (unsigned char) (total / (unsigned int)kernel_width);
+        }
+        pixels += stride_in_bytes;
+    }
+}
+
+NK_INTERN void
+nk_tt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes,
+    int kernel_width)
+{
+    unsigned char buffer[NK_TT_MAX_OVERSAMPLE];
+    int safe_h = h - kernel_width;
+    int j;
+
+    for (j=0; j < w; ++j)
+    {
+        int i;
+        unsigned int total;
+        NK_MEMSET(buffer, 0, (nk_size)kernel_width);
+
+        total = 0;
+
+        /* make kernel_width a constant in common cases so compiler can optimize out the divide */
+        switch (kernel_width) {
+        case 2:
+            for (i=0; i <= safe_h; ++i) {
+                total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]);
+                buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes];
+                pixels[i*stride_in_bytes] = (unsigned char) (total / 2);
+            }
+            break;
+         case 3:
+            for (i=0; i <= safe_h; ++i) {
+                total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]);
+                buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes];
+                pixels[i*stride_in_bytes] = (unsigned char) (total / 3);
+            }
+            break;
+         case 4:
+            for (i=0; i <= safe_h; ++i) {
+                total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]);
+                buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes];
+                pixels[i*stride_in_bytes] = (unsigned char) (total / 4);
+            }
+            break;
+         case 5:
+            for (i=0; i <= safe_h; ++i) {
+                total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]);
+                buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes];
+                pixels[i*stride_in_bytes] = (unsigned char) (total / 5);
+            }
+            break;
+         default:
+            for (i=0; i <= safe_h; ++i) {
+                total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]);
+                buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes];
+                pixels[i*stride_in_bytes] = (unsigned char) (total / (unsigned int)kernel_width);
+            }
+            break;
+        }
+
+        for (; i < h; ++i) {
+            NK_ASSERT(pixels[i*stride_in_bytes] == 0);
+            total -= (unsigned int)(buffer[i & NK_TT__OVER_MASK]);
+            pixels[i*stride_in_bytes] = (unsigned char) (total / (unsigned int)kernel_width);
+        }
+        pixels += 1;
+    }
+}
+
+NK_INTERN float
+nk_tt__oversample_shift(int oversample)
+{
+    if (!oversample)
+        return 0.0f;
+
+    /* The prefilter is a box filter of width "oversample", */
+    /* which shifts phase by (oversample - 1)/2 pixels in */
+    /* oversampled space. We want to shift in the opposite */
+    /* direction to counter this. */
+    return (float)-(oversample - 1) / (2.0f * (float)oversample);
+}
+
+/* rects array must be big enough to accommodate all characters in the given ranges */
+NK_INTERN int
+nk_tt_PackFontRangesGatherRects(struct nk_tt_pack_context *spc,
+    struct nk_tt_fontinfo *info, struct nk_tt_pack_range *ranges,
+    int num_ranges, struct nk_rp_rect *rects)
+{
+    int i,j,k;
+    k = 0;
+
+    for (i=0; i < num_ranges; ++i) {
+        float fh = ranges[i].font_size;
+        float scale = (fh > 0) ? nk_tt_ScaleForPixelHeight(info, fh):
+            nk_tt_ScaleForMappingEmToPixels(info, -fh);
+        ranges[i].h_oversample = (unsigned char) spc->h_oversample;
+        ranges[i].v_oversample = (unsigned char) spc->v_oversample;
+        for (j=0; j < ranges[i].num_chars; ++j) {
+            int x0,y0,x1,y1;
+            int codepoint = ranges[i].first_unicode_codepoint_in_range ?
+                ranges[i].first_unicode_codepoint_in_range + j :
+                ranges[i].array_of_unicode_codepoints[j];
+
+            int glyph = nk_tt_FindGlyphIndex(info, codepoint);
+            nk_tt_GetGlyphBitmapBoxSubpixel(info,glyph, scale * (float)spc->h_oversample,
+                scale * (float)spc->v_oversample, 0,0, &x0,&y0,&x1,&y1);
+            rects[k].w = (nk_rp_coord) (x1-x0 + spc->padding + (int)spc->h_oversample-1);
+            rects[k].h = (nk_rp_coord) (y1-y0 + spc->padding + (int)spc->v_oversample-1);
+            ++k;
+        }
+    }
+    return k;
+}
+
+NK_INTERN int
+nk_tt_PackFontRangesRenderIntoRects(struct nk_tt_pack_context *spc,
+    struct nk_tt_fontinfo *info, struct nk_tt_pack_range *ranges,
+    int num_ranges, struct nk_rp_rect *rects, struct nk_allocator *alloc)
+{
+    int i,j,k, return_value = 1;
+    /* save current values */
+    int old_h_over = (int)spc->h_oversample;
+    int old_v_over = (int)spc->v_oversample;
+    /* rects array must be big enough to accommodate all characters in the given ranges */
+
+    k = 0;
+    for (i=0; i < num_ranges; ++i)
+    {
+        float fh = ranges[i].font_size;
+        float recip_h,recip_v,sub_x,sub_y;
+        float scale = fh > 0 ? nk_tt_ScaleForPixelHeight(info, fh):
+            nk_tt_ScaleForMappingEmToPixels(info, -fh);
+
+        spc->h_oversample = ranges[i].h_oversample;
+        spc->v_oversample = ranges[i].v_oversample;
+
+        recip_h = 1.0f / (float)spc->h_oversample;
+        recip_v = 1.0f / (float)spc->v_oversample;
+
+        sub_x = nk_tt__oversample_shift((int)spc->h_oversample);
+        sub_y = nk_tt__oversample_shift((int)spc->v_oversample);
+
+        for (j=0; j < ranges[i].num_chars; ++j)
+        {
+            struct nk_rp_rect *r = &rects[k];
+            if (r->was_packed)
+            {
+                struct nk_tt_packedchar *bc = &ranges[i].chardata_for_range[j];
+                int advance, lsb, x0,y0,x1,y1;
+                int codepoint = ranges[i].first_unicode_codepoint_in_range ?
+                    ranges[i].first_unicode_codepoint_in_range + j :
+                    ranges[i].array_of_unicode_codepoints[j];
+                int glyph = nk_tt_FindGlyphIndex(info, codepoint);
+                nk_rp_coord pad = (nk_rp_coord) spc->padding;
+
+                /* pad on left and top */
+                r->x = (nk_rp_coord)((int)r->x + (int)pad);
+                r->y = (nk_rp_coord)((int)r->y + (int)pad);
+                r->w = (nk_rp_coord)((int)r->w - (int)pad);
+                r->h = (nk_rp_coord)((int)r->h - (int)pad);
+
+                nk_tt_GetGlyphHMetrics(info, glyph, &advance, &lsb);
+                nk_tt_GetGlyphBitmapBox(info, glyph, scale * (float)spc->h_oversample,
+                        (scale * (float)spc->v_oversample), &x0,&y0,&x1,&y1);
+                nk_tt_MakeGlyphBitmapSubpixel(info, spc->pixels + r->x + r->y*spc->stride_in_bytes,
+                    (int)(r->w - spc->h_oversample+1), (int)(r->h - spc->v_oversample+1),
+                    spc->stride_in_bytes, scale * (float)spc->h_oversample,
+                    scale * (float)spc->v_oversample, 0,0, glyph, alloc);
+
+                if (spc->h_oversample > 1)
+                   nk_tt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
+                        r->w, r->h, spc->stride_in_bytes, (int)spc->h_oversample);
+
+                if (spc->v_oversample > 1)
+                   nk_tt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
+                        r->w, r->h, spc->stride_in_bytes, (int)spc->v_oversample);
+
+                bc->x0       = (nk_ushort)  r->x;
+                bc->y0       = (nk_ushort)  r->y;
+                bc->x1       = (nk_ushort) (r->x + r->w);
+                bc->y1       = (nk_ushort) (r->y + r->h);
+                bc->xadvance = scale * (float)advance;
+                bc->xoff     = (float)  x0 * recip_h + sub_x;
+                bc->yoff     = (float)  y0 * recip_v + sub_y;
+                bc->xoff2    = ((float)x0 + r->w) * recip_h + sub_x;
+                bc->yoff2    = ((float)y0 + r->h) * recip_v + sub_y;
+            } else {
+                return_value = 0; /* if any fail, report failure */
+            }
+            ++k;
+        }
+    }
+    /* restore original values */
+    spc->h_oversample = (unsigned int)old_h_over;
+    spc->v_oversample = (unsigned int)old_v_over;
+    return return_value;
+}
+
+NK_INTERN void
+nk_tt_GetPackedQuad(struct nk_tt_packedchar *chardata, int pw, int ph,
+    int char_index, float *xpos, float *ypos, struct nk_tt_aligned_quad *q,
+    int align_to_integer)
+{
+    float ipw = 1.0f / (float)pw, iph = 1.0f / (float)ph;
+    struct nk_tt_packedchar *b = (struct nk_tt_packedchar*)(chardata + char_index);
+    if (align_to_integer) {
+        int tx = nk_ifloorf((*xpos + b->xoff) + 0.5f);
+        int ty = nk_ifloorf((*ypos + b->yoff) + 0.5f);
+
+        float x = (float)tx;
+        float y = (float)ty;
+
+        q->x0 = x;
+        q->y0 = y;
+        q->x1 = x + b->xoff2 - b->xoff;
+        q->y1 = y + b->yoff2 - b->yoff;
+    } else {
+        q->x0 = *xpos + b->xoff;
+        q->y0 = *ypos + b->yoff;
+        q->x1 = *xpos + b->xoff2;
+        q->y1 = *ypos + b->yoff2;
+    }
+    q->s0 = b->x0 * ipw;
+    q->t0 = b->y0 * iph;
+    q->s1 = b->x1 * ipw;
+    q->t1 = b->y1 * iph;
+    *xpos += b->xadvance;
+}
+
+/* -------------------------------------------------------------
+ *
+ *                          FONT BAKING
+ *
+ * --------------------------------------------------------------*/
+struct nk_font_bake_data {
+    struct nk_tt_fontinfo info;
+    struct nk_rp_rect *rects;
+    struct nk_tt_pack_range *ranges;
+    nk_rune range_count;
+};
+
+struct nk_font_baker {
+    struct nk_allocator alloc;
+    struct nk_tt_pack_context spc;
+    struct nk_font_bake_data *build;
+    struct nk_tt_packedchar *packed_chars;
+    struct nk_rp_rect *rects;
+    struct nk_tt_pack_range *ranges;
+};
+
+NK_GLOBAL const nk_size nk_rect_align = NK_ALIGNOF(struct nk_rp_rect);
+NK_GLOBAL const nk_size nk_range_align = NK_ALIGNOF(struct nk_tt_pack_range);
+NK_GLOBAL const nk_size nk_char_align = NK_ALIGNOF(struct nk_tt_packedchar);
+NK_GLOBAL const nk_size nk_build_align = NK_ALIGNOF(struct nk_font_bake_data);
+NK_GLOBAL const nk_size nk_baker_align = NK_ALIGNOF(struct nk_font_baker);
+
+NK_INTERN int
+nk_range_count(const nk_rune *range)
+{
+    const nk_rune *iter = range;
+    NK_ASSERT(range);
+    if (!range) return 0;
+    while (*(iter++) != 0);
+    return (iter == range) ? 0 : (int)((iter - range)/2);
+}
+
+NK_INTERN int
+nk_range_glyph_count(const nk_rune *range, int count)
+{
+    int i = 0;
+    int total_glyphs = 0;
+    for (i = 0; i < count; ++i) {
+        int diff;
+        nk_rune f = range[(i*2)+0];
+        nk_rune t = range[(i*2)+1];
+        NK_ASSERT(t >= f);
+        diff = (int)((t - f) + 1);
+        total_glyphs += diff;
+    }
+    return total_glyphs;
+}
+
+NK_API const nk_rune*
+nk_font_default_glyph_ranges(void)
+{
+    NK_STORAGE const nk_rune ranges[] = {0x0020, 0x00FF, 0};
+    return ranges;
+}
+
+NK_API const nk_rune*
+nk_font_chinese_glyph_ranges(void)
+{
+    NK_STORAGE const nk_rune ranges[] = {
+        0x0020, 0x00FF,
+        0x3000, 0x30FF,
+        0x31F0, 0x31FF,
+        0xFF00, 0xFFEF,
+        0x4e00, 0x9FAF,
+        0
+    };
+    return ranges;
+}
+
+NK_API const nk_rune*
+nk_font_cyrillic_glyph_ranges(void)
+{
+    NK_STORAGE const nk_rune ranges[] = {
+        0x0020, 0x00FF,
+        0x0400, 0x052F,
+        0x2DE0, 0x2DFF,
+        0xA640, 0xA69F,
+        0
+    };
+    return ranges;
+}
+
+NK_API const nk_rune*
+nk_font_korean_glyph_ranges(void)
+{
+    NK_STORAGE const nk_rune ranges[] = {
+        0x0020, 0x00FF,
+        0x3131, 0x3163,
+        0xAC00, 0xD79D,
+        0
+    };
+    return ranges;
+}
+
+NK_INTERN void
+nk_font_baker_memory(nk_size *temp, int *glyph_count,
+    struct nk_font_config *config_list, int count)
+{
+    int range_count = 0;
+    int total_range_count = 0;
+    struct nk_font_config *iter;
+
+    NK_ASSERT(config_list);
+    NK_ASSERT(glyph_count);
+    if (!config_list) {
+        *temp = 0;
+        *glyph_count = 0;
+        return;
+    }
+
+    *glyph_count = 0;
+    if (!config_list->range)
+        config_list->range = nk_font_default_glyph_ranges();
+    for (iter = config_list; iter; iter = iter->next) {
+        range_count = nk_range_count(iter->range);
+        total_range_count += range_count;
+        *glyph_count += nk_range_glyph_count(iter->range, range_count);
+    }
+
+    *temp = (nk_size)*glyph_count * sizeof(struct nk_rp_rect);
+    *temp += (nk_size)total_range_count * sizeof(struct nk_tt_pack_range);
+    *temp += (nk_size)*glyph_count * sizeof(struct nk_tt_packedchar);
+    *temp += (nk_size)count * sizeof(struct nk_font_bake_data);
+    *temp += sizeof(struct nk_font_baker);
+    *temp += nk_rect_align + nk_range_align + nk_char_align;
+    *temp += nk_build_align + nk_baker_align;
+}
+
+NK_INTERN struct nk_font_baker*
+nk_font_baker(void *memory, int glyph_count, int count, struct nk_allocator *alloc)
+{
+    struct nk_font_baker *baker;
+    if (!memory) return 0;
+    /* setup baker inside a memory block  */
+    baker = (struct nk_font_baker*)NK_ALIGN_PTR(memory, nk_baker_align);
+    baker->build = (struct nk_font_bake_data*)NK_ALIGN_PTR((baker + 1), nk_build_align);
+    baker->packed_chars = (struct nk_tt_packedchar*)NK_ALIGN_PTR((baker->build + count), nk_char_align);
+    baker->rects = (struct nk_rp_rect*)NK_ALIGN_PTR((baker->packed_chars + glyph_count), nk_rect_align);
+    baker->ranges = (struct nk_tt_pack_range*)NK_ALIGN_PTR((baker->rects + glyph_count), nk_range_align);
+    baker->alloc = *alloc;
+    return baker;
+}
+
+NK_INTERN int
+nk_font_bake_pack(struct nk_font_baker *baker,
+    nk_size *image_memory, int *width, int *height, struct nk_recti *custom,
+    const struct nk_font_config *config_list, int count,
+    struct nk_allocator *alloc)
+{
+    NK_STORAGE const nk_size max_height = 1024 * 32;
+    const struct nk_font_config *config_iter;
+    int total_glyph_count = 0;
+    int total_range_count = 0;
+    int range_count = 0;
+    int i = 0;
+
+    NK_ASSERT(image_memory);
+    NK_ASSERT(width);
+    NK_ASSERT(height);
+    NK_ASSERT(config_list);
+    NK_ASSERT(count);
+    NK_ASSERT(alloc);
+
+    if (!image_memory || !width || !height || !config_list || !count) return nk_false;
+    for (config_iter = config_list; config_iter; config_iter = config_iter->next) {
+        range_count = nk_range_count(config_iter->range);
+        total_range_count += range_count;
+        total_glyph_count += nk_range_glyph_count(config_iter->range, range_count);
+    }
+
+    /* setup font baker from temporary memory */
+    for (config_iter = config_list; config_iter; config_iter = config_iter->next) {
+        const struct nk_font_config *cfg = config_iter;
+        if (!nk_tt_InitFont(&baker->build[i++].info, (const unsigned char*)cfg->ttf_blob, 0))
+            return nk_false;
+    }
+
+    *height = 0;
+    *width = (total_glyph_count > 1000) ? 1024 : 512;
+    nk_tt_PackBegin(&baker->spc, 0, (int)*width, (int)max_height, 0, 1, alloc);
+    {
+        int input_i = 0;
+        int range_n = 0;
+        int rect_n = 0;
+        int char_n = 0;
+
+        if (custom) {
+            /* pack custom user data first so it will be in the upper left corner*/
+            struct nk_rp_rect custom_space;
+            nk_zero(&custom_space, sizeof(custom_space));
+            custom_space.w = (nk_rp_coord)((custom->w * 2) + 1);
+            custom_space.h = (nk_rp_coord)(custom->h + 1);
+
+            nk_tt_PackSetOversampling(&baker->spc, 1, 1);
+            nk_rp_pack_rects((struct nk_rp_context*)baker->spc.pack_info, &custom_space, 1);
+            *height = NK_MAX(*height, (int)(custom_space.y + custom_space.h));
+
+            custom->x = (short)custom_space.x;
+            custom->y = (short)custom_space.y;
+            custom->w = (short)custom_space.w;
+            custom->h = (short)custom_space.h;
+        }
+
+        /* first font pass: pack all glyphs */
+        for (input_i = 0, config_iter = config_list; input_i < count && config_iter;
+            input_i++, config_iter = config_iter->next)
+        {
+            int n = 0;
+            int glyph_count;
+            const nk_rune *in_range;
+            const struct nk_font_config *cfg = config_iter;
+            struct nk_font_bake_data *tmp = &baker->build[input_i];
+
+            /* count glyphs + ranges in current font */
+            glyph_count = 0; range_count = 0;
+            for (in_range = cfg->range; in_range[0] && in_range[1]; in_range += 2) {
+                glyph_count += (int)(in_range[1] - in_range[0]) + 1;
+                range_count++;
+            }
+
+            /* setup ranges  */
+            tmp->ranges = baker->ranges + range_n;
+            tmp->range_count = (nk_rune)range_count;
+            range_n += range_count;
+            for (i = 0; i < range_count; ++i) {
+                in_range = &cfg->range[i * 2];
+                tmp->ranges[i].font_size = cfg->size;
+                tmp->ranges[i].first_unicode_codepoint_in_range = (int)in_range[0];
+                tmp->ranges[i].num_chars = (int)(in_range[1]- in_range[0]) + 1;
+                tmp->ranges[i].chardata_for_range = baker->packed_chars + char_n;
+                char_n += tmp->ranges[i].num_chars;
+            }
+
+            /* pack */
+            tmp->rects = baker->rects + rect_n;
+            rect_n += glyph_count;
+            nk_tt_PackSetOversampling(&baker->spc, cfg->oversample_h, cfg->oversample_v);
+            n = nk_tt_PackFontRangesGatherRects(&baker->spc, &tmp->info,
+                tmp->ranges, (int)tmp->range_count, tmp->rects);
+            nk_rp_pack_rects((struct nk_rp_context*)baker->spc.pack_info, tmp->rects, (int)n);
+
+            /* texture height */
+            for (i = 0; i < n; ++i) {
+                if (tmp->rects[i].was_packed)
+                    *height = NK_MAX(*height, tmp->rects[i].y + tmp->rects[i].h);
+            }
+        }
+        NK_ASSERT(rect_n == total_glyph_count);
+        NK_ASSERT(char_n == total_glyph_count);
+        NK_ASSERT(range_n == total_range_count);
+    }
+    *height = (int)nk_round_up_pow2((nk_uint)*height);
+    *image_memory = (nk_size)(*width) * (nk_size)(*height);
+    return nk_true;
+}
+
+NK_INTERN void
+nk_font_bake(struct nk_font_baker *baker, void *image_memory, int width, int height,
+    struct nk_font_glyph *glyphs, int glyphs_count,
+    const struct nk_font_config *config_list, int font_count)
+{
+    int input_i = 0;
+    nk_rune glyph_n = 0;
+    const struct nk_font_config *config_iter;
+
+    NK_ASSERT(image_memory);
+    NK_ASSERT(width);
+    NK_ASSERT(height);
+    NK_ASSERT(config_list);
+    NK_ASSERT(baker);
+    NK_ASSERT(font_count);
+    NK_ASSERT(glyphs_count);
+    if (!image_memory || !width || !height || !config_list ||
+        !font_count || !glyphs || !glyphs_count)
+        return;
+
+    /* second font pass: render glyphs */
+    nk_zero(image_memory, (nk_size)((nk_size)width * (nk_size)height));
+    baker->spc.pixels = (unsigned char*)image_memory;
+    baker->spc.height = (int)height;
+    for (input_i = 0, config_iter = config_list; input_i < font_count && config_iter;
+        ++input_i, config_iter = config_iter->next)
+    {
+        const struct nk_font_config *cfg = config_iter;
+        struct nk_font_bake_data *tmp = &baker->build[input_i];
+        nk_tt_PackSetOversampling(&baker->spc, cfg->oversample_h, cfg->oversample_v);
+        nk_tt_PackFontRangesRenderIntoRects(&baker->spc, &tmp->info, tmp->ranges,
+            (int)tmp->range_count, tmp->rects, &baker->alloc);
+    }
+    nk_tt_PackEnd(&baker->spc, &baker->alloc);
+
+    /* third pass: setup font and glyphs */
+    for (input_i = 0, config_iter = config_list; input_i < font_count && config_iter;
+        ++input_i, config_iter = config_iter->next)
+    {
+        nk_size i = 0;
+        int char_idx = 0;
+        nk_rune glyph_count = 0;
+        const struct nk_font_config *cfg = config_iter;
+        struct nk_font_bake_data *tmp = &baker->build[input_i];
+        struct nk_baked_font *dst_font = cfg->font;
+
+        float font_scale = nk_tt_ScaleForPixelHeight(&tmp->info, cfg->size);
+        int unscaled_ascent, unscaled_descent, unscaled_line_gap;
+        nk_tt_GetFontVMetrics(&tmp->info, &unscaled_ascent, &unscaled_descent,
+                                &unscaled_line_gap);
+
+        /* fill baked font */
+        if (!cfg->merge_mode) {
+            dst_font->ranges = cfg->range;
+            dst_font->height = cfg->size;
+            dst_font->ascent = ((float)unscaled_ascent * font_scale);
+            dst_font->descent = ((float)unscaled_descent * font_scale);
+            dst_font->glyph_offset = glyph_n;
+        }
+
+        /* fill own baked font glyph array */
+        for (i = 0; i < tmp->range_count; ++i)
+        {
+            struct nk_tt_pack_range *range = &tmp->ranges[i];
+            for (char_idx = 0; char_idx < range->num_chars; char_idx++)
+            {
+                nk_rune codepoint = 0;
+                float dummy_x = 0, dummy_y = 0;
+                struct nk_tt_aligned_quad q;
+                struct nk_font_glyph *glyph;
+
+                /* query glyph bounds from stb_truetype */
+                const struct nk_tt_packedchar *pc = &range->chardata_for_range[char_idx];
+                if (!pc->x0 && !pc->x1 && !pc->y0 && !pc->y1) continue;
+                codepoint = (nk_rune)(range->first_unicode_codepoint_in_range + char_idx);
+                nk_tt_GetPackedQuad(range->chardata_for_range, (int)width,
+                    (int)height, char_idx, &dummy_x, &dummy_y, &q, 0);
+
+                /* fill own glyph type with data */
+                glyph = &glyphs[dst_font->glyph_offset + (unsigned int)glyph_count];
+                glyph->codepoint = codepoint;
+                glyph->x0 = q.x0; glyph->y0 = q.y0;
+                glyph->x1 = q.x1; glyph->y1 = q.y1;
+                glyph->y0 += (dst_font->ascent + 0.5f);
+                glyph->y1 += (dst_font->ascent + 0.5f);
+                glyph->w = glyph->x1 - glyph->x0 + 0.5f;
+                glyph->h = glyph->y1 - glyph->y0;
+
+                if (cfg->coord_type == NK_COORD_PIXEL) {
+                    glyph->u0 = q.s0 * (float)width;
+                    glyph->v0 = q.t0 * (float)height;
+                    glyph->u1 = q.s1 * (float)width;
+                    glyph->v1 = q.t1 * (float)height;
+                } else {
+                    glyph->u0 = q.s0;
+                    glyph->v0 = q.t0;
+                    glyph->u1 = q.s1;
+                    glyph->v1 = q.t1;
+                }
+                glyph->xadvance = (pc->xadvance + cfg->spacing.x);
+                if (cfg->pixel_snap)
+                    glyph->xadvance = (float)(int)(glyph->xadvance + 0.5f);
+                glyph_count++;
+            }
+        }
+        dst_font->glyph_count = glyph_count;
+        glyph_n += dst_font->glyph_count;
+    }
+}
+
+NK_INTERN void
+nk_font_bake_custom_data(void *img_memory, int img_width, int img_height,
+    struct nk_recti img_dst, const char *texture_data_mask, int tex_width,
+    int tex_height, char white, char black)
+{
+    nk_byte *pixels;
+    int y = 0;
+    int x = 0;
+    int n = 0;
+
+    NK_ASSERT(img_memory);
+    NK_ASSERT(img_width);
+    NK_ASSERT(img_height);
+    NK_ASSERT(texture_data_mask);
+    NK_UNUSED(tex_height);
+    if (!img_memory || !img_width || !img_height || !texture_data_mask)
+        return;
+
+    pixels = (nk_byte*)img_memory;
+    for (y = 0, n = 0; y < tex_height; ++y) {
+        for (x = 0; x < tex_width; ++x, ++n) {
+            const int off0 = ((img_dst.x + x) + (img_dst.y + y) * img_width);
+            const int off1 = off0 + 1 + tex_width;
+            pixels[off0] = (texture_data_mask[n] == white) ? 0xFF : 0x00;
+            pixels[off1] = (texture_data_mask[n] == black) ? 0xFF : 0x00;
+        }
+    }
+}
+
+NK_INTERN void
+nk_font_bake_convert(void *out_memory, int img_width, int img_height,
+    const void *in_memory)
+{
+    int n = 0;
+    nk_rune *dst;
+    const nk_byte *src;
+
+    NK_ASSERT(out_memory);
+    NK_ASSERT(in_memory);
+    NK_ASSERT(img_width);
+    NK_ASSERT(img_height);
+    if (!out_memory || !in_memory || !img_height || !img_width) return;
+
+    dst = (nk_rune*)out_memory;
+    src = (const nk_byte*)in_memory;
+    for (n = (int)(img_width * img_height); n > 0; n--)
+        *dst++ = ((nk_rune)(*src++) << 24) | 0x00FFFFFF;
+}
+
+/* -------------------------------------------------------------
+ *
+ *                          FONT
+ *
+ * --------------------------------------------------------------*/
+NK_INTERN float
+nk_font_text_width(nk_handle handle, float height, const char *text, int len)
+{
+    nk_rune unicode;
+    int text_len  = 0;
+    float text_width = 0;
+    int glyph_len = 0;
+    float scale = 0;
+
+    struct nk_font *font = (struct nk_font*)handle.ptr;
+    NK_ASSERT(font);
+    NK_ASSERT(font->glyphs);
+    if (!font || !text || !len)
+        return 0;
+
+    scale = height/font->info.height;
+    glyph_len = text_len = nk_utf_decode(text, &unicode, (int)len);
+    if (!glyph_len) return 0;
+    while (text_len <= (int)len && glyph_len) {
+        const struct nk_font_glyph *g;
+        if (unicode == NK_UTF_INVALID) break;
+
+        /* query currently drawn glyph information */
+        g = nk_font_find_glyph(font, unicode);
+        text_width += g->xadvance * scale;
+
+        /* offset next glyph */
+        glyph_len = nk_utf_decode(text + text_len, &unicode, (int)len - text_len);
+        text_len += glyph_len;
+    }
+    return text_width;
+}
+
+#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT
+NK_INTERN void
+nk_font_query_font_glyph(nk_handle handle, float height,
+    struct nk_user_font_glyph *glyph, nk_rune codepoint, nk_rune next_codepoint)
+{
+    float scale;
+    const struct nk_font_glyph *g;
+    struct nk_font *font;
+
+    NK_ASSERT(glyph);
+    NK_UNUSED(next_codepoint);
+
+    font = (struct nk_font*)handle.ptr;
+    NK_ASSERT(font);
+    NK_ASSERT(font->glyphs);
+    if (!font || !glyph)
+        return;
+
+    scale = height/font->info.height;
+    g = nk_font_find_glyph(font, codepoint);
+    glyph->width = (g->x1 - g->x0) * scale;
+    glyph->height = (g->y1 - g->y0) * scale;
+    glyph->offset = nk_vec2(g->x0 * scale, g->y0 * scale);
+    glyph->xadvance = (g->xadvance * scale);
+    glyph->uv[0] = nk_vec2(g->u0, g->v0);
+    glyph->uv[1] = nk_vec2(g->u1, g->v1);
+}
+#endif
+
+NK_API const struct nk_font_glyph*
+nk_font_find_glyph(struct nk_font *font, nk_rune unicode)
+{
+    int i = 0;
+    int count;
+    int total_glyphs = 0;
+    const struct nk_font_glyph *glyph = 0;
+
+    NK_ASSERT(font);
+    NK_ASSERT(font->glyphs);
+    NK_ASSERT(font->info.ranges);
+    if (!font || !font->glyphs) return 0;
+
+    glyph = font->fallback;
+    count = nk_range_count(font->info.ranges);
+    for (i = 0; i < count; ++i) {
+        nk_rune f = font->info.ranges[(i*2)+0];
+        nk_rune t = font->info.ranges[(i*2)+1];
+        int diff = (int)((t - f) + 1);
+        if (unicode >= f && unicode <= t)
+            return &font->glyphs[((nk_rune)total_glyphs + (unicode - f))];
+        total_glyphs += diff;
+    }
+    return glyph;
+}
+
+NK_INTERN void
+nk_font_init(struct nk_font *font, float pixel_height,
+    nk_rune fallback_codepoint, struct nk_font_glyph *glyphs,
+    const struct nk_baked_font *baked_font, nk_handle atlas)
+{
+    struct nk_baked_font baked;
+    NK_ASSERT(font);
+    NK_ASSERT(glyphs);
+    NK_ASSERT(baked_font);
+    if (!font || !glyphs || !baked_font)
+        return;
+
+    baked = *baked_font;
+    font->fallback = 0;
+    font->info = baked;
+    font->scale = (float)pixel_height / (float)font->info.height;
+    font->glyphs = &glyphs[baked_font->glyph_offset];
+    font->texture = atlas;
+    font->fallback_codepoint = fallback_codepoint;
+    font->fallback = nk_font_find_glyph(font, fallback_codepoint);
+
+    font->handle.height = font->info.height * font->scale;
+    font->handle.width = nk_font_text_width;
+    font->handle.userdata.ptr = font;
+#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT
+    font->handle.query = nk_font_query_font_glyph;
+    font->handle.texture = font->texture;
+#endif
+}
+
+/* ---------------------------------------------------------------------------
+ *
+ *                          DEFAULT FONT
+ *
+ * ProggyClean.ttf
+ * Copyright (c) 2004, 2005 Tristan Grimmer
+ * MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip)
+ * Download and more information at http://upperbounds.net
+ *-----------------------------------------------------------------------------*/
+#ifdef NK_INCLUDE_DEFAULT_FONT
+
+ #ifdef __clang__
+#pragma clang diagnostic push
+
+#pragma clang diagnostic ignored "-Woverlength-strings"
+#elif defined(__GNUC__) || defined(__GNUG__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Woverlength-strings"
+#endif
+
+NK_GLOBAL const char nk_proggy_clean_ttf_compressed_data_base85[11980+1] =
+    "7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/"
+    "2*>]b(MC;$jPfY.;h^`IWM9<Lh2TlS+f-s$o6Q<BWH`YiU.xfLq$N;$0iR/GX:U(jcW2p/W*q?-qmnUCI;jHSAiFWM.R*kU@C=GH?a9wp8f$e.-4^Qg1)Q-GL(lf(r/7GrRgwV%MS=C#"
+    "`8ND>Qo#t'X#(v#Y9w0#1D$CIf;W'#pWUPXOuxXuU(H9M(1<q-UE31#^-V'8IRUo7Qf./L>=Ke$$'5F%)]0^#0X@U.a<r:QLtFsLcL6##lOj)#.Y5<-R&KgLwqJfLgN&;Q?gI^#DY2uL"
+    "i@^rMl9t=cWq6##weg>$FBjVQTSDgEKnIS7EM9>ZY9w0#L;>>#Mx&4Mvt//L[MkA#W@lK.N'[0#7RL_&#w+F%HtG9M#XL`N&.,GM4Pg;-<nLENhvx>-VsM.M0rJfLH2eTM`*oJMHRC`N"
+    "kfimM2J,W-jXS:)r0wK#@Fge$U>`w'N7G#$#fB#$E^$#:9:hk+eOe--6x)F7*E%?76%^GMHePW-Z5l'&GiF#$956:rS?dA#fiK:)Yr+`&#0j@'DbG&#^$PG.Ll+DNa<XCMKEV*N)LN/N"
+    "*b=%Q6pia-Xg8I$<MR&,VdJe$<(7G;Ckl'&hF;;$<_=X(b.RS%%)###MPBuuE1V:v&cX&#2m#(&cV]`k9OhLMbn%s$G2,B$BfD3X*sp5#l,$R#]x_X1xKX%b5U*[r5iMfUo9U`N99hG)"
+    "tm+/Us9pG)XPu`<0s-)WTt(gCRxIg(%6sfh=ktMKn3j)<6<b5Sk_/0(^]AaN#(p/L>&VZ>1i%h1S9u5o@YaaW$e+b<TWFn/Z:Oh(Cx2$lNEoN^e)#CFY@@I;BOQ*sRwZtZxRcU7uW6CX"
+    "ow0i(?$Q[cjOd[P4d)]>ROPOpxTO7Stwi1::iB1q)C_=dV26J;2,]7op$]uQr@_V7$q^%lQwtuHY]=DX,n3L#0PHDO4f9>dC@O>HBuKPpP*E,N+b3L#lpR/MrTEH.IAQk.a>D[.e;mc."
+    "x]Ip.PH^'/aqUO/$1WxLoW0[iLA<QT;5HKD+@qQ'NQ(3_PLhE48R.qAPSwQ0/WK?Z,[x?-J;jQTWA0X@KJ(_Y8N-:/M74:/-ZpKrUss?d#dZq]DAbkU*JqkL+nwX@@47`5>w=4h(9.`G"
+    "CRUxHPeR`5Mjol(dUWxZa(>STrPkrJiWx`5U7F#.g*jrohGg`cg:lSTvEY/EV_7H4Q9[Z%cnv;JQYZ5q.l7Zeas:HOIZOB?G<Nald$qs]@]L<J7bR*>gv:[7MI2k).'2($5FNP&EQ(,)"
+    "U]W]+fh18.vsai00);D3@4ku5P?DP8aJt+;qUM]=+b'8@;mViBKx0DE[-auGl8:PJ&Dj+M6OC]O^((##]`0i)drT;-7X`=-H3[igUnPG-NZlo.#k@h#=Ork$m>a>$-?Tm$UV(?#P6YY#"
+    "'/###xe7q.73rI3*pP/$1>s9)W,JrM7SN]'/4C#v$U`0#V.[0>xQsH$fEmPMgY2u7Kh(G%siIfLSoS+MK2eTM$=5,M8p`A.;_R%#u[K#$x4AG8.kK/HSB==-'Ie/QTtG?-.*^N-4B/ZM"
+    "_3YlQC7(p7q)&](`6_c)$/*JL(L-^(]$wIM`dPtOdGA,U3:w2M-0<q-]L_?^)1vw'.,MRsqVr.L;aN&#/EgJ)PBc[-f>+WomX2u7lqM2iEumMTcsF?-aT=Z-97UEnXglEn1K-bnEO`gu"
+    "Ft(c%=;Am_Qs@jLooI&NX;]0#j4#F14;gl8-GQpgwhrq8'=l_f-b49'UOqkLu7-##oDY2L(te+Mch&gLYtJ,MEtJfLh'x'M=$CS-ZZ%P]8bZ>#S?YY#%Q&q'3^Fw&?D)UDNrocM3A76/"
+    "/oL?#h7gl85[qW/NDOk%16ij;+:1a'iNIdb-ou8.P*w,v5#EI$TWS>Pot-R*H'-SEpA:g)f+O$%%`kA#G=8RMmG1&O`>to8bC]T&$,n.LoO>29sp3dt-52U%VM#q7'DHpg+#Z9%H[K<L"
+    "%a2E-grWVM3@2=-k22tL]4$##6We'8UJCKE[d_=%wI;'6X-GsLX4j^SgJ$##R*w,vP3wK#iiW&#*h^D&R?jp7+/u&#(AP##XU8c$fSYW-J95_-Dp[g9wcO&#M-h1OcJlc-*vpw0xUX&#"
+    "OQFKNX@QI'IoPp7nb,QU//MQ&ZDkKP)X<WSVL(68uVl&#c'[0#(s1X&xm$Y%B7*K:eDA323j998GXbA#pwMs-jgD$9QISB-A_(aN4xoFM^@C58D0+Q+q3n0#3U1InDjF682-SjMXJK)("
+    "h$hxua_K]ul92%'BOU&#BRRh-slg8KDlr:%L71Ka:.A;%YULjDPmL<LYs8i#XwJOYaKPKc1h:'9Ke,g)b),78=I39B;xiY$bgGw-&.Zi9InXDuYa%G*f2Bq7mn9^#p1vv%#(Wi-;/Z5h"
+    "o;#2:;%d&#x9v68C5g?ntX0X)pT`;%pB3q7mgGN)3%(P8nTd5L7GeA-GL@+%J3u2:(Yf>et`e;)f#Km8&+DC$I46>#Kr]]u-[=99tts1.qb#q72g1WJO81q+eN'03'eM>&1XxY-caEnO"
+    "j%2n8)),?ILR5^.Ibn<-X-Mq7[a82Lq:F&#ce+S9wsCK*x`569E8ew'He]h:sI[2LM$[guka3ZRd6:t%IG:;$%YiJ:Nq=?eAw;/:nnDq0(CYcMpG)qLN4$##&J<j$UpK<Q4a1]MupW^-"
+    "sj_$%[HK%'F####QRZJ::Y3EGl4'@%FkiAOg#p[##O`gukTfBHagL<LHw%q&OV0##F=6/:chIm0@eCP8X]:kFI%hl8hgO@RcBhS-@Qb$%+m=hPDLg*%K8ln(wcf3/'DW-$.lR?n[nCH-"
+    "eXOONTJlh:.RYF%3'p6sq:UIMA945&^HFS87@$EP2iG<-lCO$%c`uKGD3rC$x0BL8aFn--`ke%#HMP'vh1/R&O_J9'um,.<tx[@%wsJk&bUT2`0uMv7gg#qp/ij.L56'hl;.s5CUrxjO"
+    "M7-##.l+Au'A&O:-T72L]P`&=;ctp'XScX*rU.>-XTt,%OVU4)S1+R-#dg0/Nn?Ku1^0f$B*P:Rowwm-`0PKjYDDM'3]d39VZHEl4,.j']Pk-M.h^&:0FACm$maq-&sgw0t7/6(^xtk%"
+    "LuH88Fj-ekm>GA#_>568x6(OFRl-IZp`&b,_P'$M<Jnq79VsJW/mWS*PUiq76;]/NM_>hLbxfc$mj`,O;&%W2m`Zh:/)Uetw:aJ%]K9h:TcF]u_-Sj9,VK3M.*'&0D[Ca]J9gp8,kAW]"
+    "%(?A%R$f<->Zts'^kn=-^@c4%-pY6qI%J%1IGxfLU9CP8cbPlXv);C=b),<2mOvP8up,UVf3839acAWAW-W?#ao/^#%KYo8fRULNd2.>%m]UK:n%r$'sw]J;5pAoO_#2mO3n,'=H5(et"
+    "Hg*`+RLgv>=4U8guD$I%D:W>-r5V*%j*W:Kvej.Lp$<M-SGZ':+Q_k+uvOSLiEo(<aD/K<CCc`'Lx>'?;++O'>()jLR-^u68PHm8ZFWe+ej8h:9r6L*0//c&iH&R8pRbA#Kjm%upV1g:"
+    "a_#Ur7FuA#(tRh#.Y5K+@?3<-8m0$PEn;J:rh6?I6uG<-`wMU'ircp0LaE_OtlMb&1#6T.#FDKu#1Lw%u%+GM+X'e?YLfjM[VO0MbuFp7;>Q&#WIo)0@F%q7c#4XAXN-U&VB<HFF*qL("
+    "$/V,;(kXZejWO`<[5?\?ewY(*9=%wDc;,u<'9t3W-(H1th3+G]ucQ]kLs7df($/*JL]@*t7Bu_G3_7mp7<iaQjO@.kLg;x3B0lqp7Hf,^Ze7-##@/c58Mo(3;knp0%)A7?-W+eI'o8)b<"
+    "nKnw'Ho8C=Y>pqB>0ie&jhZ[?iLR@@_AvA-iQC(=ksRZRVp7`.=+NpBC%rh&3]R:8XDmE5^V8O(x<<aG/1N$#FX$0V5Y6x'aErI3I$7x%E`v<-BY,)%-?Psf*l?%C3.mM(=/M0:JxG'?"
+    "7WhH%o'a<-80g0NBxoO(GH<dM]n.+%q@jH?f.UsJ2Ggs&4<-e47&Kl+f//9@`b+?.TeN_&B8Ss?v;^Trk;f#YvJkl&w$]>-+k?'(<S:68tq*WoDfZu';mM?8X[ma8W%*`-=;D.(nc7/;"
+    ")g:T1=^J$&BRV(-lTmNB6xqB[@0*o.erM*<SWF]u2=st-*(6v>^](H.aREZSi,#1:[IXaZFOm<-ui#qUq2$##Ri;u75OK#(RtaW-K-F`S+cF]uN`-KMQ%rP/Xri.LRcB##=YL3BgM/3M"
+    "D?@f&1'BW-)Ju<L25gl8uhVm1hL$##*8###'A3/LkKW+(^rWX?5W_8g)a(m&K8P>#bmmWCMkk&#TR`C,5d>g)F;t,4:@_l8G/5h4vUd%&%950:VXD'QdWoY-F$BtUwmfe$YqL'8(PWX("
+    "P?^@Po3$##`MSs?DWBZ/S>+4%>fX,VWv/w'KD`LP5IbH;rTV>n3cEK8U#bX]l-/V+^lj3;vlMb&[5YQ8#pekX9JP3XUC72L,,?+Ni&co7ApnO*5NK,((W-i:$,kp'UDAO(G0Sq7MVjJs"
+    "bIu)'Z,*[>br5fX^:FPAWr-m2KgL<LUN098kTF&#lvo58=/vjDo;.;)Ka*hLR#/k=rKbxuV`>Q_nN6'8uTG&#1T5g)uLv:873UpTLgH+#FgpH'_o1780Ph8KmxQJ8#H72L4@768@Tm&Q"
+    "h4CB/5OvmA&,Q&QbUoi$a_%3M01H)4x7I^&KQVgtFnV+;[Pc>[m4k//,]1?#`VY[Jr*3&&slRfLiVZJ:]?=K3Sw=[$=uRB?3xk48@aeg<Z'<$#4H)6,>e0jT6'N#(q%.O=?2S]u*(m<-"
+    "V8J'(1)G][68hW$5'q[GC&5j`TE?m'esFGNRM)j,ffZ?-qx8;->g4t*:CIP/[Qap7/9'#(1sao7w-.qNUdkJ)tCF&#B^;xGvn2r9FEPFFFcL@.iFNkTve$m%#QvQS8U@)2Z+3K:AKM5i"
+    "sZ88+dKQ)W6>J%CL<KE>`.d*(B`-n8D9oK<Up]c$X$(,)M8Zt7/[rdkqTgl-0cuGMv'?>-XV1q['-5k'cAZ69e;D_?$ZPP&s^+7])$*$#@QYi9,5P&#9r+$%CE=68>K8r0=dSC%%(@p7"
+    ".m7jilQ02'0-VWAg<a/''3u.=4L$Y)6k/K:_[3=&jvL<L0C/2'v:^;-DIBW,B4E68:kZ;%?8(Q8BH=kO65BW?xSG&#@uU,DS*,?.+(o(#1vCS8#CHF>TlGW'b)Tq7VT9q^*^$$.:&N@@"
+    "$&)WHtPm*5_rO0&e%K&#-30j(E4#'Zb.o/(Tpm$>K'f@[PvFl,hfINTNU6u'0pao7%XUp9]5.>%h`8_=VYbxuel.NTSsJfLacFu3B'lQSu/m6-Oqem8T+oE--$0a/k]uj9EwsG>%veR*"
+    "hv^BFpQj:K'#SJ,sB-'#](j.Lg92rTw-*n%@/;39rrJF,l#qV%OrtBeC6/,;qB3ebNW[?,Hqj2L.1NP&GjUR=1D8QaS3Up&@*9wP?+lo7b?@%'k4`p0Z$22%K3+iCZj?XJN4Nm&+YF]u"
+    "@-W$U%VEQ/,,>>#)D<h#`)h0:<Q6909ua+&VU%n2:cG3FJ-%@Bj-DgLr`Hw&HAKjKjseK</xKT*)B,N9X3]krc12t'pgTV(Lv-tL[xg_%=M_q7a^x?7Ubd>#%8cY#YZ?=,`Wdxu/ae&#"
+    "w6)R89tI#6@s'(6Bf7a&?S=^ZI_kS&ai`&=tE72L_D,;^R)7[$s<Eh#c&)q.MXI%#v9ROa5FZO%sF7q7Nwb&#ptUJ:aqJe$Sl68%.D###EC><?-aF&#RNQv>o8lKN%5/$(vdfq7+ebA#"
+    "u1p]ovUKW&Y%q]'>$1@-[xfn$7ZTp7mM,G,Ko7a&Gu%G[RMxJs[0MM%wci.LFDK)(<c`Q8N)jEIF*+?P2a8g%)$q]o2aH8C&<SibC/q,(e:v;-b#6[$NtDZ84Je2KNvB#$P5?tQ3nt(0"
+    "d=j.LQf./Ll33+(;q3L-w=8dX$#WF&uIJ@-bfI>%:_i2B5CsR8&9Z&#=mPEnm0f`<&c)QL5uJ#%u%lJj+D-r;BoF&#4DoS97h5g)E#o:&S4weDF,9^Hoe`h*L+_a*NrLW-1pG_&2UdB8"
+    "6e%B/:=>)N4xeW.*wft-;$'58-ESqr<b?UI(_%@[P46>#U`'6AQ]m&6/`Z>#S?YY#Vc;r7U2&326d=w&H####?TZ`*4?&.MK?LP8Vxg>$[QXc%QJv92.(Db*B)gb*BM9dM*hJMAo*c&#"
+    "b0v=Pjer]$gG&JXDf->'StvU7505l9$AFvgYRI^&<^b68?j#q9QX4SM'RO#&sL1IM.rJfLUAj221]d##DW=m83u5;'bYx,*Sl0hL(W;;$doB&O/TQ:(Z^xBdLjL<Lni;''X.`$#8+1GD"
+    ":k$YUWsbn8ogh6rxZ2Z9]%nd+>V#*8U_72Lh+2Q8Cj0i:6hp&$C/:p(HK>T8Y[gHQ4`4)'$Ab(Nof%V'8hL&#<NEdtg(n'=S1A(Q1/I&4([%dM`,Iu'1:_hL>SfD07&6D<fp8dHM7/g+"
+    "tlPN9J*rKaPct&?'uBCem^jn%9_K)<,C5K3s=5g&GmJb*[SYq7K;TRLGCsM-$$;S%:Y@r7AK0pprpL<Lrh,q7e/%KWK:50I^+m'vi`3?%Zp+<-d+$L-Sv:@.o19n$s0&39;kn;S%BSq*"
+    "$3WoJSCLweV[aZ'MQIjO<7;X-X;&+dMLvu#^UsGEC9WEc[X(wI7#2.(F0jV*eZf<-Qv3J-c+J5AlrB#$p(H68LvEA'q3n0#m,[`*8Ft)FcYgEud]CWfm68,(aLA$@EFTgLXoBq/UPlp7"
+    ":d[/;r_ix=:TF`S5H-b<LI&HY(K=h#)]Lk$K14lVfm:x$H<3^Ql<M`$OhapBnkup'D#L$Pb_`N*g]2e;X/Dtg,bsj&K#2[-:iYr'_wgH)NUIR8a1n#S?Yej'h8^58UbZd+^FKD*T@;6A"
+    "7aQC[K8d-(v6GI$x:T<&'Gp5Uf>@M.*J:;$-rv29'M]8qMv-tLp,'886iaC=Hb*YJoKJ,(j%K=H`K.v9HggqBIiZu'QvBT.#=)0ukruV&.)3=(^1`o*Pj4<-<aN((^7('#Z0wK#5GX@7"
+    "u][`*S^43933A4rl][`*O4CgLEl]v$1Q3AeF37dbXk,.)vj#x'd`;qgbQR%FW,2(?LO=s%Sc68%NP'##Aotl8x=BE#j1UD([3$M(]UI2LX3RpKN@;/#f'f/&_mt&F)XdF<9t4)Qa.*kT"
+    "LwQ'(TTB9.xH'>#MJ+gLq9-##@HuZPN0]u:h7.T..G:;$/Usj(T7`Q8tT72LnYl<-qx8;-HV7Q-&Xdx%1a,hC=0u+HlsV>nuIQL-5<N?)NBS)QN*_I,?&)2'IM%L3I)X((e/dl2&8'<M"
+    ":^#M*Q+[T.Xri.LYS3v%fF`68h;b-X[/En'CR.q7E)p'/kle2HM,u;^%OKC-N+Ll%F9CF<Nf'^#t2L,;27W:0O@6##U6W7:$rJfLWHj$#)woqBefIZ.PK<b*t7ed;p*_m;4ExK#h@&]>"
+    "_>@kXQtMacfD.m-VAb8;IReM3$wf0''hra*so568'Ip&vRs849'MRYSp%:t:h5qSgwpEr$B>Q,;s(C#$)`svQuF$##-D,##,g68@2[T;.XSdN9Qe)rpt._K-#5wF)sP'##p#C0c%-Gb%"
+    "hd+<-j'Ai*x&&HMkT]C'OSl##5RG[JXaHN;d'uA#x._U;.`PU@(Z3dt4r152@:v,'R.Sj'w#0<-;kPI)FfJ&#AYJ&#//)>-k=m=*XnK$>=)72L]0I%>.G690a:$##<,);?;72#?x9+d;"
+    "^V'9;jY@;)br#q^YQpx:X#Te$Z^'=-=bGhLf:D6&bNwZ9-ZD#n^9HhLMr5G;']d&6'wYmTFmL<LD)F^%[tC'8;+9E#C$g%#5Y>q9wI>P(9mI[>kC-ekLC/R&CH+s'B;K-M6$EB%is00:"
+    "+A4[7xks.LrNk0&E)wILYF@2L'0Nb$+pv<(2.768/FrY&h$^3i&@+G%JT'<-,v`3;_)I9M^AE]CN?Cl2AZg+%4iTpT3<n-&%H%b<FDj2M<hH=&Eh<2Len$b*aTX=-8QxN)k11IM1c^j%"
+    "9s<L<NFSo)B?+<-(GxsF,^-Eh@$4dXhN$+#rxK8'je'D7k`e;)2pYwPA'_p9&@^18ml1^[@g4t*[JOa*[=Qp7(qJ_oOL^('7fB&Hq-:sf,sNj8xq^>$U4O]GKx'm9)b@p7YsvK3w^YR-"
+    "CdQ*:Ir<($u&)#(&?L9Rg3H)4fiEp^iI9O8KnTj,]H?D*r7'M;PwZ9K0E^k&-cpI;.p/6_vwoFMV<->#%Xi.LxVnrU(4&8/P+:hLSKj$#U%]49t'I:rgMi'FL@a:0Y-uA[39',(vbma*"
+    "hU%<-SRF`Tt:542R_VV$p@[p8DV[A,?1839FWdF<TddF<9Ah-6&9tWoDlh]&1SpGMq>Ti1O*H&#(AL8[_P%.M>v^-))qOT*F5Cq0`Ye%+$B6i:7@0IX<N+T+0MlMBPQ*Vj>SsD<U4JHY"
+    "8kD2)2fU/M#$e.)T4,_=8hLim[&);?UkK'-x?'(:siIfL<$pFM`i<?%W(mGDHM%>iWP,##P`%/L<eXi:@Z9C.7o=@(pXdAO/NLQ8lPl+HPOQa8wD8=^GlPa8TKI1CjhsCTSLJM'/Wl>-"
+    "S(qw%sf/@%#B6;/U7K]uZbi^Oc^2n<bhPmUkMw>%t<)'mEVE''n`WnJra$^TKvX5B>;_aSEK',(hwa0:i4G?.Bci.(X[?b*($,=-n<.Q%`(X=?+@Am*Js0&=3bh8K]mL<LoNs'6,'85`"
+    "0?t/'_U59@]ddF<#LdF<eWdF<OuN/45rY<-L@&#+fm>69=Lb,OcZV/);TTm8VI;?%OtJ<(b4mq7M6:u?KRdF<gR@2L=FNU-<b[(9c/ML3m;Z[$oF3g)GAWqpARc=<ROu7cL5l;-[A]%/"
+    "+fsd;l#SafT/f*W]0=O'$(Tb<[)*@e775R-:Yob%g*>l*:xP?Yb.5)%w_I?7uk5JC+FS(m#i'k.'a0i)9<7b'fs'59hq$*5Uhv##pi^8+hIEBF`nvo`;'l0.^S1<-wUK2/Coh58KKhLj"
+    "M=SO*rfO`+qC`W-On.=AJ56>>i2@2LH6A:&5q`?9I3@@'04&p2/LVa*T-4<-i3;M9UvZd+N7>b*eIwg:CC)c<>nO&#<IGe;__.thjZl<%w(Wk2xmp4Q@I#I9,DF]u7-P=.-_:YJ]aS@V"
+    "?6*C()dOp7:WL,b&3Rg/.cmM9&r^>$(>.Z-I&J(Q0Hd5Q%7Co-b`-c<N(6r@ip+AurK<m86QIth*#v;-OBqi+L7wDE-Ir8K['m+DDSLwK&/.?-V%U_%3:qKNu$_b*B-kp7NaD'QdWQPK"
+    "Yq[@>P)hI;*_F]u`Rb[.j8_Q/<&>uu+VsH$sM9TA%?)(vmJ80),P7E>)tjD%2L=-t#fK[%`v=Q8<FfNkgg^oIbah*#8/Qt$F&:K*-(N/'+1vMB,u()-a.VUU*#[e%gAAO(S>WlA2);Sa"
+    ">gXm8YB`1d@K#n]76-a$U,mF<fX]idqd)<3,]J7JmW4`6]uks=4-72L(jEk+:bJ0M^q-8Dm_Z?0olP1C9Sa&H[d&c$ooQUj]Exd*3ZM@-WGW2%s',B-_M%>%Ul:#/'xoFM9QX-$.QN'>"
+    "[%$Z$uF6pA6Ki2O5:8w*vP1<-1`[G,)-m#>0`P&#eb#.3i)rtB61(o'$?X3B</R90;eZ]%Ncq;-Tl]#F>2Qft^ae_5tKL9MUe9b*sLEQ95C&`=G?@Mj=wh*'3E>=-<)Gt*Iw)'QG:`@I"
+    "wOf7&]1i'S01B+Ev/Nac#9S;=;YQpg_6U`*kVY39xK,[/6Aj7:'1Bm-_1EYfa1+o&o4hp7KN_Q(OlIo@S%;jVdn0'1<Vc52=u`3^o-n1'g4v58Hj&6_t7$##?M)c<$bgQ_'SY((-xkA#"
+    "Y(,p'H9rIVY-b,'%bCPF7.J<Up^,(dU1VY*5#WkTU>h19w,WQhLI)3S#f$2(eb,jr*b;3Vw]*7NH%$c4Vs,eD9>XW8?N]o+(*pgC%/72LV-u<Hp,3@e^9UB1J+ak9-TN/mhKPg+AJYd$"
+    "MlvAF_jCK*.O-^(63adMT->W%iewS8W6m2rtCpo'RS1R84=@paTKt)>=%&1[)*vp'u+x,VrwN;&]kuO9JDbg=pO$J*.jVe;u'm0dr9l,<*wMK*Oe=g8lV_KEBFkO'oU]^=[-792#ok,)"
+    "i]lR8qQ2oA8wcRCZ^7w/Njh;?.stX?Q1>S1q4Bn$)K1<-rGdO'$Wr.Lc.CG)$/*JL4tNR/,SVO3,aUw'DJN:)Ss;wGn9A32ijw%FL+Z0Fn.U9;reSq)bmI32U==5ALuG&#Vf1398/pVo"
+    "1*c-(aY168o<`JsSbk-,1N;$>0:OUas(3:8Z972LSfF8eb=c-;>SPw7.6hn3m`9^Xkn(r.qS[0;T%&Qc=+STRxX'q1BNk3&*eu2;&8q$&x>Q#Q7^Tf+6<(d%ZVmj2bDi%.3L2n+4W'$P"
+    "iDDG)g,r%+?,$@?uou5tSe2aN_AQU*<h`e-GI7)?OK2A.d7_c)?wQ5AS@DL3r#7fSkgl6-++D:'A,uq7SvlB$pcpH'q3n0#_%dY#xCpr-l<F0NR@-##FEV6NTF6##$l84N1w?AO>'IAO"
+    "URQ##V^Fv-XFbGM7Fl(N<3DhLGF%q.1rC$#:T__&Pi68%0xi_&[qFJ(77j_&JWoF.V735&T,[R*:xFR*K5>>#`bW-?4Ne_&6Ne_&6Ne_&n`kr-#GJcM6X;uM6X;uM(.a..^2TkL%oR(#"
+    ";u.T%fAr%4tJ8&><1=GHZ_+m9/#H1F^R#SC#*N=BA9(D?v[UiFY>>^8p,KKF.W]L29uLkLlu/+4T<XoIB&hx=T1PcDaB&;HH+-AFr?(m9HZV)FKS8JCw;SD=6[^/DZUL`EUDf]GGlG&>"
+    "w$)F./^n3+rlo+DB;5sIYGNk+i1t-69Jg--0pao7Sm#K)pdHW&;LuDNH@H>#/X-TI(;P>#,Gc>#0Su>#4`1?#8lC?#<xU?#@.i?#D:%@#HF7@#LRI@#P_[@#Tkn@#Xw*A#]-=A#a9OA#"
+    "d<F&#*;G##.GY##2Sl##6`($#:l:$#>xL$#B.`$#F:r$#JF.%#NR@%#R_R%#Vke%#Zww%#_-4&#3^Rh%Sflr-k'MS.o?.5/sWel/wpEM0%3'/1)K^f1-d>G21&v(35>V`39V7A4=onx4"
+    "A1OY5EI0;6Ibgr6M$HS7Q<)58C5w,;WoA*#[%T*#`1g*#d=#+#hI5+#lUG+#pbY+#tnl+#x$),#&1;,#*=M,#.I`,#2Ur,#6b.-#;w[H#iQtA#m^0B#qjBB#uvTB##-hB#'9$C#+E6C#"
+    "/QHC#3^ZC#7jmC#;v)D#?,<D#C8ND#GDaD#KPsD#O]/E#g1A5#KA*1#gC17#MGd;#8(02#L-d3#rWM4#Hga1#,<w0#T.j<#O#'2#CYN1#qa^:#_4m3#o@/=#eG8=#t8J5#`+78#4uI-#"
+    "m3B2#SB[8#Q0@8#i[*9#iOn8#1Nm;#^sN9#qh<9#:=x-#P;K2#$%X9#bC+.#Rg;<#mN=.#MTF.#RZO.#2?)4#Y#(/#[)1/#b;L/#dAU/#0Sv;#lY$0#n`-0#sf60#(F24#wrH0#%/e0#"
+    "TmD<#%JSMFove:CTBEXI:<eh2g)B,3h2^G3i;#d3jD>)4kMYD4lVu`4m`:&5niUA5@(A5BA1]PBB:xlBCC=2CDLXMCEUtiCf&0g2'tN?PGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CP"
+    "GT4CPGT4CPGT4CPGT4CPGT4CPGT4CP-qekC`.9kEg^+F$kwViFJTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5o,^<-28ZI'O?;xp"
+    "O?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xp;7q-#lLYI:xvD=#";
+#endif /* NK_INCLUDE_DEFAULT_FONT */
+
+#define NK_CURSOR_DATA_W 90
+#define NK_CURSOR_DATA_H 27
+NK_GLOBAL const char nk_custom_cursor_data[NK_CURSOR_DATA_W * NK_CURSOR_DATA_H + 1] =
+{
+    "..-         -XXXXXXX-    X    -           X           -XXXXXXX          -          XXXXXXX"
+    "..-         -X.....X-   X.X   -          X.X          -X.....X          -          X.....X"
+    "---         -XXX.XXX-  X...X  -         X...X         -X....X           -           X....X"
+    "X           -  X.X  - X.....X -        X.....X        -X...X            -            X...X"
+    "XX          -  X.X  -X.......X-       X.......X       -X..X.X           -           X.X..X"
+    "X.X         -  X.X  -XXXX.XXXX-       XXXX.XXXX       -X.X X.X          -          X.X X.X"
+    "X..X        -  X.X  -   X.X   -          X.X          -XX   X.X         -         X.X   XX"
+    "X...X       -  X.X  -   X.X   -    XX    X.X    XX    -      X.X        -        X.X      "
+    "X....X      -  X.X  -   X.X   -   X.X    X.X    X.X   -       X.X       -       X.X       "
+    "X.....X     -  X.X  -   X.X   -  X..X    X.X    X..X  -        X.X      -      X.X        "
+    "X......X    -  X.X  -   X.X   - X...XXXXXX.XXXXXX...X -         X.X   XX-XX   X.X         "
+    "X.......X   -  X.X  -   X.X   -X.....................X-          X.X X.X-X.X X.X          "
+    "X........X  -  X.X  -   X.X   - X...XXXXXX.XXXXXX...X -           X.X..X-X..X.X           "
+    "X.........X -XXX.XXX-   X.X   -  X..X    X.X    X..X  -            X...X-X...X            "
+    "X..........X-X.....X-   X.X   -   X.X    X.X    X.X   -           X....X-X....X           "
+    "X......XXXXX-XXXXXXX-   X.X   -    XX    X.X    XX    -          X.....X-X.....X          "
+    "X...X..X    ---------   X.X   -          X.X          -          XXXXXXX-XXXXXXX          "
+    "X..X X..X   -       -XXXX.XXXX-       XXXX.XXXX       ------------------------------------"
+    "X.X  X..X   -       -X.......X-       X.......X       -    XX           XX    -           "
+    "XX    X..X  -       - X.....X -        X.....X        -   X.X           X.X   -           "
+    "      X..X          -  X...X  -         X...X         -  X..X           X..X  -           "
+    "       XX           -   X.X   -          X.X          - X...XXXXXXXXXXXXX...X -           "
+    "------------        -    X    -           X           -X.....................X-           "
+    "                    ----------------------------------- X...XXXXXXXXXXXXX...X -           "
+    "                                                      -  X..X           X..X  -           "
+    "                                                      -   X.X           X.X   -           "
+    "                                                      -    XX           XX    -           "
+};
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#elif defined(__GNUC__) || defined(__GNUG__)
+#pragma GCC diagnostic pop
+#endif
+
+NK_INTERN unsigned int
+nk_decompress_length(unsigned char *input)
+{
+    return (unsigned int)((input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11]);
+}
+
+NK_GLOBAL unsigned char *nk__barrier;
+NK_GLOBAL unsigned char *nk__barrier2;
+NK_GLOBAL unsigned char *nk__barrier3;
+NK_GLOBAL unsigned char *nk__barrier4;
+NK_GLOBAL unsigned char *nk__dout;
+
+NK_INTERN void
+nk__match(unsigned char *data, unsigned int length)
+{
+    /* INVERSE of memmove... write each byte before copying the next...*/
+    NK_ASSERT (nk__dout + length <= nk__barrier);
+    if (nk__dout + length > nk__barrier) { nk__dout += length; return; }
+    if (data < nk__barrier4) { nk__dout = nk__barrier+1; return; }
+    while (length--) *nk__dout++ = *data++;
+}
+
+NK_INTERN void
+nk__lit(unsigned char *data, unsigned int length)
+{
+    NK_ASSERT (nk__dout + length <= nk__barrier);
+    if (nk__dout + length > nk__barrier) { nk__dout += length; return; }
+    if (data < nk__barrier2) { nk__dout = nk__barrier+1; return; }
+    NK_MEMCPY(nk__dout, data, length);
+    nk__dout += length;
+}
+
+#define nk__in2(x)   ((i[x] << 8) + i[(x)+1])
+#define nk__in3(x)   ((i[x] << 16) + nk__in2((x)+1))
+#define nk__in4(x)   ((i[x] << 24) + nk__in3((x)+1))
+
+NK_INTERN unsigned char*
+nk_decompress_token(unsigned char *i)
+{
+    if (*i >= 0x20) { /* use fewer if's for cases that expand small */
+        if (*i >= 0x80)       nk__match(nk__dout-i[1]-1, (unsigned int)i[0] - 0x80 + 1), i += 2;
+        else if (*i >= 0x40)  nk__match(nk__dout-(nk__in2(0) - 0x4000 + 1), (unsigned int)i[2]+1), i += 3;
+        else /* *i >= 0x20 */ nk__lit(i+1, (unsigned int)i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1);
+    } else { /* more ifs for cases that expand large, since overhead is amortized */
+        if (*i >= 0x18)       nk__match(nk__dout-(unsigned int)(nk__in3(0) - 0x180000 + 1), (unsigned int)i[3]+1), i += 4;
+        else if (*i >= 0x10)  nk__match(nk__dout-(unsigned int)(nk__in3(0) - 0x100000 + 1), (unsigned int)nk__in2(3)+1), i += 5;
+        else if (*i >= 0x08)  nk__lit(i+2, (unsigned int)nk__in2(0) - 0x0800 + 1), i += 2 + (nk__in2(0) - 0x0800 + 1);
+        else if (*i == 0x07)  nk__lit(i+3, (unsigned int)nk__in2(1) + 1), i += 3 + (nk__in2(1) + 1);
+        else if (*i == 0x06)  nk__match(nk__dout-(unsigned int)(nk__in3(1)+1), i[4]+1u), i += 5;
+        else if (*i == 0x04)  nk__match(nk__dout-(unsigned int)(nk__in3(1)+1), (unsigned int)nk__in2(4)+1u), i += 6;
+    }
+    return i;
+}
+
+NK_INTERN unsigned int
+nk_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen)
+{
+    const unsigned long ADLER_MOD = 65521;
+    unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16;
+    unsigned long blocklen, i;
+
+    blocklen = buflen % 5552;
+    while (buflen) {
+        for (i=0; i + 7 < blocklen; i += 8) {
+            s1 += buffer[0]; s2 += s1;
+            s1 += buffer[1]; s2 += s1;
+            s1 += buffer[2]; s2 += s1;
+            s1 += buffer[3]; s2 += s1;
+            s1 += buffer[4]; s2 += s1;
+            s1 += buffer[5]; s2 += s1;
+            s1 += buffer[6]; s2 += s1;
+            s1 += buffer[7]; s2 += s1;
+            buffer += 8;
+        }
+        for (; i < blocklen; ++i) {
+            s1 += *buffer++; s2 += s1;
+        }
+
+        s1 %= ADLER_MOD; s2 %= ADLER_MOD;
+        buflen -= (unsigned int)blocklen;
+        blocklen = 5552;
+    }
+    return (unsigned int)(s2 << 16) + (unsigned int)s1;
+}
+
+NK_INTERN unsigned int
+nk_decompress(unsigned char *output, unsigned char *i, unsigned int length)
+{
+    unsigned int olen;
+    if (nk__in4(0) != 0x57bC0000) return 0;
+    if (nk__in4(4) != 0)          return 0; /* error! stream is > 4GB */
+    olen = nk_decompress_length(i);
+    nk__barrier2 = i;
+    nk__barrier3 = i+length;
+    nk__barrier = output + olen;
+    nk__barrier4 = output;
+    i += 16;
+
+    nk__dout = output;
+    for (;;) {
+        unsigned char *old_i = i;
+        i = nk_decompress_token(i);
+        if (i == old_i) {
+            if (*i == 0x05 && i[1] == 0xfa) {
+                NK_ASSERT(nk__dout == output + olen);
+                if (nk__dout != output + olen) return 0;
+                if (nk_adler32(1, output, olen) != (unsigned int) nk__in4(2))
+                    return 0;
+                return olen;
+            } else {
+                NK_ASSERT(0); /* NOTREACHED */
+                return 0;
+            }
+        }
+        NK_ASSERT(nk__dout <= output + olen);
+        if (nk__dout > output + olen)
+            return 0;
+    }
+}
+
+NK_INTERN unsigned int
+nk_decode_85_byte(char c)
+{ return (unsigned int)((c >= '\\') ? c-36 : c-35); }
+
+NK_INTERN void
+nk_decode_85(unsigned char* dst, const unsigned char* src)
+{
+    while (*src)
+    {
+        unsigned int tmp =
+            nk_decode_85_byte((char)src[0]) +
+            85 * (nk_decode_85_byte((char)src[1]) +
+            85 * (nk_decode_85_byte((char)src[2]) +
+            85 * (nk_decode_85_byte((char)src[3]) +
+            85 * nk_decode_85_byte((char)src[4]))));
+
+        /* we can't assume little-endianess. */
+        dst[0] = (unsigned char)((tmp >> 0) & 0xFF);
+        dst[1] = (unsigned char)((tmp >> 8) & 0xFF);
+        dst[2] = (unsigned char)((tmp >> 16) & 0xFF);
+        dst[3] = (unsigned char)((tmp >> 24) & 0xFF);
+
+        src += 5;
+        dst += 4;
+    }
+}
+
+/* -------------------------------------------------------------
+ *
+ *                          FONT ATLAS
+ *
+ * --------------------------------------------------------------*/
+NK_API struct nk_font_config
+nk_font_config(float pixel_height)
+{
+    struct nk_font_config cfg;
+    nk_zero_struct(cfg);
+    cfg.ttf_blob = 0;
+    cfg.ttf_size = 0;
+    cfg.ttf_data_owned_by_atlas = 0;
+    cfg.size = pixel_height;
+    cfg.oversample_h = 3;
+    cfg.oversample_v = 1;
+    cfg.pixel_snap = 0;
+    cfg.coord_type = NK_COORD_UV;
+    cfg.spacing = nk_vec2(0,0);
+    cfg.range = nk_font_default_glyph_ranges();
+    cfg.merge_mode = 0;
+    cfg.fallback_glyph = '?';
+    cfg.font = 0;
+    return cfg;
+}
+
+#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR
+NK_API void
+nk_font_atlas_init_default(struct nk_font_atlas *atlas)
+{
+    NK_ASSERT(atlas);
+    if (!atlas) return;
+    nk_zero_struct(*atlas);
+    atlas->temporary.userdata.ptr = 0;
+    atlas->temporary.alloc = nk_malloc;
+    atlas->temporary.free = nk_mfree;
+    atlas->permanent.userdata.ptr = 0;
+    atlas->permanent.alloc = nk_malloc;
+    atlas->permanent.free = nk_mfree;
+}
+#endif
+
+NK_API void
+nk_font_atlas_init(struct nk_font_atlas *atlas, struct nk_allocator *alloc)
+{
+    NK_ASSERT(atlas);
+    NK_ASSERT(alloc);
+    if (!atlas || !alloc) return;
+    nk_zero_struct(*atlas);
+    atlas->permanent = *alloc;
+    atlas->temporary = *alloc;
+}
+
+NK_API void
+nk_font_atlas_init_custom(struct nk_font_atlas *atlas,
+    struct nk_allocator *permanent, struct nk_allocator *temporary)
+{
+    NK_ASSERT(atlas);
+    NK_ASSERT(permanent);
+    NK_ASSERT(temporary);
+    if (!atlas || !permanent || !temporary) return;
+    nk_zero_struct(*atlas);
+    atlas->permanent = *permanent;
+    atlas->temporary = *temporary;
+}
+
+NK_API void
+nk_font_atlas_begin(struct nk_font_atlas *atlas)
+{
+    NK_ASSERT(atlas);
+    NK_ASSERT(atlas->temporary.alloc && atlas->temporary.free);
+    NK_ASSERT(atlas->permanent.alloc && atlas->permanent.free);
+    if (!atlas || !atlas->permanent.alloc || !atlas->permanent.free ||
+        !atlas->temporary.alloc || !atlas->temporary.free) return;
+    if (atlas->glyphs) {
+        atlas->permanent.free(atlas->permanent.userdata, atlas->glyphs);
+        atlas->glyphs = 0;
+    }
+    if (atlas->pixel) {
+        atlas->permanent.free(atlas->permanent.userdata, atlas->pixel);
+        atlas->pixel = 0;
+    }
+}
+
+NK_API struct nk_font*
+nk_font_atlas_add(struct nk_font_atlas *atlas, const struct nk_font_config *config)
+{
+    struct nk_font *font = 0;
+    struct nk_font_config *cfg;
+
+    NK_ASSERT(atlas);
+    NK_ASSERT(atlas->permanent.alloc);
+    NK_ASSERT(atlas->permanent.free);
+    NK_ASSERT(atlas->temporary.alloc);
+    NK_ASSERT(atlas->temporary.free);
+
+    NK_ASSERT(config);
+    NK_ASSERT(config->ttf_blob);
+    NK_ASSERT(config->ttf_size);
+    NK_ASSERT(config->size > 0.0f);
+
+    if (!atlas || !config || !config->ttf_blob || !config->ttf_size || config->size <= 0.0f||
+        !atlas->permanent.alloc || !atlas->permanent.free ||
+        !atlas->temporary.alloc || !atlas->temporary.free)
+        return 0;
+
+    /* allocate and insert font config into list */
+    cfg = (struct nk_font_config*)
+        atlas->permanent.alloc(atlas->permanent.userdata,0, sizeof(struct nk_font_config));
+    NK_MEMCPY(cfg, config, sizeof(*config));
+    if (!atlas->config) {
+        atlas->config = cfg;
+        cfg->next = 0;
+    } else {
+        cfg->next = atlas->config;
+        atlas->config = cfg;
+    }
+
+    /* allocate new font */
+    if (!config->merge_mode) {
+        font = (struct nk_font*)
+            atlas->permanent.alloc(atlas->permanent.userdata,0, sizeof(struct nk_font));
+        NK_ASSERT(font);
+        if (!font) return 0;
+        font->config = cfg;
+    } else {
+        NK_ASSERT(atlas->font_num);
+        font = atlas->fonts;
+        font->config = cfg;
+    }
+
+    /* insert font into list */
+    if (!config->merge_mode) {
+        if (!atlas->fonts) {
+            atlas->fonts = font;
+            font->next = 0;
+        } else {
+            font->next = atlas->fonts;
+            atlas->fonts = font;
+        }
+        cfg->font = &font->info;
+    }
+
+    /* create own copy of .TTF font blob */
+    if (!config->ttf_data_owned_by_atlas) {
+        cfg->ttf_blob = atlas->permanent.alloc(atlas->permanent.userdata,0, cfg->ttf_size);
+        NK_ASSERT(cfg->ttf_blob);
+        if (!cfg->ttf_blob) {
+            atlas->font_num++;
+            return 0;
+        }
+        NK_MEMCPY(cfg->ttf_blob, config->ttf_blob, cfg->ttf_size);
+        cfg->ttf_data_owned_by_atlas = 1;
+    }
+    atlas->font_num++;
+    return font;
+}
+
+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)
+{
+    struct nk_font_config cfg;
+    NK_ASSERT(memory);
+    NK_ASSERT(size);
+
+    NK_ASSERT(atlas);
+    NK_ASSERT(atlas->temporary.alloc);
+    NK_ASSERT(atlas->temporary.free);
+    NK_ASSERT(atlas->permanent.alloc);
+    NK_ASSERT(atlas->permanent.free);
+    if (!atlas || !atlas->temporary.alloc || !atlas->temporary.free || !memory || !size ||
+        !atlas->permanent.alloc || !atlas->permanent.free)
+        return 0;
+
+    cfg = (config) ? *config: nk_font_config(height);
+    cfg.ttf_blob = memory;
+    cfg.ttf_size = size;
+    cfg.size = height;
+    cfg.ttf_data_owned_by_atlas = 0;
+    return nk_font_atlas_add(atlas, &cfg);
+}
+
+#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 *config)
+{
+    nk_size size;
+    char *memory;
+    struct nk_font_config cfg;
+
+    NK_ASSERT(atlas);
+    NK_ASSERT(atlas->temporary.alloc);
+    NK_ASSERT(atlas->temporary.free);
+    NK_ASSERT(atlas->permanent.alloc);
+    NK_ASSERT(atlas->permanent.free);
+
+    if (!atlas || !file_path) return 0;
+    memory = nk_file_load(file_path, &size, &atlas->permanent);
+    if (!memory) return 0;
+
+    cfg = (config) ? *config: nk_font_config(height);
+    cfg.ttf_blob = memory;
+    cfg.ttf_size = size;
+    cfg.size = height;
+    cfg.ttf_data_owned_by_atlas = 1;
+    return nk_font_atlas_add(atlas, &cfg);
+}
+#endif
+
+NK_API struct nk_font*
+nk_font_atlas_add_compressed(struct nk_font_atlas *atlas,
+    void *compressed_data, nk_size compressed_size, float height,
+    const struct nk_font_config *config)
+{
+    unsigned int decompressed_size;
+    void *decompressed_data;
+    struct nk_font_config cfg;
+
+    NK_ASSERT(atlas);
+    NK_ASSERT(atlas->temporary.alloc);
+    NK_ASSERT(atlas->temporary.free);
+    NK_ASSERT(atlas->permanent.alloc);
+    NK_ASSERT(atlas->permanent.free);
+
+    NK_ASSERT(compressed_data);
+    NK_ASSERT(compressed_size);
+    if (!atlas || !compressed_data || !atlas->temporary.alloc || !atlas->temporary.free ||
+        !atlas->permanent.alloc || !atlas->permanent.free)
+        return 0;
+
+    decompressed_size = nk_decompress_length((unsigned char*)compressed_data);
+    decompressed_data = atlas->permanent.alloc(atlas->permanent.userdata,0,decompressed_size);
+    NK_ASSERT(decompressed_data);
+    if (!decompressed_data) return 0;
+    nk_decompress((unsigned char*)decompressed_data, (unsigned char*)compressed_data,
+        (unsigned int)compressed_size);
+
+    cfg = (config) ? *config: nk_font_config(height);
+    cfg.ttf_blob = decompressed_data;
+    cfg.ttf_size = decompressed_size;
+    cfg.size = height;
+    cfg.ttf_data_owned_by_atlas = 1;
+    return nk_font_atlas_add(atlas, &cfg);
+}
+
+NK_API struct nk_font*
+nk_font_atlas_add_compressed_base85(struct nk_font_atlas *atlas,
+    const char *data_base85, float height, const struct nk_font_config *config)
+{
+    int compressed_size;
+    void *compressed_data;
+    struct nk_font *font;
+
+    NK_ASSERT(atlas);
+    NK_ASSERT(atlas->temporary.alloc);
+    NK_ASSERT(atlas->temporary.free);
+    NK_ASSERT(atlas->permanent.alloc);
+    NK_ASSERT(atlas->permanent.free);
+
+    NK_ASSERT(data_base85);
+    if (!atlas || !data_base85 || !atlas->temporary.alloc || !atlas->temporary.free ||
+        !atlas->permanent.alloc || !atlas->permanent.free)
+        return 0;
+
+    compressed_size = (((int)nk_strlen(data_base85) + 4) / 5) * 4;
+    compressed_data = atlas->temporary.alloc(atlas->temporary.userdata,0, (nk_size)compressed_size);
+    NK_ASSERT(compressed_data);
+    if (!compressed_data) return 0;
+    nk_decode_85((unsigned char*)compressed_data, (const unsigned char*)data_base85);
+    font = nk_font_atlas_add_compressed(atlas, compressed_data,
+                    (nk_size)compressed_size, height, config);
+    atlas->temporary.free(atlas->temporary.userdata, compressed_data);
+    return font;
+}
+
+#ifdef NK_INCLUDE_DEFAULT_FONT
+NK_API struct nk_font*
+nk_font_atlas_add_default(struct nk_font_atlas *atlas,
+    float pixel_height, const struct nk_font_config *config)
+{
+    NK_ASSERT(atlas);
+    NK_ASSERT(atlas->temporary.alloc);
+    NK_ASSERT(atlas->temporary.free);
+    NK_ASSERT(atlas->permanent.alloc);
+    NK_ASSERT(atlas->permanent.free);
+    return nk_font_atlas_add_compressed_base85(atlas,
+        nk_proggy_clean_ttf_compressed_data_base85, pixel_height, config);
+}
+#endif
+
+NK_API const void*
+nk_font_atlas_bake(struct nk_font_atlas *atlas, int *width, int *height,
+    enum nk_font_atlas_format fmt)
+{
+    int i = 0;
+    void *tmp = 0;
+    nk_size tmp_size, img_size;
+    struct nk_font *font_iter;
+    struct nk_font_baker *baker;
+
+    NK_ASSERT(atlas);
+    NK_ASSERT(atlas->temporary.alloc);
+    NK_ASSERT(atlas->temporary.free);
+    NK_ASSERT(atlas->permanent.alloc);
+    NK_ASSERT(atlas->permanent.free);
+
+    NK_ASSERT(width);
+    NK_ASSERT(height);
+    if (!atlas || !width || !height ||
+        !atlas->temporary.alloc || !atlas->temporary.free ||
+        !atlas->permanent.alloc || !atlas->permanent.free)
+        return 0;
+
+#ifdef NK_INCLUDE_DEFAULT_FONT
+    /* no font added so just use default font */
+    if (!atlas->font_num)
+        atlas->default_font = nk_font_atlas_add_default(atlas, 13.0f, 0);
+#endif
+    NK_ASSERT(atlas->font_num);
+    if (!atlas->font_num) return 0;
+
+    /* allocate temporary baker memory required for the baking process */
+    nk_font_baker_memory(&tmp_size, &atlas->glyph_count, atlas->config, atlas->font_num);
+    tmp = atlas->temporary.alloc(atlas->temporary.userdata,0, tmp_size);
+    NK_ASSERT(tmp);
+    if (!tmp) goto failed;
+
+    /* allocate glyph memory for all fonts */
+    baker = nk_font_baker(tmp, atlas->glyph_count, atlas->font_num, &atlas->temporary);
+    atlas->glyphs = (struct nk_font_glyph*)atlas->permanent.alloc(
+        atlas->permanent.userdata,0, sizeof(struct nk_font_glyph)*(nk_size)atlas->glyph_count);
+    NK_ASSERT(atlas->glyphs);
+    if (!atlas->glyphs)
+        goto failed;
+
+    /* pack all glyphs into a tight fit space */
+    atlas->custom.w = (NK_CURSOR_DATA_W*2)+1;
+    atlas->custom.h = NK_CURSOR_DATA_H + 1;
+    if (!nk_font_bake_pack(baker, &img_size, width, height, &atlas->custom,
+        atlas->config, atlas->font_num, &atlas->temporary))
+        goto failed;
+
+    /* allocate memory for the baked image font atlas */
+    atlas->pixel = atlas->temporary.alloc(atlas->temporary.userdata,0, img_size);
+    NK_ASSERT(atlas->pixel);
+    if (!atlas->pixel)
+        goto failed;
+
+    /* bake glyphs and custom white pixel into image */
+    nk_font_bake(baker, atlas->pixel, *width, *height,
+        atlas->glyphs, atlas->glyph_count, atlas->config, atlas->font_num);
+    nk_font_bake_custom_data(atlas->pixel, *width, *height, atlas->custom,
+            nk_custom_cursor_data, NK_CURSOR_DATA_W, NK_CURSOR_DATA_H, '.', 'X');
+
+    if (fmt == NK_FONT_ATLAS_RGBA32) {
+        /* convert alpha8 image into rgba32 image */
+        void *img_rgba = atlas->temporary.alloc(atlas->temporary.userdata,0,
+                            (nk_size)(*width * *height * 4));
+        NK_ASSERT(img_rgba);
+        if (!img_rgba) goto failed;
+        nk_font_bake_convert(img_rgba, *width, *height, atlas->pixel);
+        atlas->temporary.free(atlas->temporary.userdata, atlas->pixel);
+        atlas->pixel = img_rgba;
+    }
+    atlas->tex_width = *width;
+    atlas->tex_height = *height;
+
+    /* initialize each font */
+    for (font_iter = atlas->fonts; font_iter; font_iter = font_iter->next) {
+        struct nk_font *font = font_iter;
+        struct nk_font_config *config = font->config;
+        nk_font_init(font, config->size, config->fallback_glyph, atlas->glyphs,
+            config->font, nk_handle_ptr(0));
+    }
+
+    /* initialize each cursor */
+    {NK_STORAGE const struct nk_vec2 nk_cursor_data[NK_CURSOR_COUNT][3] = {
+        /* Pos ----- Size ------- Offset --*/
+        {{ 0, 3},   {12,19},    { 0, 0}},
+        {{13, 0},   { 7,16},    { 4, 8}},
+        {{31, 0},   {23,23},    {11,11}},
+        {{21, 0},   { 9, 23},   { 5,11}},
+        {{55,18},   {23, 9},    {11, 5}},
+        {{73, 0},   {17,17},    { 9, 9}},
+        {{55, 0},   {17,17},    { 9, 9}}
+    };
+    for (i = 0; i < NK_CURSOR_COUNT; ++i) {
+        struct nk_cursor *cursor = &atlas->cursors[i];
+        cursor->img.w = (unsigned short)*width;
+        cursor->img.h = (unsigned short)*height;
+        cursor->img.region[0] = (unsigned short)(atlas->custom.x + nk_cursor_data[i][0].x);
+        cursor->img.region[1] = (unsigned short)(atlas->custom.y + nk_cursor_data[i][0].y);
+        cursor->img.region[2] = (unsigned short)nk_cursor_data[i][1].x;
+        cursor->img.region[3] = (unsigned short)nk_cursor_data[i][1].y;
+        cursor->size = nk_cursor_data[i][1];
+        cursor->offset = nk_cursor_data[i][2];
+    }}
+    /* free temporary memory */
+    atlas->temporary.free(atlas->temporary.userdata, tmp);
+    return atlas->pixel;
+
+failed:
+    /* error so cleanup all memory */
+    if (tmp) atlas->temporary.free(atlas->temporary.userdata, tmp);
+    if (atlas->glyphs) {
+        atlas->permanent.free(atlas->permanent.userdata, atlas->glyphs);
+        atlas->glyphs = 0;
+    }
+    if (atlas->pixel) {
+        atlas->temporary.free(atlas->temporary.userdata, atlas->pixel);
+        atlas->pixel = 0;
+    }
+    return 0;
+}
+
+NK_API void
+nk_font_atlas_end(struct nk_font_atlas *atlas, nk_handle texture,
+    struct nk_draw_null_texture *null)
+{
+    int i = 0;
+    struct nk_font *font_iter;
+    NK_ASSERT(atlas);
+    if (!atlas) {
+        if (!null) return;
+        null->texture = texture;
+        null->uv = nk_vec2(0.5f,0.5f);
+    }
+    if (null) {
+        null->texture = texture;
+        null->uv.x = (atlas->custom.x + 0.5f)/(float)atlas->tex_width;
+        null->uv.y = (atlas->custom.y + 0.5f)/(float)atlas->tex_height;
+    }
+    for (font_iter = atlas->fonts; font_iter; font_iter = font_iter->next) {
+        font_iter->texture = texture;
+#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT
+        font_iter->handle.texture = texture;
+#endif
+    }
+    for (i = 0; i < NK_CURSOR_COUNT; ++i)
+        atlas->cursors[i].img.handle = texture;
+
+    atlas->temporary.free(atlas->temporary.userdata, atlas->pixel);
+    atlas->pixel = 0;
+    atlas->tex_width = 0;
+    atlas->tex_height = 0;
+    atlas->custom.x = 0;
+    atlas->custom.y = 0;
+    atlas->custom.w = 0;
+    atlas->custom.h = 0;
+}
+
+NK_API void
+nk_font_atlas_cleanup(struct nk_font_atlas *atlas)
+{
+    NK_ASSERT(atlas);
+    NK_ASSERT(atlas->temporary.alloc);
+    NK_ASSERT(atlas->temporary.free);
+    NK_ASSERT(atlas->permanent.alloc);
+    NK_ASSERT(atlas->permanent.free);
+
+    if (!atlas || !atlas->permanent.alloc || !atlas->permanent.free) return;
+    if (atlas->config) {
+        struct nk_font_config *iter, *next;
+        for (iter = atlas->config; iter; iter = next) {
+            next = iter->next;
+            atlas->permanent.free(atlas->permanent.userdata, iter->ttf_blob);
+            atlas->permanent.free(atlas->permanent.userdata, iter);
+        }
+        atlas->config = 0;
+    }
+}
+
+NK_API void
+nk_font_atlas_clear(struct nk_font_atlas *atlas)
+{
+    NK_ASSERT(atlas);
+    NK_ASSERT(atlas->temporary.alloc);
+    NK_ASSERT(atlas->temporary.free);
+    NK_ASSERT(atlas->permanent.alloc);
+    NK_ASSERT(atlas->permanent.free);
+    if (!atlas || !atlas->permanent.alloc || !atlas->permanent.free) return;
+
+    nk_font_atlas_cleanup(atlas);
+    if (atlas->fonts) {
+        struct nk_font *iter, *next;
+        for (iter = atlas->fonts; iter; iter = next) {
+            next = iter->next;
+            atlas->permanent.free(atlas->permanent.userdata, iter);
+        }
+        atlas->fonts = 0;
+    }
+    if (atlas->glyphs)
+        atlas->permanent.free(atlas->permanent.userdata, atlas->glyphs);
+    nk_zero_struct(*atlas);
+}
+#endif
+/* ==============================================================
+ *
+ *                          INPUT
+ *
+ * ===============================================================*/
+NK_API void
+nk_input_begin(struct nk_context *ctx)
+{
+    int i;
+    struct nk_input *in;
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    in = &ctx->input;
+    for (i = 0; i < NK_BUTTON_MAX; ++i)
+        in->mouse.buttons[i].clicked = 0;
+
+    in->keyboard.text_len = 0;
+    in->mouse.scroll_delta = nk_vec2(0,0);
+    in->mouse.prev.x = in->mouse.pos.x;
+    in->mouse.prev.y = in->mouse.pos.y;
+    in->mouse.delta.x = 0;
+    in->mouse.delta.y = 0;
+    for (i = 0; i < NK_KEY_MAX; i++)
+        in->keyboard.keys[i].clicked = 0;
+}
+
+NK_API void
+nk_input_end(struct nk_context *ctx)
+{
+    struct nk_input *in;
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    in = &ctx->input;
+    if (in->mouse.grab)
+        in->mouse.grab = 0;
+    if (in->mouse.ungrab) {
+        in->mouse.grabbed = 0;
+        in->mouse.ungrab = 0;
+        in->mouse.grab = 0;
+    }
+}
+
+NK_API void
+nk_input_motion(struct nk_context *ctx, int x, int y)
+{
+    struct nk_input *in;
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    in = &ctx->input;
+    in->mouse.pos.x = (float)x;
+    in->mouse.pos.y = (float)y;
+    in->mouse.delta.x = in->mouse.pos.x - in->mouse.prev.x;
+    in->mouse.delta.y = in->mouse.pos.y - in->mouse.prev.y;
+}
+
+NK_API void
+nk_input_key(struct nk_context *ctx, enum nk_keys key, int down)
+{
+    struct nk_input *in;
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    in = &ctx->input;
+    if (in->keyboard.keys[key].down != down)
+        in->keyboard.keys[key].clicked++;
+    in->keyboard.keys[key].down = down;
+}
+
+NK_API void
+nk_input_button(struct nk_context *ctx, enum nk_buttons id, int x, int y, int down)
+{
+    struct nk_mouse_button *btn;
+    struct nk_input *in;
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    in = &ctx->input;
+    if (in->mouse.buttons[id].down == down) return;
+
+    btn = &in->mouse.buttons[id];
+    btn->clicked_pos.x = (float)x;
+    btn->clicked_pos.y = (float)y;
+    btn->down = down;
+    btn->clicked++;
+}
+
+NK_API void
+nk_input_scroll(struct nk_context *ctx, struct nk_vec2 val)
+{
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    ctx->input.mouse.scroll_delta.x += val.x;
+    ctx->input.mouse.scroll_delta.y += val.y;
+}
+
+NK_API void
+nk_input_glyph(struct nk_context *ctx, const nk_glyph glyph)
+{
+    int len = 0;
+    nk_rune unicode;
+    struct nk_input *in;
+
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    in = &ctx->input;
+
+    len = nk_utf_decode(glyph, &unicode, NK_UTF_SIZE);
+    if (len && ((in->keyboard.text_len + len) < NK_INPUT_MAX)) {
+        nk_utf_encode(unicode, &in->keyboard.text[in->keyboard.text_len],
+            NK_INPUT_MAX - in->keyboard.text_len);
+        in->keyboard.text_len += len;
+    }
+}
+
+NK_API void
+nk_input_char(struct nk_context *ctx, char c)
+{
+    nk_glyph glyph;
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    glyph[0] = c;
+    nk_input_glyph(ctx, glyph);
+}
+
+NK_API void
+nk_input_unicode(struct nk_context *ctx, nk_rune unicode)
+{
+    nk_glyph rune;
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    nk_utf_encode(unicode, rune, NK_UTF_SIZE);
+    nk_input_glyph(ctx, rune);
+}
+
+NK_API int
+nk_input_has_mouse_click(const struct nk_input *i, enum nk_buttons id)
+{
+    const struct nk_mouse_button *btn;
+    if (!i) return nk_false;
+    btn = &i->mouse.buttons[id];
+    return (btn->clicked && btn->down == nk_false) ? nk_true : nk_false;
+}
+
+NK_API int
+nk_input_has_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id,
+    struct nk_rect b)
+{
+    const struct nk_mouse_button *btn;
+    if (!i) return nk_false;
+    btn = &i->mouse.buttons[id];
+    if (!NK_INBOX(btn->clicked_pos.x,btn->clicked_pos.y,b.x,b.y,b.w,b.h))
+        return nk_false;
+    return nk_true;
+}
+
+NK_API int
+nk_input_has_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id,
+    struct nk_rect b, int down)
+{
+    const struct nk_mouse_button *btn;
+    if (!i) return nk_false;
+    btn = &i->mouse.buttons[id];
+    return nk_input_has_mouse_click_in_rect(i, id, b) && (btn->down == down);
+}
+
+NK_API int
+nk_input_is_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id,
+    struct nk_rect b)
+{
+    const struct nk_mouse_button *btn;
+    if (!i) return nk_false;
+    btn = &i->mouse.buttons[id];
+    return (nk_input_has_mouse_click_down_in_rect(i, id, b, nk_false) &&
+            btn->clicked) ? nk_true : nk_false;
+}
+
+NK_API int
+nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id,
+    struct nk_rect b, int down)
+{
+    const struct nk_mouse_button *btn;
+    if (!i) return nk_false;
+    btn = &i->mouse.buttons[id];
+    return (nk_input_has_mouse_click_down_in_rect(i, id, b, down) &&
+            btn->clicked) ? nk_true : nk_false;
+}
+
+NK_API int
+nk_input_any_mouse_click_in_rect(const struct nk_input *in, struct nk_rect b)
+{
+    int i, down = 0;
+    for (i = 0; i < NK_BUTTON_MAX; ++i)
+        down = down || nk_input_is_mouse_click_in_rect(in, (enum nk_buttons)i, b);
+    return down;
+}
+
+NK_API int
+nk_input_is_mouse_hovering_rect(const struct nk_input *i, struct nk_rect rect)
+{
+    if (!i) return nk_false;
+    return NK_INBOX(i->mouse.pos.x, i->mouse.pos.y, rect.x, rect.y, rect.w, rect.h);
+}
+
+NK_API int
+nk_input_is_mouse_prev_hovering_rect(const struct nk_input *i, struct nk_rect rect)
+{
+    if (!i) return nk_false;
+    return NK_INBOX(i->mouse.prev.x, i->mouse.prev.y, rect.x, rect.y, rect.w, rect.h);
+}
+
+NK_API int
+nk_input_mouse_clicked(const struct nk_input *i, enum nk_buttons id, struct nk_rect rect)
+{
+    if (!i) return nk_false;
+    if (!nk_input_is_mouse_hovering_rect(i, rect)) return nk_false;
+    return nk_input_is_mouse_click_in_rect(i, id, rect);
+}
+
+NK_API int
+nk_input_is_mouse_down(const struct nk_input *i, enum nk_buttons id)
+{
+    if (!i) return nk_false;
+    return i->mouse.buttons[id].down;
+}
+
+NK_API int
+nk_input_is_mouse_pressed(const struct nk_input *i, enum nk_buttons id)
+{
+    const struct nk_mouse_button *b;
+    if (!i) return nk_false;
+    b = &i->mouse.buttons[id];
+    if (b->down && b->clicked)
+        return nk_true;
+    return nk_false;
+}
+
+NK_API int
+nk_input_is_mouse_released(const struct nk_input *i, enum nk_buttons id)
+{
+    if (!i) return nk_false;
+    return (!i->mouse.buttons[id].down && i->mouse.buttons[id].clicked);
+}
+
+NK_API int
+nk_input_is_key_pressed(const struct nk_input *i, enum nk_keys key)
+{
+    const struct nk_key *k;
+    if (!i) return nk_false;
+    k = &i->keyboard.keys[key];
+    if ((k->down && k->clicked) || (!k->down && k->clicked >= 2))
+        return nk_true;
+    return nk_false;
+}
+
+NK_API int
+nk_input_is_key_released(const struct nk_input *i, enum nk_keys key)
+{
+    const struct nk_key *k;
+    if (!i) return nk_false;
+    k = &i->keyboard.keys[key];
+    if ((!k->down && k->clicked) || (k->down && k->clicked >= 2))
+        return nk_true;
+    return nk_false;
+}
+
+NK_API int
+nk_input_is_key_down(const struct nk_input *i, enum nk_keys key)
+{
+    const struct nk_key *k;
+    if (!i) return nk_false;
+    k = &i->keyboard.keys[key];
+    if (k->down) return nk_true;
+    return nk_false;
+}
+
+/*
+ * ==============================================================
+ *
+ *                          TEXT EDITOR
+ *
+ * ===============================================================
+ */
+/* stb_textedit.h - v1.8  - public domain - Sean Barrett */
+struct nk_text_find {
+   float x,y;    /* position of n'th character */
+   float height; /* height of line */
+   int first_char, length; /* first char of row, and length */
+   int prev_first;  /*_ first char of previous row */
+};
+
+struct nk_text_edit_row {
+   float x0,x1;
+   /* starting x location, end x location (allows for align=right, etc) */
+   float baseline_y_delta;
+   /* position of baseline relative to previous row's baseline*/
+   float ymin,ymax;
+   /* height of row above and below baseline */
+   int num_chars;
+};
+
+/* forward declarations */
+NK_INTERN void nk_textedit_makeundo_delete(struct nk_text_edit*, int, int);
+NK_INTERN void nk_textedit_makeundo_insert(struct nk_text_edit*, int, int);
+NK_INTERN void nk_textedit_makeundo_replace(struct nk_text_edit*, int, int, int);
+#define NK_TEXT_HAS_SELECTION(s)   ((s)->select_start != (s)->select_end)
+
+NK_INTERN float
+nk_textedit_get_width(const struct nk_text_edit *edit, int line_start, int char_id,
+    const struct nk_user_font *font)
+{
+    int len = 0;
+    nk_rune unicode = 0;
+    const char *str = nk_str_at_const(&edit->string, line_start + char_id, &unicode, &len);
+    return font->width(font->userdata, font->height, str, len);
+}
+
+NK_INTERN void
+nk_textedit_layout_row(struct nk_text_edit_row *r, struct nk_text_edit *edit,
+    int line_start_id, float row_height, const struct nk_user_font *font)
+{
+    int l;
+    int glyphs = 0;
+    nk_rune unicode;
+    const char *remaining;
+    int len = nk_str_len_char(&edit->string);
+    const char *end = nk_str_get_const(&edit->string) + len;
+    const char *text = nk_str_at_const(&edit->string, line_start_id, &unicode, &l);
+    const struct nk_vec2 size = nk_text_calculate_text_bounds(font,
+        text, (int)(end - text), row_height, &remaining, 0, &glyphs, NK_STOP_ON_NEW_LINE);
+
+    r->x0 = 0.0f;
+    r->x1 = size.x;
+    r->baseline_y_delta = size.y;
+    r->ymin = 0.0f;
+    r->ymax = size.y;
+    r->num_chars = glyphs;
+}
+
+NK_INTERN int
+nk_textedit_locate_coord(struct nk_text_edit *edit, float x, float y,
+    const struct nk_user_font *font, float row_height)
+{
+    struct nk_text_edit_row r;
+    int n = edit->string.len;
+    float base_y = 0, prev_x;
+    int i=0, k;
+
+    r.x0 = r.x1 = 0;
+    r.ymin = r.ymax = 0;
+    r.num_chars = 0;
+
+    /* search rows to find one that straddles 'y' */
+    while (i < n) {
+        nk_textedit_layout_row(&r, edit, i, row_height, font);
+        if (r.num_chars <= 0)
+            return n;
+
+        if (i==0 && y < base_y + r.ymin)
+            return 0;
+
+        if (y < base_y + r.ymax)
+            break;
+
+        i += r.num_chars;
+        base_y += r.baseline_y_delta;
+    }
+
+    /* below all text, return 'after' last character */
+    if (i >= n)
+        return n;
+
+    /* check if it's before the beginning of the line */
+    if (x < r.x0)
+        return i;
+
+    /* check if it's before the end of the line */
+    if (x < r.x1) {
+        /* search characters in row for one that straddles 'x' */
+        k = i;
+        prev_x = r.x0;
+        for (i=0; i < r.num_chars; ++i) {
+            float w = nk_textedit_get_width(edit, k, i, font);
+            if (x < prev_x+w) {
+                if (x < prev_x+w/2)
+                    return k+i;
+                else return k+i+1;
+            }
+            prev_x += w;
+        }
+        /* shouldn't happen, but if it does, fall through to end-of-line case */
+    }
+
+    /* if the last character is a newline, return that.
+     * otherwise return 'after' the last character */
+    if (nk_str_rune_at(&edit->string, i+r.num_chars-1) == '\n')
+        return i+r.num_chars-1;
+    else return i+r.num_chars;
+}
+
+NK_INTERN void
+nk_textedit_click(struct nk_text_edit *state, float x, float y,
+    const struct nk_user_font *font, float row_height)
+{
+    /* API click: on mouse down, move the cursor to the clicked location,
+     * and reset the selection */
+    state->cursor = nk_textedit_locate_coord(state, x, y, font, row_height);
+    state->select_start = state->cursor;
+    state->select_end = state->cursor;
+    state->has_preferred_x = 0;
+}
+
+NK_INTERN void
+nk_textedit_drag(struct nk_text_edit *state, float x, float y,
+    const struct nk_user_font *font, float row_height)
+{
+    /* API drag: on mouse drag, move the cursor and selection endpoint
+     * to the clicked location */
+    int p = nk_textedit_locate_coord(state, x, y, font, row_height);
+    if (state->select_start == state->select_end)
+        state->select_start = state->cursor;
+    state->cursor = state->select_end = p;
+}
+
+NK_INTERN void
+nk_textedit_find_charpos(struct nk_text_find *find, struct nk_text_edit *state,
+    int n, int single_line, const struct nk_user_font *font, float row_height)
+{
+    /* find the x/y location of a character, and remember info about the previous
+     * row in case we get a move-up event (for page up, we'll have to rescan) */
+    struct nk_text_edit_row r;
+    int prev_start = 0;
+    int z = state->string.len;
+    int i=0, first;
+
+    nk_zero_struct(r);
+    if (n == z) {
+        /* if it's at the end, then find the last line -- simpler than trying to
+        explicitly handle this case in the regular code */
+        nk_textedit_layout_row(&r, state, 0, row_height, font);
+        if (single_line) {
+            find->first_char = 0;
+            find->length = z;
+        } else {
+            while (i < z) {
+                prev_start = i;
+                i += r.num_chars;
+                nk_textedit_layout_row(&r, state, i, row_height, font);
+            }
+
+            find->first_char = i;
+            find->length = r.num_chars;
+        }
+        find->x = r.x1;
+        find->y = r.ymin;
+        find->height = r.ymax - r.ymin;
+        find->prev_first = prev_start;
+        return;
+    }
+
+    /* search rows to find the one that straddles character n */
+    find->y = 0;
+
+    for(;;) {
+        nk_textedit_layout_row(&r, state, i, row_height, font);
+        if (n < i + r.num_chars) break;
+        prev_start = i;
+        i += r.num_chars;
+        find->y += r.baseline_y_delta;
+    }
+
+    find->first_char = first = i;
+    find->length = r.num_chars;
+    find->height = r.ymax - r.ymin;
+    find->prev_first = prev_start;
+
+    /* now scan to find xpos */
+    find->x = r.x0;
+    for (i=0; first+i < n; ++i)
+        find->x += nk_textedit_get_width(state, first, i, font);
+}
+
+NK_INTERN void
+nk_textedit_clamp(struct nk_text_edit *state)
+{
+    /* make the selection/cursor state valid if client altered the string */
+    int n = state->string.len;
+    if (NK_TEXT_HAS_SELECTION(state)) {
+        if (state->select_start > n) state->select_start = n;
+        if (state->select_end   > n) state->select_end = n;
+        /* if clamping forced them to be equal, move the cursor to match */
+        if (state->select_start == state->select_end)
+            state->cursor = state->select_start;
+    }
+    if (state->cursor > n) state->cursor = n;
+}
+
+NK_API void
+nk_textedit_delete(struct nk_text_edit *state, int where, int len)
+{
+    /* delete characters while updating undo */
+    nk_textedit_makeundo_delete(state, where, len);
+    nk_str_delete_runes(&state->string, where, len);
+    state->has_preferred_x = 0;
+}
+
+NK_API void
+nk_textedit_delete_selection(struct nk_text_edit *state)
+{
+    /* delete the section */
+    nk_textedit_clamp(state);
+    if (NK_TEXT_HAS_SELECTION(state)) {
+        if (state->select_start < state->select_end) {
+            nk_textedit_delete(state, state->select_start,
+                state->select_end - state->select_start);
+            state->select_end = state->cursor = state->select_start;
+        } else {
+            nk_textedit_delete(state, state->select_end,
+                state->select_start - state->select_end);
+            state->select_start = state->cursor = state->select_end;
+        }
+        state->has_preferred_x = 0;
+    }
+}
+
+NK_INTERN void
+nk_textedit_sortselection(struct nk_text_edit *state)
+{
+    /* canonicalize the selection so start <= end */
+    if (state->select_end < state->select_start) {
+        int temp = state->select_end;
+        state->select_end = state->select_start;
+        state->select_start = temp;
+    }
+}
+
+NK_INTERN void
+nk_textedit_move_to_first(struct nk_text_edit *state)
+{
+    /* move cursor to first character of selection */
+    if (NK_TEXT_HAS_SELECTION(state)) {
+        nk_textedit_sortselection(state);
+        state->cursor = state->select_start;
+        state->select_end = state->select_start;
+        state->has_preferred_x = 0;
+    }
+}
+
+NK_INTERN void
+nk_textedit_move_to_last(struct nk_text_edit *state)
+{
+    /* move cursor to last character of selection */
+    if (NK_TEXT_HAS_SELECTION(state)) {
+        nk_textedit_sortselection(state);
+        nk_textedit_clamp(state);
+        state->cursor = state->select_end;
+        state->select_start = state->select_end;
+        state->has_preferred_x = 0;
+    }
+}
+
+NK_INTERN int
+nk_is_word_boundary( struct nk_text_edit *state, int idx)
+{
+    int len;
+    nk_rune c;
+    if (idx <= 0) return 1;
+    if (!nk_str_at_rune(&state->string, idx, &c, &len)) return 1;
+    return (c == ' ' || c == '\t' ||c == 0x3000 || c == ',' || c == ';' ||
+            c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']' ||
+            c == '|');
+}
+
+NK_INTERN int
+nk_textedit_move_to_word_previous(struct nk_text_edit *state)
+{
+   int c = state->cursor - 1;
+   while( c >= 0 && !nk_is_word_boundary(state, c))
+      --c;
+
+   if( c < 0 )
+      c = 0;
+
+   return c;
+}
+
+NK_INTERN int
+nk_textedit_move_to_word_next(struct nk_text_edit *state)
+{
+   const int len = state->string.len;
+   int c = state->cursor+1;
+   while( c < len && !nk_is_word_boundary(state, c))
+      ++c;
+
+   if( c > len )
+      c = len;
+
+   return c;
+}
+
+NK_INTERN void
+nk_textedit_prep_selection_at_cursor(struct nk_text_edit *state)
+{
+    /* update selection and cursor to match each other */
+    if (!NK_TEXT_HAS_SELECTION(state))
+        state->select_start = state->select_end = state->cursor;
+    else state->cursor = state->select_end;
+}
+
+NK_API int
+nk_textedit_cut(struct nk_text_edit *state)
+{
+    /* API cut: delete selection */
+    if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
+        return 0;
+    if (NK_TEXT_HAS_SELECTION(state)) {
+        nk_textedit_delete_selection(state); /* implicitly clamps */
+        state->has_preferred_x = 0;
+        return 1;
+    }
+   return 0;
+}
+
+NK_API int
+nk_textedit_paste(struct nk_text_edit *state, char const *ctext, int len)
+{
+    /* API paste: replace existing selection with passed-in text */
+    int glyphs;
+    const char *text = (const char *) ctext;
+    if (state->mode == NK_TEXT_EDIT_MODE_VIEW) return 0;
+
+    /* if there's a selection, the paste should delete it */
+    nk_textedit_clamp(state);
+    nk_textedit_delete_selection(state);
+
+    /* try to insert the characters */
+    glyphs = nk_utf_len(ctext, len);
+    if (nk_str_insert_text_char(&state->string, state->cursor, text, len)) {
+        nk_textedit_makeundo_insert(state, state->cursor, glyphs);
+        state->cursor += len;
+        state->has_preferred_x = 0;
+        return 1;
+    }
+    /* remove the undo since we didn't actually insert the characters */
+    if (state->undo.undo_point)
+        --state->undo.undo_point;
+    return 0;
+}
+
+NK_API void
+nk_textedit_text(struct nk_text_edit *state, const char *text, int total_len)
+{
+    nk_rune unicode;
+    int glyph_len;
+    int text_len = 0;
+
+    NK_ASSERT(state);
+    NK_ASSERT(text);
+    if (!text || !total_len || state->mode == NK_TEXT_EDIT_MODE_VIEW) return;
+
+    glyph_len = nk_utf_decode(text, &unicode, total_len);
+    while ((text_len < total_len) && glyph_len)
+    {
+        /* don't insert a backward delete, just process the event */
+        if (unicode == 127) goto next;
+        /* can't add newline in single-line mode */
+        if (unicode == '\n' && state->single_line) goto next;
+        /* filter incoming text */
+        if (state->filter && !state->filter(state, unicode)) goto next;
+
+        if (!NK_TEXT_HAS_SELECTION(state) &&
+            state->cursor < state->string.len)
+        {
+            if (state->mode == NK_TEXT_EDIT_MODE_REPLACE) {
+                nk_textedit_makeundo_replace(state, state->cursor, 1, 1);
+                nk_str_delete_runes(&state->string, state->cursor, 1);
+            }
+            if (nk_str_insert_text_utf8(&state->string, state->cursor,
+                                        text+text_len, 1))
+            {
+                ++state->cursor;
+                state->has_preferred_x = 0;
+            }
+        } else {
+            nk_textedit_delete_selection(state); /* implicitly clamps */
+            if (nk_str_insert_text_utf8(&state->string, state->cursor,
+                                        text+text_len, 1))
+            {
+                nk_textedit_makeundo_insert(state, state->cursor, 1);
+                ++state->cursor;
+                state->has_preferred_x = 0;
+            }
+        }
+        next:
+        text_len += glyph_len;
+        glyph_len = nk_utf_decode(text + text_len, &unicode, total_len-text_len);
+    }
+}
+
+NK_INTERN void
+nk_textedit_key(struct nk_text_edit *state, enum nk_keys key, int shift_mod,
+    const struct nk_user_font *font, float row_height)
+{
+retry:
+    switch (key)
+    {
+    case NK_KEY_NONE:
+    case NK_KEY_CTRL:
+    case NK_KEY_ENTER:
+    case NK_KEY_SHIFT:
+    case NK_KEY_TAB:
+    case NK_KEY_COPY:
+    case NK_KEY_CUT:
+    case NK_KEY_PASTE:
+    case NK_KEY_MAX:
+    default: break;
+    case NK_KEY_TEXT_UNDO:
+         nk_textedit_undo(state);
+         state->has_preferred_x = 0;
+         break;
+
+    case NK_KEY_TEXT_REDO:
+        nk_textedit_redo(state);
+        state->has_preferred_x = 0;
+        break;
+
+    case NK_KEY_TEXT_SELECT_ALL:
+        nk_textedit_select_all(state);
+        state->has_preferred_x = 0;
+        break;
+
+    case NK_KEY_TEXT_INSERT_MODE:
+        if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
+            state->mode = NK_TEXT_EDIT_MODE_INSERT;
+        break;
+    case NK_KEY_TEXT_REPLACE_MODE:
+        if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
+            state->mode = NK_TEXT_EDIT_MODE_REPLACE;
+        break;
+    case NK_KEY_TEXT_RESET_MODE:
+        if (state->mode == NK_TEXT_EDIT_MODE_INSERT ||
+            state->mode == NK_TEXT_EDIT_MODE_REPLACE)
+            state->mode = NK_TEXT_EDIT_MODE_VIEW;
+        break;
+
+    case NK_KEY_LEFT:
+        if (shift_mod) {
+            nk_textedit_clamp(state);
+            nk_textedit_prep_selection_at_cursor(state);
+            /* move selection left */
+            if (state->select_end > 0)
+                --state->select_end;
+            state->cursor = state->select_end;
+            state->has_preferred_x = 0;
+        } else {
+            /* if currently there's a selection,
+             * move cursor to start of selection */
+            if (NK_TEXT_HAS_SELECTION(state))
+                nk_textedit_move_to_first(state);
+            else if (state->cursor > 0)
+               --state->cursor;
+            state->has_preferred_x = 0;
+        } break;
+
+    case NK_KEY_RIGHT:
+        if (shift_mod) {
+            nk_textedit_prep_selection_at_cursor(state);
+            /* move selection right */
+            ++state->select_end;
+            nk_textedit_clamp(state);
+            state->cursor = state->select_end;
+            state->has_preferred_x = 0;
+        } else {
+            /* if currently there's a selection,
+             * move cursor to end of selection */
+            if (NK_TEXT_HAS_SELECTION(state))
+                nk_textedit_move_to_last(state);
+            else ++state->cursor;
+            nk_textedit_clamp(state);
+            state->has_preferred_x = 0;
+        } break;
+
+    case NK_KEY_TEXT_WORD_LEFT:
+        if (shift_mod) {
+            if( !NK_TEXT_HAS_SELECTION( state ) )
+            nk_textedit_prep_selection_at_cursor(state);
+            state->cursor = nk_textedit_move_to_word_previous(state);
+            state->select_end = state->cursor;
+            nk_textedit_clamp(state );
+        } else {
+            if (NK_TEXT_HAS_SELECTION(state))
+                nk_textedit_move_to_first(state);
+            else {
+                state->cursor = nk_textedit_move_to_word_previous(state);
+                nk_textedit_clamp(state );
+            }
+        } break;
+
+    case NK_KEY_TEXT_WORD_RIGHT:
+        if (shift_mod) {
+            if( !NK_TEXT_HAS_SELECTION( state ) )
+                nk_textedit_prep_selection_at_cursor(state);
+            state->cursor = nk_textedit_move_to_word_next(state);
+            state->select_end = state->cursor;
+            nk_textedit_clamp(state);
+        } else {
+            if (NK_TEXT_HAS_SELECTION(state))
+                nk_textedit_move_to_last(state);
+            else {
+                state->cursor = nk_textedit_move_to_word_next(state);
+                nk_textedit_clamp(state );
+            }
+        } break;
+
+    case NK_KEY_DOWN: {
+        struct nk_text_find find;
+        struct nk_text_edit_row row;
+        int i, sel = shift_mod;
+
+        if (state->single_line) {
+            /* on windows, up&down in single-line behave like left&right */
+            key = NK_KEY_RIGHT;
+            goto retry;
+        }
+
+        if (sel)
+            nk_textedit_prep_selection_at_cursor(state);
+        else if (NK_TEXT_HAS_SELECTION(state))
+            nk_textedit_move_to_last(state);
+
+        /* compute current position of cursor point */
+        nk_textedit_clamp(state);
+        nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
+            font, row_height);
+
+        /* now find character position down a row */
+        if (find.length)
+        {
+            float x;
+            float goal_x = state->has_preferred_x ? state->preferred_x : find.x;
+            int start = find.first_char + find.length;
+
+            state->cursor = start;
+            nk_textedit_layout_row(&row, state, state->cursor, row_height, font);
+            x = row.x0;
+
+            for (i=0; i < row.num_chars && x < row.x1; ++i) {
+                float dx = nk_textedit_get_width(state, start, i, font);
+                x += dx;
+                if (x > goal_x)
+                    break;
+                ++state->cursor;
+            }
+            nk_textedit_clamp(state);
+
+            state->has_preferred_x = 1;
+            state->preferred_x = goal_x;
+            if (sel)
+                state->select_end = state->cursor;
+        }
+    } break;
+
+    case NK_KEY_UP: {
+        struct nk_text_find find;
+        struct nk_text_edit_row row;
+        int i, sel = shift_mod;
+
+        if (state->single_line) {
+            /* on windows, up&down become left&right */
+            key = NK_KEY_LEFT;
+            goto retry;
+        }
+
+        if (sel)
+            nk_textedit_prep_selection_at_cursor(state);
+        else if (NK_TEXT_HAS_SELECTION(state))
+            nk_textedit_move_to_first(state);
+
+         /* compute current position of cursor point */
+         nk_textedit_clamp(state);
+         nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
+                font, row_height);
+
+         /* can only go up if there's a previous row */
+         if (find.prev_first != find.first_char) {
+            /* now find character position up a row */
+            float x;
+            float goal_x = state->has_preferred_x ? state->preferred_x : find.x;
+
+            state->cursor = find.prev_first;
+            nk_textedit_layout_row(&row, state, state->cursor, row_height, font);
+            x = row.x0;
+
+            for (i=0; i < row.num_chars && x < row.x1; ++i) {
+                float dx = nk_textedit_get_width(state, find.prev_first, i, font);
+                x += dx;
+                if (x > goal_x)
+                    break;
+                ++state->cursor;
+            }
+            nk_textedit_clamp(state);
+
+            state->has_preferred_x = 1;
+            state->preferred_x = goal_x;
+            if (sel) state->select_end = state->cursor;
+         }
+      } break;
+
+    case NK_KEY_DEL:
+        if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
+            break;
+        if (NK_TEXT_HAS_SELECTION(state))
+            nk_textedit_delete_selection(state);
+        else {
+            int n = state->string.len;
+            if (state->cursor < n)
+                nk_textedit_delete(state, state->cursor, 1);
+         }
+         state->has_preferred_x = 0;
+         break;
+
+    case NK_KEY_BACKSPACE:
+        if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
+            break;
+        if (NK_TEXT_HAS_SELECTION(state))
+            nk_textedit_delete_selection(state);
+        else {
+            nk_textedit_clamp(state);
+            if (state->cursor > 0) {
+                nk_textedit_delete(state, state->cursor-1, 1);
+                --state->cursor;
+            }
+         }
+         state->has_preferred_x = 0;
+         break;
+
+    case NK_KEY_TEXT_START:
+         if (shift_mod) {
+            nk_textedit_prep_selection_at_cursor(state);
+            state->cursor = state->select_end = 0;
+            state->has_preferred_x = 0;
+         } else {
+            state->cursor = state->select_start = state->select_end = 0;
+            state->has_preferred_x = 0;
+         }
+         break;
+
+    case NK_KEY_TEXT_END:
+         if (shift_mod) {
+            nk_textedit_prep_selection_at_cursor(state);
+            state->cursor = state->select_end = state->string.len;
+            state->has_preferred_x = 0;
+         } else {
+            state->cursor = state->string.len;
+            state->select_start = state->select_end = 0;
+            state->has_preferred_x = 0;
+         }
+         break;
+
+    case NK_KEY_TEXT_LINE_START: {
+        if (shift_mod) {
+            struct nk_text_find find;
+           nk_textedit_clamp(state);
+            nk_textedit_prep_selection_at_cursor(state);
+            if (state->string.len && state->cursor == state->string.len)
+                --state->cursor;
+            nk_textedit_find_charpos(&find, state,state->cursor, state->single_line,
+                font, row_height);
+            state->cursor = state->select_end = find.first_char;
+            state->has_preferred_x = 0;
+        } else {
+            struct nk_text_find find;
+            if (state->string.len && state->cursor == state->string.len)
+                --state->cursor;
+            nk_textedit_clamp(state);
+            nk_textedit_move_to_first(state);
+            nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
+                font, row_height);
+            state->cursor = find.first_char;
+            state->has_preferred_x = 0;
+        }
+      } break;
+
+    case NK_KEY_TEXT_LINE_END: {
+        if (shift_mod) {
+            struct nk_text_find find;
+            nk_textedit_clamp(state);
+            nk_textedit_prep_selection_at_cursor(state);
+            nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
+                font, row_height);
+            state->has_preferred_x = 0;
+            state->cursor = find.first_char + find.length;
+            if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) == '\n')
+                --state->cursor;
+            state->select_end = state->cursor;
+        } else {
+            struct nk_text_find find;
+            nk_textedit_clamp(state);
+            nk_textedit_move_to_first(state);
+            nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
+                font, row_height);
+
+            state->has_preferred_x = 0;
+            state->cursor = find.first_char + find.length;
+            if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) == '\n')
+                --state->cursor;
+        }} break;
+    }
+}
+
+NK_INTERN void
+nk_textedit_flush_redo(struct nk_text_undo_state *state)
+{
+    state->redo_point = NK_TEXTEDIT_UNDOSTATECOUNT;
+    state->redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT;
+}
+
+NK_INTERN void
+nk_textedit_discard_undo(struct nk_text_undo_state *state)
+{
+    /* discard the oldest entry in the undo list */
+    if (state->undo_point > 0) {
+        /* if the 0th undo state has characters, clean those up */
+        if (state->undo_rec[0].char_storage >= 0) {
+            int n = state->undo_rec[0].insert_length, i;
+            /* delete n characters from all other records */
+            state->undo_char_point = (short)(state->undo_char_point - n);
+            NK_MEMCPY(state->undo_char, state->undo_char + n,
+                (nk_size)state->undo_char_point*sizeof(nk_rune));
+            for (i=0; i < state->undo_point; ++i) {
+                if (state->undo_rec[i].char_storage >= 0)
+                state->undo_rec[i].char_storage = (short)
+                    (state->undo_rec[i].char_storage - n);
+            }
+        }
+        --state->undo_point;
+        NK_MEMCPY(state->undo_rec, state->undo_rec+1,
+            (nk_size)((nk_size)state->undo_point * sizeof(state->undo_rec[0])));
+    }
+}
+
+NK_INTERN void
+nk_textedit_discard_redo(struct nk_text_undo_state *state)
+{
+/*  discard the oldest entry in the redo list--it's bad if this
+    ever happens, but because undo & redo have to store the actual
+    characters in different cases, the redo character buffer can
+    fill up even though the undo buffer didn't */
+    nk_size num;
+    int k = NK_TEXTEDIT_UNDOSTATECOUNT-1;
+    if (state->redo_point <= k) {
+        /* if the k'th undo state has characters, clean those up */
+        if (state->undo_rec[k].char_storage >= 0) {
+            int n = state->undo_rec[k].insert_length, i;
+            /* delete n characters from all other records */
+            state->redo_char_point = (short)(state->redo_char_point + n);
+            num = (nk_size)(NK_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point);
+            NK_MEMCPY(state->undo_char + state->redo_char_point,
+                state->undo_char + state->redo_char_point-n, num * sizeof(char));
+            for (i = state->redo_point; i < k; ++i) {
+                if (state->undo_rec[i].char_storage >= 0) {
+                    state->undo_rec[i].char_storage = (short)
+                        (state->undo_rec[i].char_storage + n);
+                }
+            }
+        }
+        ++state->redo_point;
+        num = (nk_size)(NK_TEXTEDIT_UNDOSTATECOUNT - state->redo_point);
+        if (num) NK_MEMCPY(state->undo_rec + state->redo_point-1,
+            state->undo_rec + state->redo_point, num * sizeof(state->undo_rec[0]));
+    }
+}
+
+NK_INTERN struct nk_text_undo_record*
+nk_textedit_create_undo_record(struct nk_text_undo_state *state, int numchars)
+{
+    /* any time we create a new undo record, we discard redo*/
+    nk_textedit_flush_redo(state);
+
+    /* if we have no free records, we have to make room,
+     * by sliding the existing records down */
+    if (state->undo_point == NK_TEXTEDIT_UNDOSTATECOUNT)
+        nk_textedit_discard_undo(state);
+
+    /* if the characters to store won't possibly fit in the buffer,
+     * we can't undo */
+    if (numchars > NK_TEXTEDIT_UNDOCHARCOUNT) {
+        state->undo_point = 0;
+        state->undo_char_point = 0;
+        return 0;
+    }
+
+    /* if we don't have enough free characters in the buffer,
+     * we have to make room */
+    while (state->undo_char_point + numchars > NK_TEXTEDIT_UNDOCHARCOUNT)
+        nk_textedit_discard_undo(state);
+    return &state->undo_rec[state->undo_point++];
+}
+
+NK_INTERN nk_rune*
+nk_textedit_createundo(struct nk_text_undo_state *state, int pos,
+    int insert_len, int delete_len)
+{
+    struct nk_text_undo_record *r = nk_textedit_create_undo_record(state, insert_len);
+    if (r == 0)
+        return 0;
+
+    r->where = pos;
+    r->insert_length = (short) insert_len;
+    r->delete_length = (short) delete_len;
+
+    if (insert_len == 0) {
+        r->char_storage = -1;
+        return 0;
+    } else {
+        r->char_storage = state->undo_char_point;
+        state->undo_char_point = (short)(state->undo_char_point +  insert_len);
+        return &state->undo_char[r->char_storage];
+    }
+}
+
+NK_API void
+nk_textedit_undo(struct nk_text_edit *state)
+{
+    struct nk_text_undo_state *s = &state->undo;
+    struct nk_text_undo_record u, *r;
+    if (s->undo_point == 0)
+        return;
+
+    /* we need to do two things: apply the undo record, and create a redo record */
+    u = s->undo_rec[s->undo_point-1];
+    r = &s->undo_rec[s->redo_point-1];
+    r->char_storage = -1;
+
+    r->insert_length = u.delete_length;
+    r->delete_length = u.insert_length;
+    r->where = u.where;
+
+    if (u.delete_length)
+    {
+       /*   if the undo record says to delete characters, then the redo record will
+            need to re-insert the characters that get deleted, so we need to store
+            them.
+            there are three cases:
+                - there's enough room to store the characters
+                - characters stored for *redoing* don't leave room for redo
+                - characters stored for *undoing* don't leave room for redo
+            if the last is true, we have to bail */
+        if (s->undo_char_point + u.delete_length >= NK_TEXTEDIT_UNDOCHARCOUNT) {
+            /* the undo records take up too much character space; there's no space
+            * to store the redo characters */
+            r->insert_length = 0;
+        } else {
+            int i;
+            /* there's definitely room to store the characters eventually */
+            while (s->undo_char_point + u.delete_length > s->redo_char_point) {
+                /* there's currently not enough room, so discard a redo record */
+                nk_textedit_discard_redo(s);
+                /* should never happen: */
+                if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT)
+                    return;
+            }
+
+            r = &s->undo_rec[s->redo_point-1];
+            r->char_storage = (short)(s->redo_char_point - u.delete_length);
+            s->redo_char_point = (short)(s->redo_char_point -  u.delete_length);
+
+            /* now save the characters */
+            for (i=0; i < u.delete_length; ++i)
+                s->undo_char[r->char_storage + i] =
+                    nk_str_rune_at(&state->string, u.where + i);
+        }
+        /* now we can carry out the deletion */
+        nk_str_delete_runes(&state->string, u.where, u.delete_length);
+    }
+
+    /* check type of recorded action: */
+    if (u.insert_length) {
+        /* easy case: was a deletion, so we need to insert n characters */
+        nk_str_insert_text_runes(&state->string, u.where,
+            &s->undo_char[u.char_storage], u.insert_length);
+        s->undo_char_point = (short)(s->undo_char_point - u.insert_length);
+    }
+    state->cursor = (short)(u.where + u.insert_length);
+
+    s->undo_point--;
+    s->redo_point--;
+}
+
+NK_API void
+nk_textedit_redo(struct nk_text_edit *state)
+{
+    struct nk_text_undo_state *s = &state->undo;
+    struct nk_text_undo_record *u, r;
+    if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT)
+        return;
+
+    /* we need to do two things: apply the redo record, and create an undo record */
+    u = &s->undo_rec[s->undo_point];
+    r = s->undo_rec[s->redo_point];
+
+    /* we KNOW there must be room for the undo record, because the redo record
+    was derived from an undo record */
+    u->delete_length = r.insert_length;
+    u->insert_length = r.delete_length;
+    u->where = r.where;
+    u->char_storage = -1;
+
+    if (r.delete_length) {
+        /* the redo record requires us to delete characters, so the undo record
+        needs to store the characters */
+        if (s->undo_char_point + u->insert_length > s->redo_char_point) {
+            u->insert_length = 0;
+            u->delete_length = 0;
+        } else {
+            int i;
+            u->char_storage = s->undo_char_point;
+            s->undo_char_point = (short)(s->undo_char_point + u->insert_length);
+
+            /* now save the characters */
+            for (i=0; i < u->insert_length; ++i) {
+                s->undo_char[u->char_storage + i] =
+                    nk_str_rune_at(&state->string, u->where + i);
+            }
+        }
+        nk_str_delete_runes(&state->string, r.where, r.delete_length);
+    }
+
+    if (r.insert_length) {
+        /* easy case: need to insert n characters */
+        nk_str_insert_text_runes(&state->string, r.where,
+            &s->undo_char[r.char_storage], r.insert_length);
+    }
+    state->cursor = r.where + r.insert_length;
+
+    s->undo_point++;
+    s->redo_point++;
+}
+
+NK_INTERN void
+nk_textedit_makeundo_insert(struct nk_text_edit *state, int where, int length)
+{
+    nk_textedit_createundo(&state->undo, where, 0, length);
+}
+
+NK_INTERN void
+nk_textedit_makeundo_delete(struct nk_text_edit *state, int where, int length)
+{
+    int i;
+    nk_rune *p = nk_textedit_createundo(&state->undo, where, length, 0);
+    if (p) {
+        for (i=0; i < length; ++i)
+            p[i] = nk_str_rune_at(&state->string, where+i);
+    }
+}
+
+NK_INTERN void
+nk_textedit_makeundo_replace(struct nk_text_edit *state, int where,
+    int old_length, int new_length)
+{
+    int i;
+    nk_rune *p = nk_textedit_createundo(&state->undo, where, old_length, new_length);
+    if (p) {
+        for (i=0; i < old_length; ++i)
+            p[i] = nk_str_rune_at(&state->string, where+i);
+    }
+}
+
+NK_INTERN void
+nk_textedit_clear_state(struct nk_text_edit *state, enum nk_text_edit_type type,
+    nk_plugin_filter filter)
+{
+    /* reset the state to default */
+   state->undo.undo_point = 0;
+   state->undo.undo_char_point = 0;
+   state->undo.redo_point = NK_TEXTEDIT_UNDOSTATECOUNT;
+   state->undo.redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT;
+   state->select_end = state->select_start = 0;
+   state->cursor = 0;
+   state->has_preferred_x = 0;
+   state->preferred_x = 0;
+   state->cursor_at_end_of_line = 0;
+   state->initialized = 1;
+   state->single_line = (unsigned char)(type == NK_TEXT_EDIT_SINGLE_LINE);
+   state->mode = NK_TEXT_EDIT_MODE_VIEW;
+   state->filter = filter;
+   state->scrollbar = nk_vec2(0,0);
+}
+
+NK_API void
+nk_textedit_init_fixed(struct nk_text_edit *state, void *memory, nk_size size)
+{
+    NK_ASSERT(state);
+    NK_ASSERT(memory);
+    if (!state || !memory || !size) return;
+    NK_MEMSET(state, 0, sizeof(struct nk_text_edit));
+    nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0);
+    nk_str_init_fixed(&state->string, memory, size);
+}
+
+NK_API void
+nk_textedit_init(struct nk_text_edit *state, struct nk_allocator *alloc, nk_size size)
+{
+    NK_ASSERT(state);
+    NK_ASSERT(alloc);
+    if (!state || !alloc) return;
+    NK_MEMSET(state, 0, sizeof(struct nk_text_edit));
+    nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0);
+    nk_str_init(&state->string, alloc, size);
+}
+
+#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR
+NK_API void
+nk_textedit_init_default(struct nk_text_edit *state)
+{
+    NK_ASSERT(state);
+    if (!state) return;
+    NK_MEMSET(state, 0, sizeof(struct nk_text_edit));
+    nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0);
+    nk_str_init_default(&state->string);
+}
+#endif
+
+NK_API void
+nk_textedit_select_all(struct nk_text_edit *state)
+{
+    NK_ASSERT(state);
+    state->select_start = 0;
+    state->select_end = state->string.len;
+}
+
+NK_API void
+nk_textedit_free(struct nk_text_edit *state)
+{
+    NK_ASSERT(state);
+    if (!state) return;
+    nk_str_free(&state->string);
+}
+
+/* ===============================================================
+ *
+ *                          TEXT WIDGET
+ *
+ * ===============================================================*/
+#define nk_widget_state_reset(s)\
+    if ((*(s)) & NK_WIDGET_STATE_MODIFIED)\
+        (*(s)) = NK_WIDGET_STATE_INACTIVE|NK_WIDGET_STATE_MODIFIED;\
+    else (*(s)) = NK_WIDGET_STATE_INACTIVE;
+
+struct nk_text {
+    struct nk_vec2 padding;
+    struct nk_color background;
+    struct nk_color text;
+};
+
+NK_INTERN void
+nk_widget_text(struct nk_command_buffer *o, struct nk_rect b,
+    const char *string, int len, const struct nk_text *t,
+    nk_flags a, const struct nk_user_font *f)
+{
+    struct nk_rect label;
+    float text_width;
+
+    NK_ASSERT(o);
+    NK_ASSERT(t);
+    if (!o || !t) return;
+
+    b.h = NK_MAX(b.h, 2 * t->padding.y);
+    label.x = 0; label.w = 0;
+    label.y = b.y + t->padding.y;
+    label.h = NK_MIN(f->height, b.h - 2 * t->padding.y);
+
+    text_width = f->width(f->userdata, f->height, (const char*)string, len);
+    text_width += (2.0f * t->padding.x);
+
+    /* align in x-axis */
+    if (a & NK_TEXT_ALIGN_LEFT) {
+        label.x = b.x + t->padding.x;
+        label.w = NK_MAX(0, b.w - 2 * t->padding.x);
+    } else if (a & NK_TEXT_ALIGN_CENTERED) {
+        label.w = NK_MAX(1, 2 * t->padding.x + (float)text_width);
+        label.x = (b.x + t->padding.x + ((b.w - 2 * t->padding.x) - label.w) / 2);
+        label.x = NK_MAX(b.x + t->padding.x, label.x);
+        label.w = NK_MIN(b.x + b.w, label.x + label.w);
+        if (label.w >= label.x) label.w -= label.x;
+    } else if (a & NK_TEXT_ALIGN_RIGHT) {
+        label.x = NK_MAX(b.x + t->padding.x, (b.x + b.w) - (2 * t->padding.x + (float)text_width));
+        label.w = (float)text_width + 2 * t->padding.x;
+    } else return;
+
+    /* align in y-axis */
+    if (a & NK_TEXT_ALIGN_MIDDLE) {
+        label.y = b.y + b.h/2.0f - (float)f->height/2.0f;
+        label.h = NK_MAX(b.h/2.0f, b.h - (b.h/2.0f + f->height/2.0f));
+    } else if (a & NK_TEXT_ALIGN_BOTTOM) {
+        label.y = b.y + b.h - f->height;
+        label.h = f->height;
+    }
+    nk_draw_text(o, label, (const char*)string,
+        len, f, t->background, t->text);
+}
+
+NK_INTERN void
+nk_widget_text_wrap(struct nk_command_buffer *o, struct nk_rect b,
+    const char *string, int len, const struct nk_text *t,
+    const struct nk_user_font *f)
+{
+    float width;
+    int glyphs = 0;
+    int fitting = 0;
+    int done = 0;
+    struct nk_rect line;
+    struct nk_text text;
+    NK_INTERN nk_rune seperator[] = {' '};
+
+    NK_ASSERT(o);
+    NK_ASSERT(t);
+    if (!o || !t) return;
+
+    text.padding = nk_vec2(0,0);
+    text.background = t->background;
+    text.text = t->text;
+
+    b.w = NK_MAX(b.w, 2 * t->padding.x);
+    b.h = NK_MAX(b.h, 2 * t->padding.y);
+    b.h = b.h - 2 * t->padding.y;
+
+    line.x = b.x + t->padding.x;
+    line.y = b.y + t->padding.y;
+    line.w = b.w - 2 * t->padding.x;
+    line.h = 2 * t->padding.y + f->height;
+
+    fitting = nk_text_clamp(f, string, len, line.w, &glyphs, &width, seperator,NK_LEN(seperator));
+    while (done < len) {
+        if (!fitting || line.y + line.h >= (b.y + b.h)) break;
+        nk_widget_text(o, line, &string[done], fitting, &text, NK_TEXT_LEFT, f);
+        done += fitting;
+        line.y += f->height + 2 * t->padding.y;
+        fitting = nk_text_clamp(f, &string[done], len - done, line.w, &glyphs, &width, seperator,NK_LEN(seperator));
+    }
+}
+
+/* ===============================================================
+ *
+ *                          BUTTON
+ *
+ * ===============================================================*/
+NK_INTERN void
+nk_draw_symbol(struct nk_command_buffer *out, enum nk_symbol_type type,
+    struct nk_rect content, struct nk_color background, struct nk_color foreground,
+    float border_width, const struct nk_user_font *font)
+{
+    switch (type) {
+    case NK_SYMBOL_X:
+    case NK_SYMBOL_UNDERSCORE:
+    case NK_SYMBOL_PLUS:
+    case NK_SYMBOL_MINUS: {
+        /* single character text symbol */
+        const char *X = (type == NK_SYMBOL_X) ? "x":
+            (type == NK_SYMBOL_UNDERSCORE) ? "_":
+            (type == NK_SYMBOL_PLUS) ? "+": "-";
+        struct nk_text text;
+        text.padding = nk_vec2(0,0);
+        text.background = background;
+        text.text = foreground;
+        nk_widget_text(out, content, X, 1, &text, NK_TEXT_CENTERED, font);
+    } break;
+    case NK_SYMBOL_CIRCLE_SOLID:
+    case NK_SYMBOL_CIRCLE_OUTLINE:
+    case NK_SYMBOL_RECT_SOLID:
+    case NK_SYMBOL_RECT_OUTLINE: {
+        /* simple empty/filled shapes */
+        if (type == NK_SYMBOL_RECT_SOLID || type == NK_SYMBOL_RECT_OUTLINE) {
+            nk_fill_rect(out, content,  0, foreground);
+            if (type == NK_SYMBOL_RECT_OUTLINE)
+                nk_fill_rect(out, nk_shrink_rect(content, border_width), 0, background);
+        } else {
+            nk_fill_circle(out, content, foreground);
+            if (type == NK_SYMBOL_CIRCLE_OUTLINE)
+                nk_fill_circle(out, nk_shrink_rect(content, 1), background);
+        }
+    } break;
+    case NK_SYMBOL_TRIANGLE_UP:
+    case NK_SYMBOL_TRIANGLE_DOWN:
+    case NK_SYMBOL_TRIANGLE_LEFT:
+    case NK_SYMBOL_TRIANGLE_RIGHT: {
+        enum nk_heading heading;
+        struct nk_vec2 points[3];
+        heading = (type == NK_SYMBOL_TRIANGLE_RIGHT) ? NK_RIGHT :
+            (type == NK_SYMBOL_TRIANGLE_LEFT) ? NK_LEFT:
+            (type == NK_SYMBOL_TRIANGLE_UP) ? NK_UP: NK_DOWN;
+        nk_triangle_from_direction(points, content, 0, 0, heading);
+        nk_fill_triangle(out, points[0].x, points[0].y, points[1].x, points[1].y,
+            points[2].x, points[2].y, foreground);
+    } break;
+    default:
+    case NK_SYMBOL_NONE:
+    case NK_SYMBOL_MAX: break;
+    }
+}
+
+NK_INTERN int
+nk_button_behavior(nk_flags *state, struct nk_rect r,
+    const struct nk_input *i, enum nk_button_behavior behavior)
+{
+    int ret = 0;
+    nk_widget_state_reset(state);
+    if (!i) return 0;
+    if (nk_input_is_mouse_hovering_rect(i, r)) {
+        *state = NK_WIDGET_STATE_HOVERED;
+        if (nk_input_is_mouse_down(i, NK_BUTTON_LEFT))
+            *state = NK_WIDGET_STATE_ACTIVE;
+        if (nk_input_has_mouse_click_in_rect(i, NK_BUTTON_LEFT, r)) {
+            ret = (behavior != NK_BUTTON_DEFAULT) ?
+                nk_input_is_mouse_down(i, NK_BUTTON_LEFT):
+#ifdef NK_BUTTON_TRIGGER_ON_RELEASE
+                nk_input_is_mouse_released(i, NK_BUTTON_LEFT);
+#else
+                nk_input_is_mouse_pressed(i, NK_BUTTON_LEFT);
+#endif
+        }
+    }
+    if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(i, r))
+        *state |= NK_WIDGET_STATE_ENTERED;
+    else if (nk_input_is_mouse_prev_hovering_rect(i, r))
+        *state |= NK_WIDGET_STATE_LEFT;
+    return ret;
+}
+
+NK_INTERN const struct nk_style_item*
+nk_draw_button(struct nk_command_buffer *out,
+    const struct nk_rect *bounds, nk_flags state,
+    const struct nk_style_button *style)
+{
+    const struct nk_style_item *background;
+    if (state & NK_WIDGET_STATE_HOVER)
+        background = &style->hover;
+    else if (state & NK_WIDGET_STATE_ACTIVED)
+        background = &style->active;
+    else background = &style->normal;
+
+    if (background->type == NK_STYLE_ITEM_IMAGE) {
+        nk_draw_image(out, *bounds, &background->data.image, nk_white);
+    } else {
+        nk_fill_rect(out, *bounds, style->rounding, background->data.color);
+        nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color);
+    }
+    return background;
+}
+
+NK_INTERN int
+nk_do_button(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r,
+    const struct nk_style_button *style, const struct nk_input *in,
+    enum nk_button_behavior behavior, struct nk_rect *content)
+{
+    struct nk_rect bounds;
+    NK_ASSERT(style);
+    NK_ASSERT(state);
+    NK_ASSERT(out);
+    if (!out || !style)
+        return nk_false;
+
+    /* calculate button content space */
+    content->x = r.x + style->padding.x + style->border + style->rounding;
+    content->y = r.y + style->padding.y + style->border + style->rounding;
+    content->w = r.w - (2 * style->padding.x + style->border + style->rounding*2);
+    content->h = r.h - (2 * style->padding.y + style->border + style->rounding*2);
+
+    /* execute button behavior */
+    bounds.x = r.x - style->touch_padding.x;
+    bounds.y = r.y - style->touch_padding.y;
+    bounds.w = r.w + 2 * style->touch_padding.x;
+    bounds.h = r.h + 2 * style->touch_padding.y;
+    return nk_button_behavior(state, bounds, in, behavior);
+}
+
+NK_INTERN void
+nk_draw_button_text(struct nk_command_buffer *out,
+    const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state,
+    const struct nk_style_button *style, const char *txt, int len,
+    nk_flags text_alignment, const struct nk_user_font *font)
+{
+    struct nk_text text;
+    const struct nk_style_item *background;
+    background = nk_draw_button(out, bounds, state, style);
+
+    /* select correct colors/images */
+    if (background->type == NK_STYLE_ITEM_COLOR)
+        text.background = background->data.color;
+    else text.background = style->text_background;
+    if (state & NK_WIDGET_STATE_HOVER)
+        text.text = style->text_hover;
+    else if (state & NK_WIDGET_STATE_ACTIVED)
+        text.text = style->text_active;
+    else text.text = style->text_normal;
+
+    text.padding = nk_vec2(0,0);
+    nk_widget_text(out, *content, txt, len, &text, text_alignment, font);
+}
+
+NK_INTERN int
+nk_do_button_text(nk_flags *state,
+    struct nk_command_buffer *out, struct nk_rect bounds,
+    const char *string, int len, nk_flags align, enum nk_button_behavior behavior,
+    const struct nk_style_button *style, const struct nk_input *in,
+    const struct nk_user_font *font)
+{
+    struct nk_rect content;
+    int ret = nk_false;
+
+    NK_ASSERT(state);
+    NK_ASSERT(style);
+    NK_ASSERT(out);
+    NK_ASSERT(string);
+    NK_ASSERT(font);
+    if (!out || !style || !font || !string)
+        return nk_false;
+
+    ret = nk_do_button(state, out, bounds, style, in, behavior, &content);
+    if (style->draw_begin) style->draw_begin(out, style->userdata);
+    nk_draw_button_text(out, &bounds, &content, *state, style, string, len, align, font);
+    if (style->draw_end) style->draw_end(out, style->userdata);
+    return ret;
+}
+
+NK_INTERN void
+nk_draw_button_symbol(struct nk_command_buffer *out,
+    const struct nk_rect *bounds, const struct nk_rect *content,
+    nk_flags state, const struct nk_style_button *style,
+    enum nk_symbol_type type, const struct nk_user_font *font)
+{
+    struct nk_color sym, bg;
+    const struct nk_style_item *background;
+
+    /* select correct colors/images */
+    background = nk_draw_button(out, bounds, state, style);
+    if (background->type == NK_STYLE_ITEM_COLOR)
+        bg = background->data.color;
+    else bg = style->text_background;
+
+    if (state & NK_WIDGET_STATE_HOVER)
+        sym = style->text_hover;
+    else if (state & NK_WIDGET_STATE_ACTIVED)
+        sym = style->text_active;
+    else sym = style->text_normal;
+    nk_draw_symbol(out, type, *content, bg, sym, 1, font);
+}
+
+NK_INTERN int
+nk_do_button_symbol(nk_flags *state,
+    struct nk_command_buffer *out, struct nk_rect bounds,
+    enum nk_symbol_type symbol, enum nk_button_behavior behavior,
+    const struct nk_style_button *style, const struct nk_input *in,
+    const struct nk_user_font *font)
+{
+    int ret;
+    struct nk_rect content;
+
+    NK_ASSERT(state);
+    NK_ASSERT(style);
+    NK_ASSERT(font);
+    NK_ASSERT(out);
+    if (!out || !style || !font || !state)
+        return nk_false;
+
+    ret = nk_do_button(state, out, bounds, style, in, behavior, &content);
+    if (style->draw_begin) style->draw_begin(out, style->userdata);
+    nk_draw_button_symbol(out, &bounds, &content, *state, style, symbol, font);
+    if (style->draw_end) style->draw_end(out, style->userdata);
+    return ret;
+}
+
+NK_INTERN void
+nk_draw_button_image(struct nk_command_buffer *out,
+    const struct nk_rect *bounds, const struct nk_rect *content,
+    nk_flags state, const struct nk_style_button *style, const struct nk_image *img)
+{
+    nk_draw_button(out, bounds, state, style);
+    nk_draw_image(out, *content, img, nk_white);
+}
+
+NK_INTERN int
+nk_do_button_image(nk_flags *state,
+    struct nk_command_buffer *out, struct nk_rect bounds,
+    struct nk_image img, enum nk_button_behavior b,
+    const struct nk_style_button *style, const struct nk_input *in)
+{
+    int ret;
+    struct nk_rect content;
+
+    NK_ASSERT(state);
+    NK_ASSERT(style);
+    NK_ASSERT(out);
+    if (!out || !style || !state)
+        return nk_false;
+
+    ret = nk_do_button(state, out, bounds, style, in, b, &content);
+    content.x += style->image_padding.x;
+    content.y += style->image_padding.y;
+    content.w -= 2 * style->image_padding.x;
+    content.h -= 2 * style->image_padding.y;
+
+    if (style->draw_begin) style->draw_begin(out, style->userdata);
+    nk_draw_button_image(out, &bounds, &content, *state, style, &img);
+    if (style->draw_end) style->draw_end(out, style->userdata);
+    return ret;
+}
+
+NK_INTERN void
+nk_draw_button_text_symbol(struct nk_command_buffer *out,
+    const struct nk_rect *bounds, const struct nk_rect *label,
+    const struct nk_rect *symbol, nk_flags state, const struct nk_style_button *style,
+    const char *str, int len, enum nk_symbol_type type,
+    const struct nk_user_font *font)
+{
+    struct nk_color sym;
+    struct nk_text text;
+    const struct nk_style_item *background;
+
+    /* select correct background colors/images */
+    background = nk_draw_button(out, bounds, state, style);
+    if (background->type == NK_STYLE_ITEM_COLOR)
+        text.background = background->data.color;
+    else text.background = style->text_background;
+
+    /* select correct text colors */
+    if (state & NK_WIDGET_STATE_HOVER) {
+        sym = style->text_hover;
+        text.text = style->text_hover;
+    } else if (state & NK_WIDGET_STATE_ACTIVED) {
+        sym = style->text_active;
+        text.text = style->text_active;
+    } else {
+        sym = style->text_normal;
+        text.text = style->text_normal;
+    }
+
+    text.padding = nk_vec2(0,0);
+    nk_draw_symbol(out, type, *symbol, style->text_background, sym, 0, font);
+    nk_widget_text(out, *label, str, len, &text, NK_TEXT_CENTERED, font);
+}
+
+NK_INTERN int
+nk_do_button_text_symbol(nk_flags *state,
+    struct nk_command_buffer *out, struct nk_rect bounds,
+    enum nk_symbol_type symbol, const char *str, int len, nk_flags align,
+    enum nk_button_behavior behavior, const struct nk_style_button *style,
+    const struct nk_user_font *font, const struct nk_input *in)
+{
+    int ret;
+    struct nk_rect tri = {0,0,0,0};
+    struct nk_rect content;
+
+    NK_ASSERT(style);
+    NK_ASSERT(out);
+    NK_ASSERT(font);
+    if (!out || !style || !font)
+        return nk_false;
+
+    ret = nk_do_button(state, out, bounds, style, in, behavior, &content);
+    tri.y = content.y + (content.h/2) - font->height/2;
+    tri.w = font->height; tri.h = font->height;
+    if (align & NK_TEXT_ALIGN_LEFT) {
+        tri.x = (content.x + content.w) - (2 * style->padding.x + tri.w);
+        tri.x = NK_MAX(tri.x, 0);
+    } else tri.x = content.x + 2 * style->padding.x;
+
+    /* draw button */
+    if (style->draw_begin) style->draw_begin(out, style->userdata);
+    nk_draw_button_text_symbol(out, &bounds, &content, &tri,
+        *state, style, str, len, symbol, font);
+    if (style->draw_end) style->draw_end(out, style->userdata);
+    return ret;
+}
+
+NK_INTERN void
+nk_draw_button_text_image(struct nk_command_buffer *out,
+    const struct nk_rect *bounds, const struct nk_rect *label,
+    const struct nk_rect *image, nk_flags state, const struct nk_style_button *style,
+    const char *str, int len, const struct nk_user_font *font,
+    const struct nk_image *img)
+{
+    struct nk_text text;
+    const struct nk_style_item *background;
+    background = nk_draw_button(out, bounds, state, style);
+
+    /* select correct colors */
+    if (background->type == NK_STYLE_ITEM_COLOR)
+        text.background = background->data.color;
+    else text.background = style->text_background;
+    if (state & NK_WIDGET_STATE_HOVER)
+        text.text = style->text_hover;
+    else if (state & NK_WIDGET_STATE_ACTIVED)
+        text.text = style->text_active;
+    else text.text = style->text_normal;
+
+    text.padding = nk_vec2(0,0);
+    nk_widget_text(out, *label, str, len, &text, NK_TEXT_CENTERED, font);
+    nk_draw_image(out, *image, img, nk_white);
+}
+
+NK_INTERN int
+nk_do_button_text_image(nk_flags *state,
+    struct nk_command_buffer *out, struct nk_rect bounds,
+    struct nk_image img, const char* str, int len, nk_flags align,
+    enum nk_button_behavior behavior, const struct nk_style_button *style,
+    const struct nk_user_font *font, const struct nk_input *in)
+{
+    int ret;
+    struct nk_rect icon;
+    struct nk_rect content;
+
+    NK_ASSERT(style);
+    NK_ASSERT(state);
+    NK_ASSERT(font);
+    NK_ASSERT(out);
+    if (!out || !font || !style || !str)
+        return nk_false;
+
+    ret = nk_do_button(state, out, bounds, style, in, behavior, &content);
+    icon.y = bounds.y + style->padding.y;
+    icon.w = icon.h = bounds.h - 2 * style->padding.y;
+    if (align & NK_TEXT_ALIGN_LEFT) {
+        icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w);
+        icon.x = NK_MAX(icon.x, 0);
+    } else icon.x = bounds.x + 2 * style->padding.x;
+
+    icon.x += style->image_padding.x;
+    icon.y += style->image_padding.y;
+    icon.w -= 2 * style->image_padding.x;
+    icon.h -= 2 * style->image_padding.y;
+
+    if (style->draw_begin) style->draw_begin(out, style->userdata);
+    nk_draw_button_text_image(out, &bounds, &content, &icon, *state, style, str, len, font, &img);
+    if (style->draw_end) style->draw_end(out, style->userdata);
+    return ret;
+}
+
+/* ===============================================================
+ *
+ *                          TOGGLE
+ *
+ * ===============================================================*/
+enum nk_toggle_type {
+    NK_TOGGLE_CHECK,
+    NK_TOGGLE_OPTION
+};
+
+NK_INTERN int
+nk_toggle_behavior(const struct nk_input *in, struct nk_rect select,
+    nk_flags *state, int active)
+{
+    nk_widget_state_reset(state);
+    if (nk_button_behavior(state, select, in, NK_BUTTON_DEFAULT)) {
+        *state = NK_WIDGET_STATE_ACTIVE;
+        active = !active;
+    }
+    if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, select))
+        *state |= NK_WIDGET_STATE_ENTERED;
+    else if (nk_input_is_mouse_prev_hovering_rect(in, select))
+        *state |= NK_WIDGET_STATE_LEFT;
+    return active;
+}
+
+NK_INTERN void
+nk_draw_checkbox(struct nk_command_buffer *out,
+    nk_flags state, const struct nk_style_toggle *style, int active,
+    const struct nk_rect *label, const struct nk_rect *selector,
+    const struct nk_rect *cursors, const char *string, int len,
+    const struct nk_user_font *font)
+{
+    const struct nk_style_item *background;
+    const struct nk_style_item *cursor;
+    struct nk_text text;
+
+    /* select correct colors/images */
+    if (state & NK_WIDGET_STATE_HOVER) {
+        background = &style->hover;
+        cursor = &style->cursor_hover;
+        text.text = style->text_hover;
+    } else if (state & NK_WIDGET_STATE_ACTIVED) {
+        background = &style->hover;
+        cursor = &style->cursor_hover;
+        text.text = style->text_active;
+    } else {
+        background = &style->normal;
+        cursor = &style->cursor_normal;
+        text.text = style->text_normal;
+    }
+
+    /* draw background and cursor */
+    if (background->type == NK_STYLE_ITEM_COLOR) {
+        nk_fill_rect(out, *selector, 0, style->border_color);
+        nk_fill_rect(out, nk_shrink_rect(*selector, style->border), 0, background->data.color);
+    } else nk_draw_image(out, *selector, &background->data.image, nk_white);
+    if (active) {
+        if (cursor->type == NK_STYLE_ITEM_IMAGE)
+            nk_draw_image(out, *cursors, &cursor->data.image, nk_white);
+        else nk_fill_rect(out, *cursors, 0, cursor->data.color);
+    }
+
+    text.padding.x = 0;
+    text.padding.y = 0;
+    text.background = style->text_background;
+    nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font);
+}
+
+NK_INTERN void
+nk_draw_option(struct nk_command_buffer *out,
+    nk_flags state, const struct nk_style_toggle *style, int active,
+    const struct nk_rect *label, const struct nk_rect *selector,
+    const struct nk_rect *cursors, const char *string, int len,
+    const struct nk_user_font *font)
+{
+    const struct nk_style_item *background;
+    const struct nk_style_item *cursor;
+    struct nk_text text;
+
+    /* select correct colors/images */
+    if (state & NK_WIDGET_STATE_HOVER) {
+        background = &style->hover;
+        cursor = &style->cursor_hover;
+        text.text = style->text_hover;
+    } else if (state & NK_WIDGET_STATE_ACTIVED) {
+        background = &style->hover;
+        cursor = &style->cursor_hover;
+        text.text = style->text_active;
+    } else {
+        background = &style->normal;
+        cursor = &style->cursor_normal;
+        text.text = style->text_normal;
+    }
+
+    /* draw background and cursor */
+    if (background->type == NK_STYLE_ITEM_COLOR) {
+        nk_fill_circle(out, *selector, style->border_color);
+        nk_fill_circle(out, nk_shrink_rect(*selector, style->border), background->data.color);
+    } else nk_draw_image(out, *selector, &background->data.image, nk_white);
+    if (active) {
+        if (cursor->type == NK_STYLE_ITEM_IMAGE)
+            nk_draw_image(out, *cursors, &cursor->data.image, nk_white);
+        else nk_fill_circle(out, *cursors, cursor->data.color);
+    }
+
+    text.padding.x = 0;
+    text.padding.y = 0;
+    text.background = style->text_background;
+    nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font);
+}
+
+NK_INTERN int
+nk_do_toggle(nk_flags *state,
+    struct nk_command_buffer *out, struct nk_rect r,
+    int *active, const char *str, int len, enum nk_toggle_type type,
+    const struct nk_style_toggle *style, const struct nk_input *in,
+    const struct nk_user_font *font)
+{
+    int was_active;
+    struct nk_rect bounds;
+    struct nk_rect select;
+    struct nk_rect cursor;
+    struct nk_rect label;
+
+    NK_ASSERT(style);
+    NK_ASSERT(out);
+    NK_ASSERT(font);
+    if (!out || !style || !font || !active)
+        return 0;
+
+    r.w = NK_MAX(r.w, font->height + 2 * style->padding.x);
+    r.h = NK_MAX(r.h, font->height + 2 * style->padding.y);
+
+    /* add additional touch padding for touch screen devices */
+    bounds.x = r.x - style->touch_padding.x;
+    bounds.y = r.y - style->touch_padding.y;
+    bounds.w = r.w + 2 * style->touch_padding.x;
+    bounds.h = r.h + 2 * style->touch_padding.y;
+
+    /* calculate the selector space */
+    select.w = font->height;
+    select.h = select.w;
+    select.y = r.y + r.h/2.0f - select.h/2.0f;
+    select.x = r.x;
+
+    /* calculate the bounds of the cursor inside the selector */
+    cursor.x = select.x + style->padding.x + style->border;
+    cursor.y = select.y + style->padding.y + style->border;
+    cursor.w = select.w - (2 * style->padding.x + 2 * style->border);
+    cursor.h = select.h - (2 * style->padding.y + 2 * style->border);
+
+    /* label behind the selector */
+    label.x = select.x + select.w + style->spacing;
+    label.y = select.y;
+    label.w = NK_MAX(r.x + r.w, label.x) - label.x;
+    label.h = select.w;
+
+    /* update selector */
+    was_active = *active;
+    *active = nk_toggle_behavior(in, bounds, state, *active);
+
+    /* draw selector */
+    if (style->draw_begin)
+        style->draw_begin(out, style->userdata);
+    if (type == NK_TOGGLE_CHECK) {
+        nk_draw_checkbox(out, *state, style, *active, &label, &select, &cursor, str, len, font);
+    } else {
+        nk_draw_option(out, *state, style, *active, &label, &select, &cursor, str, len, font);
+    }
+    if (style->draw_end)
+        style->draw_end(out, style->userdata);
+    return (was_active != *active);
+}
+
+/* ===============================================================
+ *
+ *                          SELECTABLE
+ *
+ * ===============================================================*/
+NK_INTERN void
+nk_draw_selectable(struct nk_command_buffer *out,
+    nk_flags state, const struct nk_style_selectable *style, int active,
+    const struct nk_rect *bounds, const struct nk_rect *icon, const struct nk_image *img,
+    const char *string, int len, nk_flags align, const struct nk_user_font *font)
+{
+    const struct nk_style_item *background;
+    struct nk_text text;
+    text.padding = style->padding;
+
+    /* select correct colors/images */
+    if (!active) {
+        if (state & NK_WIDGET_STATE_ACTIVED) {
+            background = &style->pressed;
+            text.text = style->text_pressed;
+        } else if (state & NK_WIDGET_STATE_HOVER) {
+            background = &style->hover;
+            text.text = style->text_hover;
+        } else {
+            background = &style->normal;
+            text.text = style->text_normal;
+        }
+    } else {
+        if (state & NK_WIDGET_STATE_ACTIVED) {
+            background = &style->pressed_active;
+            text.text = style->text_pressed_active;
+        } else if (state & NK_WIDGET_STATE_HOVER) {
+            background = &style->hover_active;
+            text.text = style->text_hover_active;
+        } else {
+            background = &style->normal_active;
+            text.text = style->text_normal_active;
+        }
+    }
+
+
+    /* draw selectable background and text */
+    if (background->type == NK_STYLE_ITEM_IMAGE) {
+        nk_draw_image(out, *bounds, &background->data.image, nk_white);
+        text.background = nk_rgba(0,0,0,0);
+    } else {
+        nk_fill_rect(out, *bounds, style->rounding, background->data.color);
+        text.background = background->data.color;
+    }
+    if (img && icon) nk_draw_image(out, *icon, img, nk_white);
+    nk_widget_text(out, *bounds, string, len, &text, align, font);
+}
+
+NK_INTERN int
+nk_do_selectable(nk_flags *state, struct nk_command_buffer *out,
+    struct nk_rect bounds, const char *str, int len, nk_flags align, int *value,
+    const struct nk_style_selectable *style, const struct nk_input *in,
+    const struct nk_user_font *font)
+{
+    int old_value;
+    struct nk_rect touch;
+
+    NK_ASSERT(state);
+    NK_ASSERT(out);
+    NK_ASSERT(str);
+    NK_ASSERT(len);
+    NK_ASSERT(value);
+    NK_ASSERT(style);
+    NK_ASSERT(font);
+
+    if (!state || !out || !str || !len || !value || !style || !font) return 0;
+    old_value = *value;
+
+    /* remove padding */
+    touch.x = bounds.x - style->touch_padding.x;
+    touch.y = bounds.y - style->touch_padding.y;
+    touch.w = bounds.w + style->touch_padding.x * 2;
+    touch.h = bounds.h + style->touch_padding.y * 2;
+
+    /* update button */
+    if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT))
+        *value = !(*value);
+
+    /* draw selectable */
+    if (style->draw_begin) style->draw_begin(out, style->userdata);
+    nk_draw_selectable(out, *state, style, *value, &bounds, 0,0, str, len, align, font);
+    if (style->draw_end) style->draw_end(out, style->userdata);
+    return old_value != *value;
+}
+
+NK_INTERN int
+nk_do_selectable_image(nk_flags *state, struct nk_command_buffer *out,
+    struct nk_rect bounds, const char *str, int len, nk_flags align, int *value,
+    const struct nk_image *img, const struct nk_style_selectable *style,
+    const struct nk_input *in, const struct nk_user_font *font)
+{
+    int old_value;
+    struct nk_rect touch;
+    struct nk_rect icon;
+
+    NK_ASSERT(state);
+    NK_ASSERT(out);
+    NK_ASSERT(str);
+    NK_ASSERT(len);
+    NK_ASSERT(value);
+    NK_ASSERT(style);
+    NK_ASSERT(font);
+
+    if (!state || !out || !str || !len || !value || !style || !font) return 0;
+    old_value = *value;
+
+    /* toggle behavior */
+    touch.x = bounds.x - style->touch_padding.x;
+    touch.y = bounds.y - style->touch_padding.y;
+    touch.w = bounds.w + style->touch_padding.x * 2;
+    touch.h = bounds.h + style->touch_padding.y * 2;
+    if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT))
+        *value = !(*value);
+
+    icon.y = bounds.y + style->padding.y;
+    icon.w = icon.h = bounds.h - 2 * style->padding.y;
+    if (align & NK_TEXT_ALIGN_LEFT) {
+        icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w);
+        icon.x = NK_MAX(icon.x, 0);
+    } else icon.x = bounds.x + 2 * style->padding.x;
+
+    icon.x += style->image_padding.x;
+    icon.y += style->image_padding.y;
+    icon.w -= 2 * style->image_padding.x;
+    icon.h -= 2 * style->image_padding.y;
+
+    /* draw selectable */
+    if (style->draw_begin) style->draw_begin(out, style->userdata);
+    nk_draw_selectable(out, *state, style, *value, &bounds, &icon, img, str, len, align, font);
+    if (style->draw_end) style->draw_end(out, style->userdata);
+    return old_value != *value;
+}
+
+
+/* ===============================================================
+ *
+ *                          SLIDER
+ *
+ * ===============================================================*/
+NK_INTERN float
+nk_slider_behavior(nk_flags *state, struct nk_rect *logical_cursor,
+    struct nk_rect *visual_cursor, struct nk_input *in,
+    struct nk_rect bounds, float slider_min, float slider_max, float slider_value,
+    float slider_step, float slider_steps)
+{
+    int left_mouse_down;
+    int left_mouse_click_in_cursor;
+
+    /* check if visual cursor is being dragged */
+    nk_widget_state_reset(state);
+    left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down;
+    left_mouse_click_in_cursor = in && nk_input_has_mouse_click_down_in_rect(in,
+            NK_BUTTON_LEFT, *visual_cursor, nk_true);
+
+    if (left_mouse_down && left_mouse_click_in_cursor)
+    {
+        float ratio = 0;
+        const float d = in->mouse.pos.x - (visual_cursor->x+visual_cursor->w*0.5f);
+        const float pxstep = bounds.w / slider_steps;
+
+        /* only update value if the next slider step is reached */
+        *state = NK_WIDGET_STATE_ACTIVE;
+        if (NK_ABS(d) >= pxstep) {
+            const float steps = (float)((int)(NK_ABS(d) / pxstep));
+            slider_value += (d > 0) ? (slider_step*steps) : -(slider_step*steps);
+            slider_value = NK_CLAMP(slider_min, slider_value, slider_max);
+            ratio = (slider_value - slider_min)/slider_step;
+            logical_cursor->x = bounds.x + (logical_cursor->w * ratio);
+            in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = logical_cursor->x;
+        }
+    }
+
+    /* slider widget state */
+    if (nk_input_is_mouse_hovering_rect(in, bounds))
+        *state = NK_WIDGET_STATE_HOVERED;
+    if (*state & NK_WIDGET_STATE_HOVER &&
+        !nk_input_is_mouse_prev_hovering_rect(in, bounds))
+        *state |= NK_WIDGET_STATE_ENTERED;
+    else if (nk_input_is_mouse_prev_hovering_rect(in, bounds))
+        *state |= NK_WIDGET_STATE_LEFT;
+    return slider_value;
+}
+
+NK_INTERN void
+nk_draw_slider(struct nk_command_buffer *out, nk_flags state,
+    const struct nk_style_slider *style, const struct nk_rect *bounds,
+    const struct nk_rect *visual_cursor, float min, float value, float max)
+{
+    struct nk_rect fill;
+    struct nk_rect bar;
+    const struct nk_style_item *background;
+
+    /* select correct slider images/colors */
+    struct nk_color bar_color;
+    const struct nk_style_item *cursor;
+
+    NK_UNUSED(min);
+    NK_UNUSED(max);
+    NK_UNUSED(value);
+
+    if (state & NK_WIDGET_STATE_ACTIVED) {
+        background = &style->active;
+        bar_color = style->bar_active;
+        cursor = &style->cursor_active;
+    } else if (state & NK_WIDGET_STATE_HOVER) {
+        background = &style->hover;
+        bar_color = style->bar_hover;
+        cursor = &style->cursor_hover;
+    } else {
+        background = &style->normal;
+        bar_color = style->bar_normal;
+        cursor = &style->cursor_normal;
+    }
+
+    /* calculate slider background bar */
+    bar.x = bounds->x;
+    bar.y = (visual_cursor->y + visual_cursor->h/2) - bounds->h/12;
+    bar.w = bounds->w;
+    bar.h = bounds->h/6;
+
+    /* filled background bar style */
+    fill.w = (visual_cursor->x + (visual_cursor->w/2.0f)) - bar.x;
+    fill.x = bar.x;
+    fill.y = bar.y;
+    fill.h = bar.h;
+
+    /* draw background */
+    if (background->type == NK_STYLE_ITEM_IMAGE) {
+        nk_draw_image(out, *bounds, &background->data.image, nk_white);
+    } else {
+        nk_fill_rect(out, *bounds, style->rounding, background->data.color);
+        nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color);
+    }
+
+    /* draw slider bar */
+    nk_fill_rect(out, bar, style->rounding, bar_color);
+    nk_fill_rect(out, fill, style->rounding, style->bar_filled);
+
+    /* draw cursor */
+    if (cursor->type == NK_STYLE_ITEM_IMAGE)
+        nk_draw_image(out, *visual_cursor, &cursor->data.image, nk_white);
+    else nk_fill_circle(out, *visual_cursor, cursor->data.color);
+}
+
+NK_INTERN float
+nk_do_slider(nk_flags *state,
+    struct nk_command_buffer *out, struct nk_rect bounds,
+    float min, float val, float max, float step,
+    const struct nk_style_slider *style, struct nk_input *in,
+    const struct nk_user_font *font)
+{
+    float slider_range;
+    float slider_min;
+    float slider_max;
+    float slider_value;
+    float slider_steps;
+    float cursor_offset;
+
+    struct nk_rect visual_cursor;
+    struct nk_rect logical_cursor;
+
+    NK_ASSERT(style);
+    NK_ASSERT(out);
+    if (!out || !style)
+        return 0;
+
+    /* remove padding from slider bounds */
+    bounds.x = bounds.x + style->padding.x;
+    bounds.y = bounds.y + style->padding.y;
+    bounds.h = NK_MAX(bounds.h, 2*style->padding.y);
+    bounds.w = NK_MAX(bounds.w, 2*style->padding.x + style->cursor_size.x);
+    bounds.w -= 2 * style->padding.x;
+    bounds.h -= 2 * style->padding.y;
+
+    /* optional buttons */
+    if (style->show_buttons) {
+        nk_flags ws;
+        struct nk_rect button;
+        button.y = bounds.y;
+        button.w = bounds.h;
+        button.h = bounds.h;
+
+        /* decrement button */
+        button.x = bounds.x;
+        if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, NK_BUTTON_DEFAULT,
+            &style->dec_button, in, font))
+            val -= step;
+
+        /* increment button */
+        button.x = (bounds.x + bounds.w) - button.w;
+        if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, NK_BUTTON_DEFAULT,
+            &style->inc_button, in, font))
+            val += step;
+
+        bounds.x = bounds.x + button.w + style->spacing.x;
+        bounds.w = bounds.w - (2*button.w + 2*style->spacing.x);
+    }
+
+    /* remove one cursor size to support visual cursor */
+    bounds.x += style->cursor_size.x*0.5f;
+    bounds.w -= style->cursor_size.x;
+
+    /* make sure the provided values are correct */
+    slider_max = NK_MAX(min, max);
+    slider_min = NK_MIN(min, max);
+    slider_value = NK_CLAMP(slider_min, val, slider_max);
+    slider_range = slider_max - slider_min;
+    slider_steps = slider_range / step;
+    cursor_offset = (slider_value - slider_min) / step;
+
+    /* calculate cursor
+    Basically you have two cursors. One for visual representation and interaction
+    and one for updating the actual cursor value. */
+    logical_cursor.h = bounds.h;
+    logical_cursor.w = bounds.w / slider_steps;
+    logical_cursor.x = bounds.x + (logical_cursor.w * cursor_offset);
+    logical_cursor.y = bounds.y;
+
+    visual_cursor.h = style->cursor_size.y;
+    visual_cursor.w = style->cursor_size.x;
+    visual_cursor.y = (bounds.y + bounds.h*0.5f) - visual_cursor.h*0.5f;
+    visual_cursor.x = logical_cursor.x - visual_cursor.w*0.5f;
+
+    slider_value = nk_slider_behavior(state, &logical_cursor, &visual_cursor,
+        in, bounds, slider_min, slider_max, slider_value, step, slider_steps);
+    visual_cursor.x = logical_cursor.x - visual_cursor.w*0.5f;
+
+    /* draw slider */
+    if (style->draw_begin) style->draw_begin(out, style->userdata);
+    nk_draw_slider(out, *state, style, &bounds, &visual_cursor, slider_min, slider_value, slider_max);
+    if (style->draw_end) style->draw_end(out, style->userdata);
+    return slider_value;
+}
+
+/* ===============================================================
+ *
+ *                          PROGRESSBAR
+ *
+ * ===============================================================*/
+NK_INTERN nk_size
+nk_progress_behavior(nk_flags *state, const struct nk_input *in,
+    struct nk_rect r, nk_size max, nk_size value, int modifiable)
+{
+    nk_widget_state_reset(state);
+    if (in && modifiable && nk_input_is_mouse_hovering_rect(in, r)) {
+        int left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down;
+        int left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in,
+            NK_BUTTON_LEFT, r, nk_true);
+
+        if (left_mouse_down && left_mouse_click_in_cursor) {
+            float ratio = NK_MAX(0, (float)(in->mouse.pos.x - r.x)) / (float)r.w;
+            value = (nk_size)NK_MAX(0,((float)max * ratio));
+            *state = NK_WIDGET_STATE_ACTIVE;
+        } else *state = NK_WIDGET_STATE_HOVERED;
+    }
+
+    /* set progressbar widget state */
+    if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, r))
+        *state |= NK_WIDGET_STATE_ENTERED;
+    else if (nk_input_is_mouse_prev_hovering_rect(in, r))
+        *state |= NK_WIDGET_STATE_LEFT;
+
+    if (!max) return value;
+    value = NK_MIN(value, max);
+    return value;
+}
+
+NK_INTERN void
+nk_draw_progress(struct nk_command_buffer *out, nk_flags state,
+    const struct nk_style_progress *style, const struct nk_rect *bounds,
+    const struct nk_rect *scursor, nk_size value, nk_size max)
+{
+    const struct nk_style_item *background;
+    const struct nk_style_item *cursor;
+
+    NK_UNUSED(max);
+    NK_UNUSED(value);
+
+    /* select correct colors/images to draw */
+    if (state & NK_WIDGET_STATE_ACTIVED) {
+        background = &style->active;
+        cursor = &style->cursor_active;
+    } else if (state & NK_WIDGET_STATE_HOVER){
+        background = &style->hover;
+        cursor = &style->cursor_hover;
+    } else {
+        background = &style->normal;
+        cursor = &style->cursor_normal;
+    }
+
+    /* draw background */
+    if (background->type == NK_STYLE_ITEM_COLOR) {
+        nk_fill_rect(out, *bounds, style->rounding, background->data.color);
+        nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color);
+    } else nk_draw_image(out, *bounds, &background->data.image, nk_white);
+
+    /* draw cursor */
+    if (cursor->type == NK_STYLE_ITEM_COLOR) {
+        nk_fill_rect(out, *scursor, style->rounding, cursor->data.color);
+        nk_stroke_rect(out, *scursor, style->rounding, style->border, style->border_color);
+    } else nk_draw_image(out, *scursor, &cursor->data.image, nk_white);
+}
+
+NK_INTERN nk_size
+nk_do_progress(nk_flags *state,
+    struct nk_command_buffer *out, struct nk_rect bounds,
+    nk_size value, nk_size max, int modifiable,
+    const struct nk_style_progress *style, const struct nk_input *in)
+{
+    float prog_scale;
+    nk_size prog_value;
+    struct nk_rect cursor;
+
+    NK_ASSERT(style);
+    NK_ASSERT(out);
+    if (!out || !style) return 0;
+
+    /* calculate progressbar cursor */
+    cursor.w = NK_MAX(bounds.w, 2 * style->padding.x + 2 * style->border);
+    cursor.h = NK_MAX(bounds.h, 2 * style->padding.y + 2 * style->border);
+    cursor = nk_pad_rect(bounds, nk_vec2(style->padding.x + style->border, style->padding.y + style->border));
+    prog_scale = (float)value / (float)max;
+    cursor.w = (bounds.w - 2) * prog_scale;
+
+    /* update progressbar */
+    prog_value = NK_MIN(value, max);
+    prog_value = nk_progress_behavior(state, in, bounds, max, prog_value, modifiable);
+
+    /* draw progressbar */
+    if (style->draw_begin) style->draw_begin(out, style->userdata);
+    nk_draw_progress(out, *state, style, &bounds, &cursor, value, max);
+    if (style->draw_end) style->draw_end(out, style->userdata);
+    return prog_value;
+}
+
+/* ===============================================================
+ *
+ *                          SCROLLBAR
+ *
+ * ===============================================================*/
+NK_INTERN float
+nk_scrollbar_behavior(nk_flags *state, struct nk_input *in,
+    int has_scrolling, const struct nk_rect *scroll,
+    const struct nk_rect *cursor, const struct nk_rect *empty0,
+    const struct nk_rect *empty1, float scroll_offset,
+    float target, float scroll_step, enum nk_orientation o)
+{
+    nk_flags ws = 0;
+    int left_mouse_down;
+    int left_mouse_click_in_cursor;
+    float scroll_delta;
+
+    nk_widget_state_reset(state);
+    if (!in) return scroll_offset;
+
+    left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down;
+    left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in,
+        NK_BUTTON_LEFT, *cursor, nk_true);
+    if (nk_input_is_mouse_hovering_rect(in, *scroll))
+        *state = NK_WIDGET_STATE_HOVERED;
+
+    scroll_delta = (o == NK_VERTICAL) ? in->mouse.scroll_delta.y: in->mouse.scroll_delta.x;
+    if (left_mouse_down && left_mouse_click_in_cursor) {
+        /* update cursor by mouse dragging */
+        float pixel, delta;
+        *state = NK_WIDGET_STATE_ACTIVE;
+        if (o == NK_VERTICAL) {
+            float cursor_y;
+            pixel = in->mouse.delta.y;
+            delta = (pixel / scroll->h) * target;
+            scroll_offset = NK_CLAMP(0, scroll_offset + delta, target - scroll->h);
+            cursor_y = scroll->y + ((scroll_offset/target) * scroll->h);
+            in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y = cursor_y + cursor->h/2.0f;
+        } else {
+            float cursor_x;
+            pixel = in->mouse.delta.x;
+            delta = (pixel / scroll->w) * target;
+            scroll_offset = NK_CLAMP(0, scroll_offset + delta, target - scroll->w);
+            cursor_x = scroll->x + ((scroll_offset/target) * scroll->w);
+            in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = cursor_x + cursor->w/2.0f;
+        }
+    } else if ((nk_input_is_key_pressed(in, NK_KEY_SCROLL_UP) && o == NK_VERTICAL && has_scrolling)||
+            nk_button_behavior(&ws, *empty0, in, NK_BUTTON_DEFAULT)) {
+        /* scroll page up by click on empty space or shortcut */
+        if (o == NK_VERTICAL)
+            scroll_offset = NK_MAX(0, scroll_offset - scroll->h);
+        else scroll_offset = NK_MAX(0, scroll_offset - scroll->w);
+    } else if ((nk_input_is_key_pressed(in, NK_KEY_SCROLL_DOWN) && o == NK_VERTICAL && has_scrolling) ||
+        nk_button_behavior(&ws, *empty1, in, NK_BUTTON_DEFAULT)) {
+        /* scroll page down by click on empty space or shortcut */
+        if (o == NK_VERTICAL)
+            scroll_offset = NK_MIN(scroll_offset + scroll->h, target - scroll->h);
+        else scroll_offset = NK_MIN(scroll_offset + scroll->w, target - scroll->w);
+    } else if (has_scrolling) {
+        if ((scroll_delta < 0 || (scroll_delta > 0))) {
+            /* update cursor by mouse scrolling */
+            scroll_offset = scroll_offset + scroll_step * (-scroll_delta);
+            if (o == NK_VERTICAL)
+                scroll_offset = NK_CLAMP(0, scroll_offset, target - scroll->h);
+            else scroll_offset = NK_CLAMP(0, scroll_offset, target - scroll->w);
+        } else if (nk_input_is_key_pressed(in, NK_KEY_SCROLL_START)) {
+            /* update cursor to the beginning  */
+            if (o == NK_VERTICAL) scroll_offset = 0;
+        } else if (nk_input_is_key_pressed(in, NK_KEY_SCROLL_END)) {
+            /* update cursor to the end */
+            if (o == NK_VERTICAL) scroll_offset = target - scroll->h;
+        }
+    }
+    if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, *scroll))
+        *state |= NK_WIDGET_STATE_ENTERED;
+    else if (nk_input_is_mouse_prev_hovering_rect(in, *scroll))
+        *state |= NK_WIDGET_STATE_LEFT;
+    return scroll_offset;
+}
+
+NK_INTERN void
+nk_draw_scrollbar(struct nk_command_buffer *out, nk_flags state,
+    const struct nk_style_scrollbar *style, const struct nk_rect *bounds,
+    const struct nk_rect *scroll)
+{
+    const struct nk_style_item *background;
+    const struct nk_style_item *cursor;
+
+    /* select correct colors/images to draw */
+    if (state & NK_WIDGET_STATE_ACTIVED) {
+        background = &style->active;
+        cursor = &style->cursor_active;
+    } else if (state & NK_WIDGET_STATE_HOVER) {
+        background = &style->hover;
+        cursor = &style->cursor_hover;
+    } else {
+        background = &style->normal;
+        cursor = &style->cursor_normal;
+    }
+
+    /* draw background */
+    if (background->type == NK_STYLE_ITEM_COLOR) {
+        nk_fill_rect(out, *bounds, style->rounding, background->data.color);
+        nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color);
+    } else {
+        nk_draw_image(out, *bounds, &background->data.image, nk_white);
+    }
+
+    /* draw cursor */
+    if (background->type == NK_STYLE_ITEM_COLOR) {
+        nk_fill_rect(out, *scroll, style->rounding_cursor, cursor->data.color);
+        nk_stroke_rect(out, *scroll, style->rounding_cursor, style->border_cursor, style->cursor_border_color);
+    } else nk_draw_image(out, *scroll, &cursor->data.image, nk_white);
+}
+
+NK_INTERN float
+nk_do_scrollbarv(nk_flags *state,
+    struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling,
+    float offset, float target, float step, float button_pixel_inc,
+    const struct nk_style_scrollbar *style, struct nk_input *in,
+    const struct nk_user_font *font)
+{
+    struct nk_rect empty_north;
+    struct nk_rect empty_south;
+    struct nk_rect cursor;
+
+    float scroll_step;
+    float scroll_offset;
+    float scroll_off;
+    float scroll_ratio;
+
+    NK_ASSERT(out);
+    NK_ASSERT(style);
+    NK_ASSERT(state);
+    if (!out || !style) return 0;
+
+    scroll.w = NK_MAX(scroll.w, 1);
+    scroll.h = NK_MAX(scroll.h, 0);
+    if (target <= scroll.h) return 0;
+
+    /* optional scrollbar buttons */
+    if (style->show_buttons) {
+        nk_flags ws;
+        float scroll_h;
+        struct nk_rect button;
+
+        button.x = scroll.x;
+        button.w = scroll.w;
+        button.h = scroll.w;
+
+        scroll_h = NK_MAX(scroll.h - 2 * button.h,0);
+        scroll_step = NK_MIN(step, button_pixel_inc);
+
+        /* decrement button */
+        button.y = scroll.y;
+        if (nk_do_button_symbol(&ws, out, button, style->dec_symbol,
+            NK_BUTTON_REPEATER, &style->dec_button, in, font))
+            offset = offset - scroll_step;
+
+        /* increment button */
+        button.y = scroll.y + scroll.h - button.h;
+        if (nk_do_button_symbol(&ws, out, button, style->inc_symbol,
+            NK_BUTTON_REPEATER, &style->inc_button, in, font))
+            offset = offset + scroll_step;
+
+        scroll.y = scroll.y + button.h;
+        scroll.h = scroll_h;
+    }
+
+    /* calculate scrollbar constants */
+    scroll_step = NK_MIN(step, scroll.h);
+    scroll_offset = NK_CLAMP(0, offset, target - scroll.h);
+    scroll_ratio = scroll.h / target;
+    scroll_off = scroll_offset / target;
+
+    /* calculate scrollbar cursor bounds */
+    cursor.h = NK_MAX((scroll_ratio * scroll.h) - (2*style->border + 2*style->padding.y), 0);
+    cursor.y = scroll.y + (scroll_off * scroll.h) + style->border + style->padding.y;
+    cursor.w = scroll.w - (2 * style->border + 2 * style->padding.x);
+    cursor.x = scroll.x + style->border + style->padding.x;
+
+    /* calculate empty space around cursor */
+    empty_north.x = scroll.x;
+    empty_north.y = scroll.y;
+    empty_north.w = scroll.w;
+    empty_north.h = NK_MAX(cursor.y - scroll.y, 0);
+
+    empty_south.x = scroll.x;
+    empty_south.y = cursor.y + cursor.h;
+    empty_south.w = scroll.w;
+    empty_south.h = NK_MAX((scroll.y + scroll.h) - (cursor.y + cursor.h), 0);
+
+    /* update scrollbar */
+    scroll_offset = nk_scrollbar_behavior(state, in, has_scrolling, &scroll, &cursor,
+        &empty_north, &empty_south, scroll_offset, target, scroll_step, NK_VERTICAL);
+    scroll_off = scroll_offset / target;
+    cursor.y = scroll.y + (scroll_off * scroll.h) + style->border_cursor + style->padding.y;
+
+    /* draw scrollbar */
+    if (style->draw_begin) style->draw_begin(out, style->userdata);
+    nk_draw_scrollbar(out, *state, style, &scroll, &cursor);
+    if (style->draw_end) style->draw_end(out, style->userdata);
+    return scroll_offset;
+}
+
+NK_INTERN float
+nk_do_scrollbarh(nk_flags *state,
+    struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling,
+    float offset, float target, float step, float button_pixel_inc,
+    const struct nk_style_scrollbar *style, struct nk_input *in,
+    const struct nk_user_font *font)
+{
+    struct nk_rect cursor;
+    struct nk_rect empty_west;
+    struct nk_rect empty_east;
+
+    float scroll_step;
+    float scroll_offset;
+    float scroll_off;
+    float scroll_ratio;
+
+    NK_ASSERT(out);
+    NK_ASSERT(style);
+    if (!out || !style) return 0;
+
+    /* scrollbar background */
+    scroll.h = NK_MAX(scroll.h, 1);
+    scroll.w = NK_MAX(scroll.w, 2 * scroll.h);
+    if (target <= scroll.w) return 0;
+
+    /* optional scrollbar buttons */
+    if (style->show_buttons) {
+        nk_flags ws;
+        float scroll_w;
+        struct nk_rect button;
+        button.y = scroll.y;
+        button.w = scroll.h;
+        button.h = scroll.h;
+
+        scroll_w = scroll.w - 2 * button.w;
+        scroll_step = NK_MIN(step, button_pixel_inc);
+
+        /* decrement button */
+        button.x = scroll.x;
+        if (nk_do_button_symbol(&ws, out, button, style->dec_symbol,
+            NK_BUTTON_REPEATER, &style->dec_button, in, font))
+            offset = offset - scroll_step;
+
+        /* increment button */
+        button.x = scroll.x + scroll.w - button.w;
+        if (nk_do_button_symbol(&ws, out, button, style->inc_symbol,
+            NK_BUTTON_REPEATER, &style->inc_button, in, font))
+            offset = offset + scroll_step;
+
+        scroll.x = scroll.x + button.w;
+        scroll.w = scroll_w;
+    }
+
+    /* calculate scrollbar constants */
+    scroll_step = NK_MIN(step, scroll.w);
+    scroll_offset = NK_CLAMP(0, offset, target - scroll.w);
+    scroll_ratio = scroll.w / target;
+    scroll_off = scroll_offset / target;
+
+    /* calculate cursor bounds */
+    cursor.w = (scroll_ratio * scroll.w) - (2*style->border + 2*style->padding.x);
+    cursor.x = scroll.x + (scroll_off * scroll.w) + style->border + style->padding.x;
+    cursor.h = scroll.h - (2 * style->border + 2 * style->padding.y);
+    cursor.y = scroll.y + style->border + style->padding.y;
+
+    /* calculate empty space around cursor */
+    empty_west.x = scroll.x;
+    empty_west.y = scroll.y;
+    empty_west.w = cursor.x - scroll.x;
+    empty_west.h = scroll.h;
+
+    empty_east.x = cursor.x + cursor.w;
+    empty_east.y = scroll.y;
+    empty_east.w = (scroll.x + scroll.w) - (cursor.x + cursor.w);
+    empty_east.h = scroll.h;
+
+    /* update scrollbar */
+    scroll_offset = nk_scrollbar_behavior(state, in, has_scrolling, &scroll, &cursor,
+        &empty_west, &empty_east, scroll_offset, target, scroll_step, NK_HORIZONTAL);
+    scroll_off = scroll_offset / target;
+    cursor.x = scroll.x + (scroll_off * scroll.w);
+
+    /* draw scrollbar */
+    if (style->draw_begin) style->draw_begin(out, style->userdata);
+    nk_draw_scrollbar(out, *state, style, &scroll, &cursor);
+    if (style->draw_end) style->draw_end(out, style->userdata);
+    return scroll_offset;
+}
+
+/* ===============================================================
+ *
+ *                          FILTER
+ *
+ * ===============================================================*/
+NK_API int nk_filter_default(const struct nk_text_edit *box, nk_rune unicode)
+{(void)unicode;NK_UNUSED(box);return nk_true;}
+
+NK_API int
+nk_filter_ascii(const struct nk_text_edit *box, nk_rune unicode)
+{
+    NK_UNUSED(box);
+    if (unicode > 128) return nk_false;
+    else return nk_true;
+}
+
+NK_API int
+nk_filter_float(const struct nk_text_edit *box, nk_rune unicode)
+{
+    NK_UNUSED(box);
+    if ((unicode < '0' || unicode > '9') && unicode != '.' && unicode != '-')
+        return nk_false;
+    else return nk_true;
+}
+
+NK_API int
+nk_filter_decimal(const struct nk_text_edit *box, nk_rune unicode)
+{
+    NK_UNUSED(box);
+    if ((unicode < '0' || unicode > '9') && unicode != '-')
+        return nk_false;
+    else return nk_true;
+}
+
+NK_API int
+nk_filter_hex(const struct nk_text_edit *box, nk_rune unicode)
+{
+    NK_UNUSED(box);
+    if ((unicode < '0' || unicode > '9') &&
+        (unicode < 'a' || unicode > 'f') &&
+        (unicode < 'A' || unicode > 'F'))
+        return nk_false;
+    else return nk_true;
+}
+
+NK_API int
+nk_filter_oct(const struct nk_text_edit *box, nk_rune unicode)
+{
+    NK_UNUSED(box);
+    if (unicode < '0' || unicode > '7')
+        return nk_false;
+    else return nk_true;
+}
+
+NK_API int
+nk_filter_binary(const struct nk_text_edit *box, nk_rune unicode)
+{
+    NK_UNUSED(box);
+    if (unicode != '0' && unicode != '1')
+        return nk_false;
+    else return nk_true;
+}
+
+/* ===============================================================
+ *
+ *                          EDIT
+ *
+ * ===============================================================*/
+NK_INTERN void
+nk_edit_draw_text(struct nk_command_buffer *out,
+    const struct nk_style_edit *style, float pos_x, float pos_y,
+    float x_offset, const char *text, int byte_len, float row_height,
+    const struct nk_user_font *font, struct nk_color background,
+    struct nk_color foreground, int is_selected)
+{
+    NK_ASSERT(out);
+    NK_ASSERT(font);
+    NK_ASSERT(style);
+    if (!text || !byte_len || !out || !style) return;
+
+    {int glyph_len = 0;
+    nk_rune unicode = 0;
+    int text_len = 0;
+    float line_width = 0;
+    float glyph_width;
+    const char *line = text;
+    float line_offset = 0;
+    int line_count = 0;
+
+    struct nk_text txt;
+    txt.padding = nk_vec2(0,0);
+    txt.background = background;
+    txt.text = foreground;
+
+    glyph_len = nk_utf_decode(text+text_len, &unicode, byte_len-text_len);
+    if (!glyph_len) return;
+    while ((text_len < byte_len) && glyph_len)
+    {
+        if (unicode == '\n') {
+            /* new line separator so draw previous line */
+            struct nk_rect label;
+            label.y = pos_y + line_offset;
+            label.h = row_height;
+            label.w = line_width;
+            label.x = pos_x;
+            if (!line_count)
+                label.x += x_offset;
+
+            if (is_selected) /* selection needs to draw different background color */
+                nk_fill_rect(out, label, 0, background);
+            nk_widget_text(out, label, line, (int)((text + text_len) - line),
+                &txt, NK_TEXT_CENTERED, font);
+
+            text_len++;
+            line_count++;
+            line_width = 0;
+            line = text + text_len;
+            line_offset += row_height;
+            glyph_len = nk_utf_decode(text + text_len, &unicode, (int)(byte_len-text_len));
+            continue;
+        }
+        if (unicode == '\r') {
+            text_len++;
+            glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len);
+            continue;
+        }
+        glyph_width = font->width(font->userdata, font->height, text+text_len, glyph_len);
+        line_width += (float)glyph_width;
+        text_len += glyph_len;
+        glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len);
+        continue;
+    }
+    if (line_width > 0) {
+        /* draw last line */
+        struct nk_rect label;
+        label.y = pos_y + line_offset;
+        label.h = row_height;
+        label.w = line_width;
+        label.x = pos_x;
+        if (!line_count)
+            label.x += x_offset;
+
+        if (is_selected)
+            nk_fill_rect(out, label, 0, background);
+        nk_widget_text(out, label, line, (int)((text + text_len) - line),
+            &txt, NK_TEXT_LEFT, font);
+    }}
+}
+
+NK_INTERN nk_flags
+nk_do_edit(nk_flags *state, struct nk_command_buffer *out,
+    struct nk_rect bounds, nk_flags flags, nk_plugin_filter filter,
+    struct nk_text_edit *edit, const struct nk_style_edit *style,
+    struct nk_input *in, const struct nk_user_font *font)
+{
+    struct nk_rect area;
+    nk_flags ret = 0;
+    float row_height;
+    char prev_state = 0;
+    char is_hovered = 0;
+    char select_all = 0;
+    char cursor_follow = 0;
+    struct nk_rect old_clip;
+    struct nk_rect clip;
+
+    NK_ASSERT(state);
+    NK_ASSERT(out);
+    NK_ASSERT(style);
+    if (!state || !out || !style)
+        return ret;
+
+    /* visible text area calculation */
+    area.x = bounds.x + style->padding.x + style->border;
+    area.y = bounds.y + style->padding.y + style->border;
+    area.w = bounds.w - (2.0f * style->padding.x + 2 * style->border);
+    area.h = bounds.h - (2.0f * style->padding.y + 2 * style->border);
+    if (flags & NK_EDIT_MULTILINE)
+        area.w = NK_MAX(0, area.w - style->scrollbar_size.x);
+    row_height = (flags & NK_EDIT_MULTILINE)? font->height + style->row_padding: area.h;
+
+    /* calculate clipping rectangle */
+    old_clip = out->clip;
+    nk_unify(&clip, &old_clip, area.x, area.y, area.x + area.w, area.y + area.h);
+
+    /* update edit state */
+    prev_state = (char)edit->active;
+    is_hovered = (char)nk_input_is_mouse_hovering_rect(in, bounds);
+    if (in && in->mouse.buttons[NK_BUTTON_LEFT].clicked && in->mouse.buttons[NK_BUTTON_LEFT].down) {
+        edit->active = NK_INBOX(in->mouse.pos.x, in->mouse.pos.y,
+                                bounds.x, bounds.y, bounds.w, bounds.h);
+    }
+
+    /* (de)activate text editor */
+    if (!prev_state && edit->active) {
+        const enum nk_text_edit_type type = (flags & NK_EDIT_MULTILINE) ?
+            NK_TEXT_EDIT_MULTI_LINE: NK_TEXT_EDIT_SINGLE_LINE;
+        nk_textedit_clear_state(edit, type, filter);
+        if (flags & NK_EDIT_ALWAYS_INSERT_MODE)
+            edit->mode = NK_TEXT_EDIT_MODE_INSERT;
+        if (flags & NK_EDIT_AUTO_SELECT)
+            select_all = nk_true;
+        if (flags & NK_EDIT_GOTO_END_ON_ACTIVATE) {
+            edit->cursor = edit->string.len;
+            in = 0;
+        }
+    } else if (!edit->active) edit->mode = NK_TEXT_EDIT_MODE_VIEW;
+    if (flags & NK_EDIT_READ_ONLY)
+        edit->mode = NK_TEXT_EDIT_MODE_VIEW;
+
+    ret = (edit->active) ? NK_EDIT_ACTIVE: NK_EDIT_INACTIVE;
+    if (prev_state != edit->active)
+        ret |= (edit->active) ? NK_EDIT_ACTIVATED: NK_EDIT_DEACTIVATED;
+
+    /* handle user input */
+    if (edit->active && in)
+    {
+        int shift_mod = in->keyboard.keys[NK_KEY_SHIFT].down;
+        const float mouse_x = (in->mouse.pos.x - area.x) + edit->scrollbar.x;
+        const float mouse_y = (in->mouse.pos.y - area.y) + edit->scrollbar.y;
+
+        /* mouse click handler */
+        is_hovered = (char)nk_input_is_mouse_hovering_rect(in, area);
+        if (select_all) {
+            nk_textedit_select_all(edit);
+        } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down &&
+            in->mouse.buttons[NK_BUTTON_LEFT].clicked) {
+            nk_textedit_click(edit, mouse_x, mouse_y, font, row_height);
+        } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down &&
+            (in->mouse.delta.x != 0.0f || in->mouse.delta.y != 0.0f)) {
+            nk_textedit_drag(edit, mouse_x, mouse_y, font, row_height);
+            cursor_follow = nk_true;
+        } else if (is_hovered && in->mouse.buttons[NK_BUTTON_RIGHT].clicked &&
+            in->mouse.buttons[NK_BUTTON_RIGHT].down) {
+            nk_textedit_key(edit, NK_KEY_TEXT_WORD_LEFT, nk_false, font, row_height);
+            nk_textedit_key(edit, NK_KEY_TEXT_WORD_RIGHT, nk_true, font, row_height);
+            cursor_follow = nk_true;
+        }
+
+        {int i; /* keyboard input */
+        int old_mode = edit->mode;
+        for (i = 0; i < NK_KEY_MAX; ++i) {
+            if (i == NK_KEY_ENTER || i == NK_KEY_TAB) continue; /* special case */
+            if (nk_input_is_key_pressed(in, (enum nk_keys)i)) {
+                nk_textedit_key(edit, (enum nk_keys)i, shift_mod, font, row_height);
+                cursor_follow = nk_true;
+            }
+        }
+        if (old_mode != edit->mode) {
+            in->keyboard.text_len = 0;
+        }}
+
+        /* text input */
+        edit->filter = filter;
+        if (in->keyboard.text_len) {
+            nk_textedit_text(edit, in->keyboard.text, in->keyboard.text_len);
+            cursor_follow = nk_true;
+            in->keyboard.text_len = 0;
+        }
+
+        /* enter key handler */
+        if (nk_input_is_key_pressed(in, NK_KEY_ENTER)) {
+            cursor_follow = nk_true;
+            if (flags & NK_EDIT_CTRL_ENTER_NEWLINE && shift_mod)
+                nk_textedit_text(edit, "\n", 1);
+            else if (flags & NK_EDIT_SIG_ENTER)
+                ret |= NK_EDIT_COMMITED;
+            else nk_textedit_text(edit, "\n", 1);
+        }
+
+        /* cut & copy handler */
+        {int copy= nk_input_is_key_pressed(in, NK_KEY_COPY);
+        int cut = nk_input_is_key_pressed(in, NK_KEY_CUT);
+        if ((copy || cut) && (flags & NK_EDIT_CLIPBOARD))
+        {
+            int glyph_len;
+            nk_rune unicode;
+            const char *text;
+            int b = edit->select_start;
+            int e = edit->select_end;
+
+            int begin = NK_MIN(b, e);
+            int end = NK_MAX(b, e);
+            text = nk_str_at_const(&edit->string, begin, &unicode, &glyph_len);
+            if (edit->clip.copy)
+                edit->clip.copy(edit->clip.userdata, text, end - begin);
+            if (cut && !(flags & NK_EDIT_READ_ONLY)){
+                nk_textedit_cut(edit);
+                cursor_follow = nk_true;
+            }
+        }}
+
+        /* paste handler */
+        {int paste = nk_input_is_key_pressed(in, NK_KEY_PASTE);
+        if (paste && (flags & NK_EDIT_CLIPBOARD) && edit->clip.paste) {
+            edit->clip.paste(edit->clip.userdata, edit);
+            cursor_follow = nk_true;
+        }}
+
+        /* tab handler */
+        {int tab = nk_input_is_key_pressed(in, NK_KEY_TAB);
+        if (tab && (flags & NK_EDIT_ALLOW_TAB)) {
+            nk_textedit_text(edit, "    ", 4);
+            cursor_follow = nk_true;
+        }}
+    }
+
+    /* set widget state */
+    if (edit->active)
+        *state = NK_WIDGET_STATE_ACTIVE;
+    else nk_widget_state_reset(state);
+
+    if (is_hovered)
+        *state |= NK_WIDGET_STATE_HOVERED;
+
+    /* DRAW EDIT */
+    {const char *text = nk_str_get_const(&edit->string);
+    int len = nk_str_len_char(&edit->string);
+
+    {/* select background colors/images  */
+    const struct nk_style_item *background;
+    if (*state & NK_WIDGET_STATE_ACTIVED)
+        background = &style->active;
+    else if (*state & NK_WIDGET_STATE_HOVER)
+        background = &style->hover;
+    else background = &style->normal;
+
+    /* draw background frame */
+    if (background->type == NK_STYLE_ITEM_COLOR) {
+        nk_stroke_rect(out, bounds, style->rounding, style->border, style->border_color);
+        nk_fill_rect(out, bounds, style->rounding, background->data.color);
+    } else nk_draw_image(out, bounds, &background->data.image, nk_white);}
+
+    area.w = NK_MAX(0, area.w - style->cursor_size);
+    if (edit->active)
+    {
+        int total_lines = 1;
+        struct nk_vec2 text_size = nk_vec2(0,0);
+
+        /* text pointer positions */
+        const char *cursor_ptr = 0;
+        const char *select_begin_ptr = 0;
+        const char *select_end_ptr = 0;
+
+        /* 2D pixel positions */
+        struct nk_vec2 cursor_pos = nk_vec2(0,0);
+        struct nk_vec2 selection_offset_start = nk_vec2(0,0);
+        struct nk_vec2 selection_offset_end = nk_vec2(0,0);
+
+        int selection_begin = NK_MIN(edit->select_start, edit->select_end);
+        int selection_end = NK_MAX(edit->select_start, edit->select_end);
+
+        /* calculate total line count + total space + cursor/selection position */
+        float line_width = 0.0f;
+        if (text && len)
+        {
+            /* utf8 encoding */
+            float glyph_width;
+            int glyph_len = 0;
+            nk_rune unicode = 0;
+            int text_len = 0;
+            int glyphs = 0;
+            int row_begin = 0;
+
+            glyph_len = nk_utf_decode(text, &unicode, len);
+            glyph_width = font->width(font->userdata, font->height, text, glyph_len);
+            line_width = 0;
+
+            /* iterate all lines */
+            while ((text_len < len) && glyph_len)
+            {
+                /* set cursor 2D position and line */
+                if (!cursor_ptr && glyphs == edit->cursor)
+                {
+                    int glyph_offset;
+                    struct nk_vec2 out_offset;
+                    struct nk_vec2 row_size;
+                    const char *remaining;
+
+                    /* calculate 2d position */
+                    cursor_pos.y = (float)(total_lines-1) * row_height;
+                    row_size = nk_text_calculate_text_bounds(font, text+row_begin,
+                                text_len-row_begin, row_height, &remaining,
+                                &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE);
+                    cursor_pos.x = row_size.x;
+                    cursor_ptr = text + text_len;
+                }
+
+                /* set start selection 2D position and line */
+                if (!select_begin_ptr && edit->select_start != edit->select_end &&
+                    glyphs == selection_begin)
+                {
+                    int glyph_offset;
+                    struct nk_vec2 out_offset;
+                    struct nk_vec2 row_size;
+                    const char *remaining;
+
+                    /* calculate 2d position */
+                    selection_offset_start.y = (float)(NK_MAX(total_lines-1,0)) * row_height;
+                    row_size = nk_text_calculate_text_bounds(font, text+row_begin,
+                                text_len-row_begin, row_height, &remaining,
+                                &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE);
+                    selection_offset_start.x = row_size.x;
+                    select_begin_ptr = text + text_len;
+                }
+
+                /* set end selection 2D position and line */
+                if (!select_end_ptr && edit->select_start != edit->select_end &&
+                    glyphs == selection_end)
+                {
+                    int glyph_offset;
+                    struct nk_vec2 out_offset;
+                    struct nk_vec2 row_size;
+                    const char *remaining;
+
+                    /* calculate 2d position */
+                    selection_offset_end.y = (float)(total_lines-1) * row_height;
+                    row_size = nk_text_calculate_text_bounds(font, text+row_begin,
+                                text_len-row_begin, row_height, &remaining,
+                                &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE);
+                    selection_offset_end.x = row_size.x;
+                    select_end_ptr = text + text_len;
+                }
+                if (unicode == '\n') {
+                    text_size.x = NK_MAX(text_size.x, line_width);
+                    total_lines++;
+                    line_width = 0;
+                    text_len++;
+                    glyphs++;
+                    row_begin = text_len;
+                    glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len);
+                    glyph_width = font->width(font->userdata, font->height, text+text_len, glyph_len);
+                    continue;
+                }
+
+                glyphs++;
+                text_len += glyph_len;
+                line_width += (float)glyph_width;
+
+                glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len);
+                glyph_width = font->width(font->userdata, font->height,
+                    text+text_len, glyph_len);
+                continue;
+            }
+            text_size.y = (float)total_lines * row_height;
+
+            /* handle case when cursor is at end of text buffer */
+            if (!cursor_ptr && edit->cursor == edit->string.len) {
+                cursor_pos.x = line_width;
+                cursor_pos.y = text_size.y - row_height;
+            }
+        }
+        {
+            /* scrollbar */
+            if (cursor_follow)
+            {
+                /* update scrollbar to follow cursor */
+                if (!(flags & NK_EDIT_NO_HORIZONTAL_SCROLL)) {
+                    /* horizontal scroll */
+                    const float scroll_increment = area.w * 0.25f;
+                    if (cursor_pos.x < edit->scrollbar.x)
+                        edit->scrollbar.x = (float)(int)NK_MAX(0.0f, cursor_pos.x - scroll_increment);
+                    if (cursor_pos.x >= edit->scrollbar.x + area.w)
+                        edit->scrollbar.x = (float)(int)NK_MAX(0.0f, cursor_pos.x);
+                } else edit->scrollbar.x = 0;
+
+                if (flags & NK_EDIT_MULTILINE) {
+                    /* vertical scroll */
+                    if (cursor_pos.y < edit->scrollbar.y)
+                        edit->scrollbar.y = NK_MAX(0.0f, cursor_pos.y - row_height);
+                    if (cursor_pos.y >= edit->scrollbar.y + area.h)
+                        edit->scrollbar.y = edit->scrollbar.y + row_height;
+                } else edit->scrollbar.y = 0;
+            }
+
+            /* scrollbar widget */
+            if (flags & NK_EDIT_MULTILINE)
+            {
+                nk_flags ws;
+                struct nk_rect scroll;
+                float scroll_target;
+                float scroll_offset;
+                float scroll_step;
+                float scroll_inc;
+
+                scroll = area;
+                scroll.x = (bounds.x + bounds.w - style->border) - style->scrollbar_size.x;
+                scroll.w = style->scrollbar_size.x;
+
+                scroll_offset = edit->scrollbar.y;
+                scroll_step = scroll.h * 0.10f;
+                scroll_inc = scroll.h * 0.01f;
+                scroll_target = text_size.y;
+                edit->scrollbar.y = nk_do_scrollbarv(&ws, out, scroll, 0,
+                        scroll_offset, scroll_target, scroll_step, scroll_inc,
+                        &style->scrollbar, in, font);
+            }
+        }
+
+        /* draw text */
+        {struct nk_color background_color;
+        struct nk_color text_color;
+        struct nk_color sel_background_color;
+        struct nk_color sel_text_color;
+        struct nk_color cursor_color;
+        struct nk_color cursor_text_color;
+        const struct nk_style_item *background;
+        nk_push_scissor(out, clip);
+
+        /* select correct colors to draw */
+        if (*state & NK_WIDGET_STATE_ACTIVED) {
+            background = &style->active;
+            text_color = style->text_active;
+            sel_text_color = style->selected_text_hover;
+            sel_background_color = style->selected_hover;
+            cursor_color = style->cursor_hover;
+            cursor_text_color = style->cursor_text_hover;
+        } else if (*state & NK_WIDGET_STATE_HOVER) {
+            background = &style->hover;
+            text_color = style->text_hover;
+            sel_text_color = style->selected_text_hover;
+            sel_background_color = style->selected_hover;
+            cursor_text_color = style->cursor_text_hover;
+            cursor_color = style->cursor_hover;
+        } else {
+            background = &style->normal;
+            text_color = style->text_normal;
+            sel_text_color = style->selected_text_normal;
+            sel_background_color = style->selected_normal;
+            cursor_color = style->cursor_normal;
+            cursor_text_color = style->cursor_text_normal;
+        }
+        if (background->type == NK_STYLE_ITEM_IMAGE)
+            background_color = nk_rgba(0,0,0,0);
+        else background_color = background->data.color;
+
+
+        if (edit->select_start == edit->select_end) {
+            /* no selection so just draw the complete text */
+            const char *begin = nk_str_get_const(&edit->string);
+            int l = nk_str_len_char(&edit->string);
+            nk_edit_draw_text(out, style, area.x - edit->scrollbar.x,
+                area.y - edit->scrollbar.y, 0, begin, l, row_height, font,
+                background_color, text_color, nk_false);
+        } else {
+            /* edit has selection so draw 1-3 text chunks */
+            if (edit->select_start != edit->select_end && selection_begin > 0){
+                /* draw unselected text before selection */
+                const char *begin = nk_str_get_const(&edit->string);
+                NK_ASSERT(select_begin_ptr);
+                nk_edit_draw_text(out, style, area.x - edit->scrollbar.x,
+                    area.y - edit->scrollbar.y, 0, begin, (int)(select_begin_ptr - begin),
+                    row_height, font, background_color, text_color, nk_false);
+            }
+            if (edit->select_start != edit->select_end) {
+                /* draw selected text */
+                NK_ASSERT(select_begin_ptr);
+                if (!select_end_ptr) {
+                    const char *begin = nk_str_get_const(&edit->string);
+                    select_end_ptr = begin + nk_str_len_char(&edit->string);
+                }
+                nk_edit_draw_text(out, style,
+                    area.x - edit->scrollbar.x,
+                    area.y + selection_offset_start.y - edit->scrollbar.y,
+                    selection_offset_start.x,
+                    select_begin_ptr, (int)(select_end_ptr - select_begin_ptr),
+                    row_height, font, sel_background_color, sel_text_color, nk_true);
+            }
+            if ((edit->select_start != edit->select_end &&
+                selection_end < edit->string.len))
+            {
+                /* draw unselected text after selected text */
+                const char *begin = select_end_ptr;
+                const char *end = nk_str_get_const(&edit->string) +
+                                    nk_str_len_char(&edit->string);
+                NK_ASSERT(select_end_ptr);
+                nk_edit_draw_text(out, style,
+                    area.x - edit->scrollbar.x,
+                    area.y + selection_offset_end.y - edit->scrollbar.y,
+                    selection_offset_end.x,
+                    begin, (int)(end - begin), row_height, font,
+                    background_color, text_color, nk_true);
+            }
+        }
+
+        /* cursor */
+        if (edit->select_start == edit->select_end)
+        {
+            if (edit->cursor >= nk_str_len(&edit->string) ||
+                (cursor_ptr && *cursor_ptr == '\n')) {
+                /* draw cursor at end of line */
+                struct nk_rect cursor;
+                cursor.w = style->cursor_size;
+                cursor.h = font->height;
+                cursor.x = area.x + cursor_pos.x - edit->scrollbar.x;
+                cursor.y = area.y + cursor_pos.y + row_height/2.0f - cursor.h/2.0f;
+                cursor.y -= edit->scrollbar.y;
+                nk_fill_rect(out, cursor, 0, cursor_color);
+            } else {
+                /* draw cursor inside text */
+                int glyph_len;
+                struct nk_rect label;
+                struct nk_text txt;
+
+                nk_rune unicode;
+                NK_ASSERT(cursor_ptr);
+                glyph_len = nk_utf_decode(cursor_ptr, &unicode, 4);
+
+                label.x = area.x + cursor_pos.x - edit->scrollbar.x;
+                label.y = area.y + cursor_pos.y - edit->scrollbar.y;
+                label.w = font->width(font->userdata, font->height, cursor_ptr, glyph_len);
+                label.h = row_height;
+
+                txt.padding = nk_vec2(0,0);
+                txt.background = cursor_color;;
+                txt.text = cursor_text_color;
+                nk_fill_rect(out, label, 0, cursor_color);
+                nk_widget_text(out, label, cursor_ptr, glyph_len, &txt, NK_TEXT_LEFT, font);
+            }
+        }}
+    } else {
+        /* not active so just draw text */
+        int l = nk_str_len_char(&edit->string);
+        const char *begin = nk_str_get_const(&edit->string);
+
+        const struct nk_style_item *background;
+        struct nk_color background_color;
+        struct nk_color text_color;
+        nk_push_scissor(out, clip);
+        if (*state & NK_WIDGET_STATE_ACTIVED) {
+            background = &style->active;
+            text_color = style->text_active;
+        } else if (*state & NK_WIDGET_STATE_HOVER) {
+            background = &style->hover;
+            text_color = style->text_hover;
+        } else {
+            background = &style->normal;
+            text_color = style->text_normal;
+        }
+        if (background->type == NK_STYLE_ITEM_IMAGE)
+            background_color = nk_rgba(0,0,0,0);
+        else background_color = background->data.color;
+        nk_edit_draw_text(out, style, area.x - edit->scrollbar.x,
+            area.y - edit->scrollbar.y, 0, begin, l, row_height, font,
+            background_color, text_color, nk_false);
+    }
+    nk_push_scissor(out, old_clip);}
+    return ret;
+}
+
+/* ===============================================================
+ *
+ *                          PROPERTY
+ *
+ * ===============================================================*/
+enum nk_property_status {
+    NK_PROPERTY_DEFAULT,
+    NK_PROPERTY_EDIT,
+    NK_PROPERTY_DRAG
+};
+enum nk_property_filter {
+    NK_FILTER_INT,
+    NK_FILTER_FLOAT
+};
+enum nk_property_kind {
+    NK_PROPERTY_INT,
+    NK_PROPERTY_FLOAT,
+    NK_PROPERTY_DOUBLE
+};
+union nk_property {
+    int i;
+    float f;
+    double d;
+};
+struct nk_property_variant {
+    enum nk_property_kind kind;
+    union nk_property value;
+    union nk_property min_value;
+    union nk_property max_value;
+    union nk_property step;
+};
+
+NK_INTERN void
+nk_drag_behavior(nk_flags *state, const struct nk_input *in,
+    struct nk_rect drag, struct nk_property_variant *variant,
+    float inc_per_pixel)
+{
+    int left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down;
+    int left_mouse_click_in_cursor = in &&
+        nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, drag, nk_true);
+
+    nk_widget_state_reset(state);
+    if (nk_input_is_mouse_hovering_rect(in, drag))
+        *state = NK_WIDGET_STATE_HOVERED;
+
+    if (left_mouse_down && left_mouse_click_in_cursor) {
+        float delta, pixels;
+        pixels = in->mouse.delta.x;
+        delta = pixels * inc_per_pixel;
+        switch (variant->kind) {
+        default: break;
+        case NK_PROPERTY_INT:
+            variant->value.i = variant->value.i + (int)delta;
+            variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i, variant->max_value.i);
+            break;
+        case NK_PROPERTY_FLOAT:
+            variant->value.f = variant->value.f + (float)delta;
+            variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f, variant->max_value.f);
+            break;
+        case NK_PROPERTY_DOUBLE:
+            variant->value.d = variant->value.d + (double)delta;
+            variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d, variant->max_value.d);
+            break;
+        }
+        *state = NK_WIDGET_STATE_ACTIVE;
+    }
+    if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, drag))
+        *state |= NK_WIDGET_STATE_ENTERED;
+    else if (nk_input_is_mouse_prev_hovering_rect(in, drag))
+        *state |= NK_WIDGET_STATE_LEFT;
+}
+
+NK_INTERN void
+nk_property_behavior(nk_flags *ws, const struct nk_input *in,
+    struct nk_rect property,  struct nk_rect label, struct nk_rect edit,
+    struct nk_rect empty, int *state, struct nk_property_variant *variant,
+    float inc_per_pixel)
+{
+    if (in && *state == NK_PROPERTY_DEFAULT) {
+        if (nk_button_behavior(ws, edit, in, NK_BUTTON_DEFAULT))
+            *state = NK_PROPERTY_EDIT;
+        else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, label, nk_true))
+            *state = NK_PROPERTY_DRAG;
+        else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, empty, nk_true))
+            *state = NK_PROPERTY_DRAG;
+    }
+    if (*state == NK_PROPERTY_DRAG) {
+        nk_drag_behavior(ws, in, property, variant, inc_per_pixel);
+        if (!(*ws & NK_WIDGET_STATE_ACTIVED)) *state = NK_PROPERTY_DEFAULT;
+    }
+}
+
+NK_INTERN void
+nk_draw_property(struct nk_command_buffer *out, const struct nk_style_property *style,
+    const struct nk_rect *bounds, const struct nk_rect *label, nk_flags state,
+    const char *name, int len, const struct nk_user_font *font)
+{
+    struct nk_text text;
+    const struct nk_style_item *background;
+
+    /* select correct background and text color */
+    if (state & NK_WIDGET_STATE_ACTIVED) {
+        background = &style->active;
+        text.text = style->label_active;
+    } else if (state & NK_WIDGET_STATE_HOVER) {
+        background = &style->hover;
+        text.text = style->label_hover;
+    } else {
+        background = &style->normal;
+        text.text = style->label_normal;
+    }
+
+    /* draw background */
+    if (background->type == NK_STYLE_ITEM_IMAGE) {
+        nk_draw_image(out, *bounds, &background->data.image, nk_white);
+        text.background = nk_rgba(0,0,0,0);
+    } else {
+        text.background = background->data.color;
+        nk_fill_rect(out, *bounds, style->rounding, background->data.color);
+        nk_stroke_rect(out, *bounds, style->rounding, style->border, background->data.color);
+    }
+
+    /* draw label */
+    text.padding = nk_vec2(0,0);
+    nk_widget_text(out, *label, name, len, &text, NK_TEXT_CENTERED, font);
+}
+
+NK_INTERN void
+nk_do_property(nk_flags *ws,
+    struct nk_command_buffer *out, struct nk_rect property,
+    const char *name, struct nk_property_variant *variant,
+    float inc_per_pixel, char *buffer, int *len,
+    int *state, int *cursor, int *select_begin, int *select_end,
+    const struct nk_style_property *style,
+    enum nk_property_filter filter, struct nk_input *in,
+    const struct nk_user_font *font, struct nk_text_edit *text_edit,
+    enum nk_button_behavior behavior)
+{
+    const nk_plugin_filter filters[] = {
+        nk_filter_decimal,
+        nk_filter_float
+    };
+    int active, old;
+    int num_len, name_len;
+    char string[NK_MAX_NUMBER_BUFFER];
+    float size;
+
+    char *dst = 0;
+    int *length;
+
+    struct nk_rect left;
+    struct nk_rect right;
+    struct nk_rect label;
+    struct nk_rect edit;
+    struct nk_rect empty;
+
+    /* left decrement button */
+    left.h = font->height/2;
+    left.w = left.h;
+    left.x = property.x + style->border + style->padding.x;
+    left.y = property.y + style->border + property.h/2.0f - left.h/2;
+
+    /* text label */
+    name_len = nk_strlen(name);
+    size = font->width(font->userdata, font->height, name, name_len);
+    label.x = left.x + left.w + style->padding.x;
+    label.w = (float)size + 2 * style->padding.x;
+    label.y = property.y + style->border + style->padding.y;
+    label.h = property.h - (2 * style->border + 2 * style->padding.y);
+
+    /* right increment button */
+    right.y = left.y;
+    right.w = left.w;
+    right.h = left.h;
+    right.x = property.x + property.w - (right.w + style->padding.x);
+
+    /* edit */
+    if (*state == NK_PROPERTY_EDIT) {
+        size = font->width(font->userdata, font->height, buffer, *len);
+        size += style->edit.cursor_size;
+        length = len;
+        dst = buffer;
+    } else {
+        switch (variant->kind) {
+        default: break;
+        case NK_PROPERTY_INT:
+            nk_itoa(string, variant->value.i);
+            num_len = nk_strlen(string);
+            break;
+        case NK_PROPERTY_FLOAT:
+            nk_dtoa(string, (double)variant->value.f);
+            num_len = nk_string_float_limit(string, NK_MAX_FLOAT_PRECISION);
+            break;
+        case NK_PROPERTY_DOUBLE:
+            nk_dtoa(string, variant->value.d);
+            num_len = nk_string_float_limit(string, NK_MAX_FLOAT_PRECISION);
+            break;
+        }
+        size = font->width(font->userdata, font->height, string, num_len);
+        dst = string;
+        length = &num_len;
+    }
+
+    edit.w =  (float)size + 2 * style->padding.x;
+    edit.w = NK_MIN(edit.w, right.x - (label.x + label.w));
+    edit.x = right.x - (edit.w + style->padding.x);
+    edit.y = property.y + style->border;
+    edit.h = property.h - (2 * style->border);
+
+    /* empty left space activator */
+    empty.w = edit.x - (label.x + label.w);
+    empty.x = label.x + label.w;
+    empty.y = property.y;
+    empty.h = property.h;
+
+    /* update property */
+    old = (*state == NK_PROPERTY_EDIT);
+    nk_property_behavior(ws, in, property, label, edit, empty, state, variant, inc_per_pixel);
+
+    /* draw property */
+    if (style->draw_begin) style->draw_begin(out, style->userdata);
+    nk_draw_property(out, style, &property, &label, *ws, name, name_len, font);
+    if (style->draw_end) style->draw_end(out, style->userdata);
+
+    /* execute right button  */
+    if (nk_do_button_symbol(ws, out, left, style->sym_left, behavior, &style->dec_button, in, font)) {
+        switch (variant->kind) {
+        default: break;
+        case NK_PROPERTY_INT:
+            variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i - variant->step.i, variant->max_value.i); break;
+        case NK_PROPERTY_FLOAT:
+            variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f - variant->step.f, variant->max_value.f); break;
+        case NK_PROPERTY_DOUBLE:
+            variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d - variant->step.d, variant->max_value.d); break;
+        }
+    }
+    /* execute left button  */
+    if (nk_do_button_symbol(ws, out, right, style->sym_right, behavior, &style->inc_button, in, font)) {
+        switch (variant->kind) {
+        default: break;
+        case NK_PROPERTY_INT:
+            variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i + variant->step.i, variant->max_value.i); break;
+        case NK_PROPERTY_FLOAT:
+            variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f + variant->step.f, variant->max_value.f); break;
+        case NK_PROPERTY_DOUBLE:
+            variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d + variant->step.d, variant->max_value.d); break;
+        }
+    }
+    if (old != NK_PROPERTY_EDIT && (*state == NK_PROPERTY_EDIT)) {
+        /* property has been activated so setup buffer */
+        NK_MEMCPY(buffer, dst, (nk_size)*length);
+        *cursor = nk_utf_len(buffer, *length);
+        *len = *length;
+        length = len;
+        dst = buffer;
+        active = 0;
+    } else active = (*state == NK_PROPERTY_EDIT);
+
+    /* execute and run text edit field */
+    nk_textedit_clear_state(text_edit, NK_TEXT_EDIT_SINGLE_LINE, filters[filter]);
+    text_edit->active = (unsigned char)active;
+    text_edit->string.len = *length;
+    text_edit->cursor = NK_CLAMP(0, *cursor, *length);
+    text_edit->select_start = NK_CLAMP(0,*select_begin, *length);
+    text_edit->select_end = NK_CLAMP(0,*select_end, *length);
+    text_edit->string.buffer.allocated = (nk_size)*length;
+    text_edit->string.buffer.memory.size = NK_MAX_NUMBER_BUFFER;
+    text_edit->string.buffer.memory.ptr = dst;
+    text_edit->string.buffer.size = NK_MAX_NUMBER_BUFFER;
+    text_edit->mode = NK_TEXT_EDIT_MODE_INSERT;
+    nk_do_edit(ws, out, edit, NK_EDIT_FIELD|NK_EDIT_AUTO_SELECT,
+        filters[filter], text_edit, &style->edit, (*state == NK_PROPERTY_EDIT) ? in: 0, font);
+
+    *length = text_edit->string.len;
+    *cursor = text_edit->cursor;
+    *select_begin = text_edit->select_start;
+    *select_end = text_edit->select_end;
+    if (text_edit->active && nk_input_is_key_pressed(in, NK_KEY_ENTER))
+        text_edit->active = nk_false;
+
+    if (active && !text_edit->active) {
+        /* property is now not active so convert edit text to value*/
+        *state = NK_PROPERTY_DEFAULT;
+        buffer[*len] = '\0';
+        switch (variant->kind) {
+        default: break;
+        case NK_PROPERTY_INT:
+            variant->value.i = nk_strtoi(buffer, 0);
+            variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i, variant->max_value.i);
+            break;
+        case NK_PROPERTY_FLOAT:
+            nk_string_float_limit(buffer, NK_MAX_FLOAT_PRECISION);
+            variant->value.f = nk_strtof(buffer, 0);
+            variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f, variant->max_value.f);
+            break;
+        case NK_PROPERTY_DOUBLE:
+            nk_string_float_limit(buffer, NK_MAX_FLOAT_PRECISION);
+            variant->value.d = nk_strtod(buffer, 0);
+            variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d, variant->max_value.d);
+            break;
+        }
+    }
+}
+/* ===============================================================
+ *
+ *                          COLOR PICKER
+ *
+ * ===============================================================*/
+NK_INTERN int
+nk_color_picker_behavior(nk_flags *state,
+    const struct nk_rect *bounds, const struct nk_rect *matrix,
+    const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar,
+    struct nk_color *color, const struct nk_input *in)
+{
+    float hsva[4];
+    int value_changed = 0;
+    int hsv_changed = 0;
+
+    NK_ASSERT(state);
+    NK_ASSERT(matrix);
+    NK_ASSERT(hue_bar);
+    NK_ASSERT(color);
+
+    /* color matrix */
+    nk_color_hsva_fv(hsva, *color);
+    if (nk_button_behavior(state, *matrix, in, NK_BUTTON_REPEATER)) {
+        hsva[1] = NK_SATURATE((in->mouse.pos.x - matrix->x) / (matrix->w-1));
+        hsva[2] = 1.0f - NK_SATURATE((in->mouse.pos.y - matrix->y) / (matrix->h-1));
+        value_changed = hsv_changed = 1;
+    }
+
+    /* hue bar */
+    if (nk_button_behavior(state, *hue_bar, in, NK_BUTTON_REPEATER)) {
+        hsva[0] = NK_SATURATE((in->mouse.pos.y - hue_bar->y) / (hue_bar->h-1));
+        value_changed = hsv_changed = 1;
+    }
+
+    /* alpha bar */
+    if (alpha_bar) {
+        if (nk_button_behavior(state, *alpha_bar, in, NK_BUTTON_REPEATER)) {
+            hsva[3] = 1.0f - NK_SATURATE((in->mouse.pos.y - alpha_bar->y) / (alpha_bar->h-1));
+            value_changed = 1;
+        }
+    }
+    nk_widget_state_reset(state);
+    if (hsv_changed) {
+        *color = nk_hsva_fv(hsva);
+        *state = NK_WIDGET_STATE_ACTIVE;
+    }
+    if (value_changed) {
+        color->a = (nk_byte)(hsva[3] * 255.0f);
+        *state = NK_WIDGET_STATE_ACTIVE;
+    }
+
+    /* set color picker widget state */
+    if (nk_input_is_mouse_hovering_rect(in, *bounds))
+        *state = NK_WIDGET_STATE_HOVERED;
+    if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, *bounds))
+        *state |= NK_WIDGET_STATE_ENTERED;
+    else if (nk_input_is_mouse_prev_hovering_rect(in, *bounds))
+        *state |= NK_WIDGET_STATE_LEFT;
+    return value_changed;
+}
+
+NK_INTERN void
+nk_draw_color_picker(struct nk_command_buffer *o, const struct nk_rect *matrix,
+    const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar,
+    struct nk_color color)
+{
+    NK_STORAGE const struct nk_color black = {0,0,0,255};
+    NK_STORAGE const struct nk_color white = {255, 255, 255, 255};
+    NK_STORAGE const struct nk_color black_trans = {0,0,0,0};
+
+    const float crosshair_size = 7.0f;
+    struct nk_color temp;
+    float hsva[4];
+    float line_y;
+    int i;
+
+    NK_ASSERT(o);
+    NK_ASSERT(matrix);
+    NK_ASSERT(hue_bar);
+
+    /* draw hue bar */
+    nk_color_hsv_fv(hsva, color);
+    for (i = 0; i < 6; ++i) {
+        NK_GLOBAL const struct nk_color hue_colors[] = {
+            {255, 0, 0, 255},
+            {255,255,0,255},
+            {0,255,0,255},
+            {0, 255,255,255},
+            {0,0,255,255},
+            {255, 0, 255, 255},
+            {255, 0, 0, 255}
+        };
+        nk_fill_rect_multi_color(o,
+            nk_rect(hue_bar->x, hue_bar->y + (float)i * (hue_bar->h/6.0f) + 0.5f,
+                hue_bar->w, (hue_bar->h/6.0f) + 0.5f), hue_colors[i], hue_colors[i],
+                hue_colors[i+1], hue_colors[i+1]);
+    }
+    line_y = (float)(int)(hue_bar->y + hsva[0] * matrix->h + 0.5f);
+    nk_stroke_line(o, hue_bar->x-1, line_y, hue_bar->x + hue_bar->w + 2,
+        line_y, 1, nk_rgb(255,255,255));
+
+    /* draw alpha bar */
+    if (alpha_bar) {
+        float alpha = NK_SATURATE((float)color.a/255.0f);
+        line_y = (float)(int)(alpha_bar->y +  (1.0f - alpha) * matrix->h + 0.5f);
+
+        nk_fill_rect_multi_color(o, *alpha_bar, white, white, black, black);
+        nk_stroke_line(o, alpha_bar->x-1, line_y, alpha_bar->x + alpha_bar->w + 2,
+            line_y, 1, nk_rgb(255,255,255));
+    }
+
+    /* draw color matrix */
+    temp = nk_hsv_f(hsva[0], 1.0f, 1.0f);
+    nk_fill_rect_multi_color(o, *matrix, white, temp, temp, white);
+    nk_fill_rect_multi_color(o, *matrix, black_trans, black_trans, black, black);
+
+    /* draw cross-hair */
+    {struct nk_vec2 p; float S = hsva[1]; float V = hsva[2];
+    p.x = (float)(int)(matrix->x + S * matrix->w);
+    p.y = (float)(int)(matrix->y + (1.0f - V) * matrix->h);
+    nk_stroke_line(o, p.x - crosshair_size, p.y, p.x-2, p.y, 1.0f, white);
+    nk_stroke_line(o, p.x + crosshair_size + 1, p.y, p.x+3, p.y, 1.0f, white);
+    nk_stroke_line(o, p.x, p.y + crosshair_size + 1, p.x, p.y+3, 1.0f, white);
+    nk_stroke_line(o, p.x, p.y - crosshair_size, p.x, p.y-2, 1.0f, white);}
+}
+
+NK_INTERN int
+nk_do_color_picker(nk_flags *state,
+    struct nk_command_buffer *out, struct nk_color *color,
+    enum nk_color_format fmt, struct nk_rect bounds,
+    struct nk_vec2 padding, const struct nk_input *in,
+    const struct nk_user_font *font)
+{
+    int ret = 0;
+    struct nk_rect matrix;
+    struct nk_rect hue_bar;
+    struct nk_rect alpha_bar;
+    float bar_w;
+
+    NK_ASSERT(out);
+    NK_ASSERT(color);
+    NK_ASSERT(state);
+    NK_ASSERT(font);
+    if (!out || !color || !state || !font)
+        return ret;
+
+    bar_w = font->height;
+    bounds.x += padding.x;
+    bounds.y += padding.x;
+    bounds.w -= 2 * padding.x;
+    bounds.h -= 2 * padding.y;
+
+    matrix.x = bounds.x;
+    matrix.y = bounds.y;
+    matrix.h = bounds.h;
+    matrix.w = bounds.w - (3 * padding.x + 2 * bar_w);
+
+    hue_bar.w = bar_w;
+    hue_bar.y = bounds.y;
+    hue_bar.h = matrix.h;
+    hue_bar.x = matrix.x + matrix.w + padding.x;
+
+    alpha_bar.x = hue_bar.x + hue_bar.w + padding.x;
+    alpha_bar.y = bounds.y;
+    alpha_bar.w = bar_w;
+    alpha_bar.h = matrix.h;
+
+    ret = nk_color_picker_behavior(state, &bounds, &matrix, &hue_bar,
+        (fmt == NK_RGBA) ? &alpha_bar:0, color, in);
+    nk_draw_color_picker(out, &matrix, &hue_bar, (fmt == NK_RGBA) ? &alpha_bar:0, *color);
+    return ret;
+}
+
+/* ==============================================================
+ *
+ *                          STYLE
+ *
+ * ===============================================================*/
+NK_API void nk_style_default(struct nk_context *ctx){nk_style_from_table(ctx, 0);}
+#define NK_COLOR_MAP(NK_COLOR)\
+    NK_COLOR(NK_COLOR_TEXT,                 175,175,175,255) \
+    NK_COLOR(NK_COLOR_WINDOW,               45, 45, 45, 255) \
+    NK_COLOR(NK_COLOR_HEADER,               40, 40, 40, 255) \
+    NK_COLOR(NK_COLOR_BORDER,               65, 65, 65, 255) \
+    NK_COLOR(NK_COLOR_BUTTON,               50, 50, 50, 255) \
+    NK_COLOR(NK_COLOR_BUTTON_HOVER,         40, 40, 40, 255) \
+    NK_COLOR(NK_COLOR_BUTTON_ACTIVE,        35, 35, 35, 255) \
+    NK_COLOR(NK_COLOR_TOGGLE,               100,100,100,255) \
+    NK_COLOR(NK_COLOR_TOGGLE_HOVER,         120,120,120,255) \
+    NK_COLOR(NK_COLOR_TOGGLE_CURSOR,        45, 45, 45, 255) \
+    NK_COLOR(NK_COLOR_SELECT,               45, 45, 45, 255) \
+    NK_COLOR(NK_COLOR_SELECT_ACTIVE,        35, 35, 35,255) \
+    NK_COLOR(NK_COLOR_SLIDER,               38, 38, 38, 255) \
+    NK_COLOR(NK_COLOR_SLIDER_CURSOR,        100,100,100,255) \
+    NK_COLOR(NK_COLOR_SLIDER_CURSOR_HOVER,  120,120,120,255) \
+    NK_COLOR(NK_COLOR_SLIDER_CURSOR_ACTIVE, 150,150,150,255) \
+    NK_COLOR(NK_COLOR_PROPERTY,             38, 38, 38, 255) \
+    NK_COLOR(NK_COLOR_EDIT,                 38, 38, 38, 255)  \
+    NK_COLOR(NK_COLOR_EDIT_CURSOR,          175,175,175,255) \
+    NK_COLOR(NK_COLOR_COMBO,                45, 45, 45, 255) \
+    NK_COLOR(NK_COLOR_CHART,                120,120,120,255) \
+    NK_COLOR(NK_COLOR_CHART_COLOR,          45, 45, 45, 255) \
+    NK_COLOR(NK_COLOR_CHART_COLOR_HIGHLIGHT,255, 0,  0, 255) \
+    NK_COLOR(NK_COLOR_SCROLLBAR,            40, 40, 40, 255) \
+    NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR,     100,100,100,255) \
+    NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR_HOVER,120,120,120,255) \
+    NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR_ACTIVE,150,150,150,255) \
+    NK_COLOR(NK_COLOR_TAB_HEADER,           40, 40, 40,255)
+
+NK_GLOBAL const struct nk_color
+nk_default_color_style[NK_COLOR_COUNT] = {
+#define NK_COLOR(a,b,c,d,e) {b,c,d,e},
+    NK_COLOR_MAP(NK_COLOR)
+#undef NK_COLOR
+};
+
+NK_GLOBAL const char *nk_color_names[NK_COLOR_COUNT] = {
+#define NK_COLOR(a,b,c,d,e) #a,
+    NK_COLOR_MAP(NK_COLOR)
+#undef NK_COLOR
+};
+
+NK_API const char *nk_style_get_color_by_name(enum nk_style_colors c)
+{return nk_color_names[c];}
+
+NK_API struct nk_style_item nk_style_item_image(struct nk_image img)
+{struct nk_style_item i; i.type = NK_STYLE_ITEM_IMAGE; i.data.image = img; return i;}
+
+NK_API struct nk_style_item nk_style_item_color(struct nk_color col)
+{struct nk_style_item i; i.type = NK_STYLE_ITEM_COLOR; i.data.color = col; return i;}
+
+NK_API struct nk_style_item nk_style_item_hide(void)
+{struct nk_style_item i; i.type = NK_STYLE_ITEM_COLOR; i.data.color = nk_rgba(0,0,0,0); return i;}
+
+NK_API void
+nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
+{
+    struct nk_style *style;
+    struct nk_style_text *text;
+    struct nk_style_button *button;
+    struct nk_style_toggle *toggle;
+    struct nk_style_selectable *select;
+    struct nk_style_slider *slider;
+    struct nk_style_progress *prog;
+    struct nk_style_scrollbar *scroll;
+    struct nk_style_edit *edit;
+    struct nk_style_property *property;
+    struct nk_style_combo *combo;
+    struct nk_style_chart *chart;
+    struct nk_style_tab *tab;
+    struct nk_style_window *win;
+
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    style = &ctx->style;
+    table = (!table) ? nk_default_color_style: table;
+
+    /* default text */
+    text = &style->text;
+    text->color = table[NK_COLOR_TEXT];
+    text->padding = nk_vec2(0,0);
+
+    /* default button */
+    button = &style->button;
+    nk_zero_struct(*button);
+    button->normal          = nk_style_item_color(table[NK_COLOR_BUTTON]);
+    button->hover           = nk_style_item_color(table[NK_COLOR_BUTTON_HOVER]);
+    button->active          = nk_style_item_color(table[NK_COLOR_BUTTON_ACTIVE]);
+    button->border_color    = table[NK_COLOR_BORDER];
+    button->text_background = table[NK_COLOR_BUTTON];
+    button->text_normal     = table[NK_COLOR_TEXT];
+    button->text_hover      = table[NK_COLOR_TEXT];
+    button->text_active     = table[NK_COLOR_TEXT];
+    button->padding         = nk_vec2(2.0f,2.0f);
+    button->image_padding   = nk_vec2(0.0f,0.0f);
+    button->touch_padding   = nk_vec2(0.0f, 0.0f);
+    button->userdata        = nk_handle_ptr(0);
+    button->text_alignment  = NK_TEXT_CENTERED;
+    button->border          = 1.0f;
+    button->rounding        = 4.0f;
+    button->draw_begin      = 0;
+    button->draw_end        = 0;
+
+    /* contextual button */
+    button = &style->contextual_button;
+    nk_zero_struct(*button);
+    button->normal          = nk_style_item_color(table[NK_COLOR_WINDOW]);
+    button->hover           = nk_style_item_color(table[NK_COLOR_BUTTON_HOVER]);
+    button->active          = nk_style_item_color(table[NK_COLOR_BUTTON_ACTIVE]);
+    button->border_color    = table[NK_COLOR_WINDOW];
+    button->text_background = table[NK_COLOR_WINDOW];
+    button->text_normal     = table[NK_COLOR_TEXT];
+    button->text_hover      = table[NK_COLOR_TEXT];
+    button->text_active     = table[NK_COLOR_TEXT];
+    button->padding         = nk_vec2(2.0f,2.0f);
+    button->touch_padding   = nk_vec2(0.0f,0.0f);
+    button->userdata        = nk_handle_ptr(0);
+    button->text_alignment  = NK_TEXT_CENTERED;
+    button->border          = 0.0f;
+    button->rounding        = 0.0f;
+    button->draw_begin      = 0;
+    button->draw_end        = 0;
+
+    /* menu button */
+    button = &style->menu_button;
+    nk_zero_struct(*button);
+    button->normal          = nk_style_item_color(table[NK_COLOR_WINDOW]);
+    button->hover           = nk_style_item_color(table[NK_COLOR_WINDOW]);
+    button->active          = nk_style_item_color(table[NK_COLOR_WINDOW]);
+    button->border_color    = table[NK_COLOR_WINDOW];
+    button->text_background = table[NK_COLOR_WINDOW];
+    button->text_normal     = table[NK_COLOR_TEXT];
+    button->text_hover      = table[NK_COLOR_TEXT];
+    button->text_active     = table[NK_COLOR_TEXT];
+    button->padding         = nk_vec2(2.0f,2.0f);
+    button->touch_padding   = nk_vec2(0.0f,0.0f);
+    button->userdata        = nk_handle_ptr(0);
+    button->text_alignment  = NK_TEXT_CENTERED;
+    button->border          = 0.0f;
+    button->rounding        = 1.0f;
+    button->draw_begin      = 0;
+    button->draw_end        = 0;
+
+    /* checkbox toggle */
+    toggle = &style->checkbox;
+    nk_zero_struct(*toggle);
+    toggle->normal          = nk_style_item_color(table[NK_COLOR_TOGGLE]);
+    toggle->hover           = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]);
+    toggle->active          = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]);
+    toggle->cursor_normal   = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]);
+    toggle->cursor_hover    = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]);
+    toggle->userdata        = nk_handle_ptr(0);
+    toggle->text_background = table[NK_COLOR_WINDOW];
+    toggle->text_normal     = table[NK_COLOR_TEXT];
+    toggle->text_hover      = table[NK_COLOR_TEXT];
+    toggle->text_active     = table[NK_COLOR_TEXT];
+    toggle->padding         = nk_vec2(2.0f, 2.0f);
+    toggle->touch_padding   = nk_vec2(0,0);
+    toggle->border_color    = nk_rgba(0,0,0,0);
+    toggle->border          = 0.0f;
+    toggle->spacing         = 4;
+
+    /* option toggle */
+    toggle = &style->option;
+    nk_zero_struct(*toggle);
+    toggle->normal          = nk_style_item_color(table[NK_COLOR_TOGGLE]);
+    toggle->hover           = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]);
+    toggle->active          = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]);
+    toggle->cursor_normal   = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]);
+    toggle->cursor_hover    = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]);
+    toggle->userdata        = nk_handle_ptr(0);
+    toggle->text_background = table[NK_COLOR_WINDOW];
+    toggle->text_normal     = table[NK_COLOR_TEXT];
+    toggle->text_hover      = table[NK_COLOR_TEXT];
+    toggle->text_active     = table[NK_COLOR_TEXT];
+    toggle->padding         = nk_vec2(3.0f, 3.0f);
+    toggle->touch_padding   = nk_vec2(0,0);
+    toggle->border_color    = nk_rgba(0,0,0,0);
+    toggle->border          = 0.0f;
+    toggle->spacing         = 4;
+
+    /* selectable */
+    select = &style->selectable;
+    nk_zero_struct(*select);
+    select->normal          = nk_style_item_color(table[NK_COLOR_SELECT]);
+    select->hover           = nk_style_item_color(table[NK_COLOR_SELECT]);
+    select->pressed         = nk_style_item_color(table[NK_COLOR_SELECT]);
+    select->normal_active   = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]);
+    select->hover_active    = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]);
+    select->pressed_active  = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]);
+    select->text_normal     = table[NK_COLOR_TEXT];
+    select->text_hover      = table[NK_COLOR_TEXT];
+    select->text_pressed    = table[NK_COLOR_TEXT];
+    select->text_normal_active  = table[NK_COLOR_TEXT];
+    select->text_hover_active   = table[NK_COLOR_TEXT];
+    select->text_pressed_active = table[NK_COLOR_TEXT];
+    select->padding         = nk_vec2(2.0f,2.0f);
+    select->touch_padding   = nk_vec2(0,0);
+    select->userdata        = nk_handle_ptr(0);
+    select->rounding        = 0.0f;
+    select->draw_begin      = 0;
+    select->draw_end        = 0;
+
+    /* slider */
+    slider = &style->slider;
+    nk_zero_struct(*slider);
+    slider->normal          = nk_style_item_hide();
+    slider->hover           = nk_style_item_hide();
+    slider->active          = nk_style_item_hide();
+    slider->bar_normal      = table[NK_COLOR_SLIDER];
+    slider->bar_hover       = table[NK_COLOR_SLIDER];
+    slider->bar_active      = table[NK_COLOR_SLIDER];
+    slider->bar_filled      = table[NK_COLOR_SLIDER_CURSOR];
+    slider->cursor_normal   = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR]);
+    slider->cursor_hover    = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_HOVER]);
+    slider->cursor_active   = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_ACTIVE]);
+    slider->inc_symbol      = NK_SYMBOL_TRIANGLE_RIGHT;
+    slider->dec_symbol      = NK_SYMBOL_TRIANGLE_LEFT;
+    slider->cursor_size     = nk_vec2(16,16);
+    slider->padding         = nk_vec2(2,2);
+    slider->spacing         = nk_vec2(2,2);
+    slider->userdata        = nk_handle_ptr(0);
+    slider->show_buttons    = nk_false;
+    slider->bar_height      = 8;
+    slider->rounding        = 0;
+    slider->draw_begin      = 0;
+    slider->draw_end        = 0;
+
+    /* slider buttons */
+    button = &style->slider.inc_button;
+    button->normal          = nk_style_item_color(nk_rgb(40,40,40));
+    button->hover           = nk_style_item_color(nk_rgb(42,42,42));
+    button->active          = nk_style_item_color(nk_rgb(44,44,44));
+    button->border_color    = nk_rgb(65,65,65);
+    button->text_background = nk_rgb(40,40,40);
+    button->text_normal     = nk_rgb(175,175,175);
+    button->text_hover      = nk_rgb(175,175,175);
+    button->text_active     = nk_rgb(175,175,175);
+    button->padding         = nk_vec2(8.0f,8.0f);
+    button->touch_padding   = nk_vec2(0.0f,0.0f);
+    button->userdata        = nk_handle_ptr(0);
+    button->text_alignment  = NK_TEXT_CENTERED;
+    button->border          = 1.0f;
+    button->rounding        = 0.0f;
+    button->draw_begin      = 0;
+    button->draw_end        = 0;
+    style->slider.dec_button = style->slider.inc_button;
+
+    /* progressbar */
+    prog = &style->progress;
+    nk_zero_struct(*prog);
+    prog->normal            = nk_style_item_color(table[NK_COLOR_SLIDER]);
+    prog->hover             = nk_style_item_color(table[NK_COLOR_SLIDER]);
+    prog->active            = nk_style_item_color(table[NK_COLOR_SLIDER]);
+    prog->cursor_normal     = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR]);
+    prog->cursor_hover      = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_HOVER]);
+    prog->cursor_active     = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_ACTIVE]);
+    prog->border_color      = nk_rgba(0,0,0,0);
+    prog->cursor_border_color = nk_rgba(0,0,0,0);
+    prog->userdata          = nk_handle_ptr(0);
+    prog->padding           = nk_vec2(4,4);
+    prog->rounding          = 0;
+    prog->border            = 0;
+    prog->cursor_rounding   = 0;
+    prog->cursor_border     = 0;
+    prog->draw_begin        = 0;
+    prog->draw_end          = 0;
+
+    /* scrollbars */
+    scroll = &style->scrollh;
+    nk_zero_struct(*scroll);
+    scroll->normal          = nk_style_item_color(table[NK_COLOR_SCROLLBAR]);
+    scroll->hover           = nk_style_item_color(table[NK_COLOR_SCROLLBAR]);
+    scroll->active          = nk_style_item_color(table[NK_COLOR_SCROLLBAR]);
+    scroll->cursor_normal   = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR]);
+    scroll->cursor_hover    = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR_HOVER]);
+    scroll->cursor_active   = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE]);
+    scroll->dec_symbol      = NK_SYMBOL_CIRCLE_SOLID;
+    scroll->inc_symbol      = NK_SYMBOL_CIRCLE_SOLID;
+    scroll->userdata        = nk_handle_ptr(0);
+    scroll->border_color    = table[NK_COLOR_SCROLLBAR];
+    scroll->cursor_border_color = table[NK_COLOR_SCROLLBAR];
+    scroll->padding         = nk_vec2(0,0);
+    scroll->show_buttons    = nk_false;
+    scroll->border          = 0;
+    scroll->rounding        = 0;
+    scroll->border_cursor   = 0;
+    scroll->rounding_cursor = 0;
+    scroll->draw_begin      = 0;
+    scroll->draw_end        = 0;
+    style->scrollv = style->scrollh;
+
+    /* scrollbars buttons */
+    button = &style->scrollh.inc_button;
+    button->normal          = nk_style_item_color(nk_rgb(40,40,40));
+    button->hover           = nk_style_item_color(nk_rgb(42,42,42));
+    button->active          = nk_style_item_color(nk_rgb(44,44,44));
+    button->border_color    = nk_rgb(65,65,65);
+    button->text_background = nk_rgb(40,40,40);
+    button->text_normal     = nk_rgb(175,175,175);
+    button->text_hover      = nk_rgb(175,175,175);
+    button->text_active     = nk_rgb(175,175,175);
+    button->padding         = nk_vec2(4.0f,4.0f);
+    button->touch_padding   = nk_vec2(0.0f,0.0f);
+    button->userdata        = nk_handle_ptr(0);
+    button->text_alignment  = NK_TEXT_CENTERED;
+    button->border          = 1.0f;
+    button->rounding        = 0.0f;
+    button->draw_begin      = 0;
+    button->draw_end        = 0;
+    style->scrollh.dec_button = style->scrollh.inc_button;
+    style->scrollv.inc_button = style->scrollh.inc_button;
+    style->scrollv.dec_button = style->scrollh.inc_button;
+
+    /* edit */
+    edit = &style->edit;
+    nk_zero_struct(*edit);
+    edit->normal            = nk_style_item_color(table[NK_COLOR_EDIT]);
+    edit->hover             = nk_style_item_color(table[NK_COLOR_EDIT]);
+    edit->active            = nk_style_item_color(table[NK_COLOR_EDIT]);
+    edit->cursor_normal     = table[NK_COLOR_TEXT];
+    edit->cursor_hover      = table[NK_COLOR_TEXT];
+    edit->cursor_text_normal= table[NK_COLOR_EDIT];
+    edit->cursor_text_hover = table[NK_COLOR_EDIT];
+    edit->border_color      = table[NK_COLOR_BORDER];
+    edit->text_normal       = table[NK_COLOR_TEXT];
+    edit->text_hover        = table[NK_COLOR_TEXT];
+    edit->text_active       = table[NK_COLOR_TEXT];
+    edit->selected_normal   = table[NK_COLOR_TEXT];
+    edit->selected_hover    = table[NK_COLOR_TEXT];
+    edit->selected_text_normal  = table[NK_COLOR_EDIT];
+    edit->selected_text_hover   = table[NK_COLOR_EDIT];
+    edit->scrollbar_size    = nk_vec2(10,10);
+    edit->scrollbar         = style->scrollv;
+    edit->padding           = nk_vec2(4,4);
+    edit->row_padding       = 2;
+    edit->cursor_size       = 4;
+    edit->border            = 1;
+    edit->rounding          = 0;
+
+    /* property */
+    property = &style->property;
+    nk_zero_struct(*property);
+    property->normal        = nk_style_item_color(table[NK_COLOR_PROPERTY]);
+    property->hover         = nk_style_item_color(table[NK_COLOR_PROPERTY]);
+    property->active        = nk_style_item_color(table[NK_COLOR_PROPERTY]);
+    property->border_color  = table[NK_COLOR_BORDER];
+    property->label_normal  = table[NK_COLOR_TEXT];
+    property->label_hover   = table[NK_COLOR_TEXT];
+    property->label_active  = table[NK_COLOR_TEXT];
+    property->sym_left      = NK_SYMBOL_TRIANGLE_LEFT;
+    property->sym_right     = NK_SYMBOL_TRIANGLE_RIGHT;
+    property->userdata      = nk_handle_ptr(0);
+    property->padding       = nk_vec2(4,4);
+    property->border        = 1;
+    property->rounding      = 10;
+    property->draw_begin    = 0;
+    property->draw_end      = 0;
+
+    /* property buttons */
+    button = &style->property.dec_button;
+    nk_zero_struct(*button);
+    button->normal          = nk_style_item_color(table[NK_COLOR_PROPERTY]);
+    button->hover           = nk_style_item_color(table[NK_COLOR_PROPERTY]);
+    button->active          = nk_style_item_color(table[NK_COLOR_PROPERTY]);
+    button->border_color    = nk_rgba(0,0,0,0);
+    button->text_background = table[NK_COLOR_PROPERTY];
+    button->text_normal     = table[NK_COLOR_TEXT];
+    button->text_hover      = table[NK_COLOR_TEXT];
+    button->text_active     = table[NK_COLOR_TEXT];
+    button->padding         = nk_vec2(0.0f,0.0f);
+    button->touch_padding   = nk_vec2(0.0f,0.0f);
+    button->userdata        = nk_handle_ptr(0);
+    button->text_alignment  = NK_TEXT_CENTERED;
+    button->border          = 0.0f;
+    button->rounding        = 0.0f;
+    button->draw_begin      = 0;
+    button->draw_end        = 0;
+    style->property.inc_button = style->property.dec_button;
+
+    /* property edit */
+    edit = &style->property.edit;
+    nk_zero_struct(*edit);
+    edit->normal            = nk_style_item_color(table[NK_COLOR_PROPERTY]);
+    edit->hover             = nk_style_item_color(table[NK_COLOR_PROPERTY]);
+    edit->active            = nk_style_item_color(table[NK_COLOR_PROPERTY]);
+    edit->border_color      = nk_rgba(0,0,0,0);
+    edit->cursor_normal     = table[NK_COLOR_TEXT];
+    edit->cursor_hover      = table[NK_COLOR_TEXT];
+    edit->cursor_text_normal= table[NK_COLOR_EDIT];
+    edit->cursor_text_hover = table[NK_COLOR_EDIT];
+    edit->text_normal       = table[NK_COLOR_TEXT];
+    edit->text_hover        = table[NK_COLOR_TEXT];
+    edit->text_active       = table[NK_COLOR_TEXT];
+    edit->selected_normal   = table[NK_COLOR_TEXT];
+    edit->selected_hover    = table[NK_COLOR_TEXT];
+    edit->selected_text_normal  = table[NK_COLOR_EDIT];
+    edit->selected_text_hover   = table[NK_COLOR_EDIT];
+    edit->padding           = nk_vec2(0,0);
+    edit->cursor_size       = 8;
+    edit->border            = 0;
+    edit->rounding          = 0;
+
+    /* chart */
+    chart = &style->chart;
+    nk_zero_struct(*chart);
+    chart->background       = nk_style_item_color(table[NK_COLOR_CHART]);
+    chart->border_color     = table[NK_COLOR_BORDER];
+    chart->selected_color   = table[NK_COLOR_CHART_COLOR_HIGHLIGHT];
+    chart->color            = table[NK_COLOR_CHART_COLOR];
+    chart->padding          = nk_vec2(4,4);
+    chart->border           = 0;
+    chart->rounding         = 0;
+
+    /* combo */
+    combo = &style->combo;
+    combo->normal           = nk_style_item_color(table[NK_COLOR_COMBO]);
+    combo->hover            = nk_style_item_color(table[NK_COLOR_COMBO]);
+    combo->active           = nk_style_item_color(table[NK_COLOR_COMBO]);
+    combo->border_color     = table[NK_COLOR_BORDER];
+    combo->label_normal     = table[NK_COLOR_TEXT];
+    combo->label_hover      = table[NK_COLOR_TEXT];
+    combo->label_active     = table[NK_COLOR_TEXT];
+    combo->sym_normal       = NK_SYMBOL_TRIANGLE_DOWN;
+    combo->sym_hover        = NK_SYMBOL_TRIANGLE_DOWN;
+    combo->sym_active       = NK_SYMBOL_TRIANGLE_DOWN;
+    combo->content_padding  = nk_vec2(4,4);
+    combo->button_padding   = nk_vec2(0,4);
+    combo->spacing          = nk_vec2(4,0);
+    combo->border           = 1;
+    combo->rounding         = 0;
+
+    /* combo button */
+    button = &style->combo.button;
+    nk_zero_struct(*button);
+    button->normal          = nk_style_item_color(table[NK_COLOR_COMBO]);
+    button->hover           = nk_style_item_color(table[NK_COLOR_COMBO]);
+    button->active          = nk_style_item_color(table[NK_COLOR_COMBO]);
+    button->border_color    = nk_rgba(0,0,0,0);
+    button->text_background = table[NK_COLOR_COMBO];
+    button->text_normal     = table[NK_COLOR_TEXT];
+    button->text_hover      = table[NK_COLOR_TEXT];
+    button->text_active     = table[NK_COLOR_TEXT];
+    button->padding         = nk_vec2(2.0f,2.0f);
+    button->touch_padding   = nk_vec2(0.0f,0.0f);
+    button->userdata        = nk_handle_ptr(0);
+    button->text_alignment  = NK_TEXT_CENTERED;
+    button->border          = 0.0f;
+    button->rounding        = 0.0f;
+    button->draw_begin      = 0;
+    button->draw_end        = 0;
+
+    /* tab */
+    tab = &style->tab;
+    tab->background         = nk_style_item_color(table[NK_COLOR_TAB_HEADER]);
+    tab->border_color       = table[NK_COLOR_BORDER];
+    tab->text               = table[NK_COLOR_TEXT];
+    tab->sym_minimize       = NK_SYMBOL_TRIANGLE_RIGHT;
+    tab->sym_maximize       = NK_SYMBOL_TRIANGLE_DOWN;
+    tab->padding            = nk_vec2(4,4);
+    tab->spacing            = nk_vec2(4,4);
+    tab->indent             = 10.0f;
+    tab->border             = 1;
+    tab->rounding           = 0;
+
+    /* tab button */
+    button = &style->tab.tab_minimize_button;
+    nk_zero_struct(*button);
+    button->normal          = nk_style_item_color(table[NK_COLOR_TAB_HEADER]);
+    button->hover           = nk_style_item_color(table[NK_COLOR_TAB_HEADER]);
+    button->active          = nk_style_item_color(table[NK_COLOR_TAB_HEADER]);
+    button->border_color    = nk_rgba(0,0,0,0);
+    button->text_background = table[NK_COLOR_TAB_HEADER];
+    button->text_normal     = table[NK_COLOR_TEXT];
+    button->text_hover      = table[NK_COLOR_TEXT];
+    button->text_active     = table[NK_COLOR_TEXT];
+    button->padding         = nk_vec2(2.0f,2.0f);
+    button->touch_padding   = nk_vec2(0.0f,0.0f);
+    button->userdata        = nk_handle_ptr(0);
+    button->text_alignment  = NK_TEXT_CENTERED;
+    button->border          = 0.0f;
+    button->rounding        = 0.0f;
+    button->draw_begin      = 0;
+    button->draw_end        = 0;
+    style->tab.tab_maximize_button =*button;
+
+    /* node button */
+    button = &style->tab.node_minimize_button;
+    nk_zero_struct(*button);
+    button->normal          = nk_style_item_color(table[NK_COLOR_WINDOW]);
+    button->hover           = nk_style_item_color(table[NK_COLOR_WINDOW]);
+    button->active          = nk_style_item_color(table[NK_COLOR_WINDOW]);
+    button->border_color    = nk_rgba(0,0,0,0);
+    button->text_background = table[NK_COLOR_TAB_HEADER];
+    button->text_normal     = table[NK_COLOR_TEXT];
+    button->text_hover      = table[NK_COLOR_TEXT];
+    button->text_active     = table[NK_COLOR_TEXT];
+    button->padding         = nk_vec2(2.0f,2.0f);
+    button->touch_padding   = nk_vec2(0.0f,0.0f);
+    button->userdata        = nk_handle_ptr(0);
+    button->text_alignment  = NK_TEXT_CENTERED;
+    button->border          = 0.0f;
+    button->rounding        = 0.0f;
+    button->draw_begin      = 0;
+    button->draw_end        = 0;
+    style->tab.node_maximize_button =*button;
+
+    /* window header */
+    win = &style->window;
+    win->header.align = NK_HEADER_RIGHT;
+    win->header.close_symbol = NK_SYMBOL_X;
+    win->header.minimize_symbol = NK_SYMBOL_MINUS;
+    win->header.maximize_symbol = NK_SYMBOL_PLUS;
+    win->header.normal = nk_style_item_color(table[NK_COLOR_HEADER]);
+    win->header.hover = nk_style_item_color(table[NK_COLOR_HEADER]);
+    win->header.active = nk_style_item_color(table[NK_COLOR_HEADER]);
+    win->header.label_normal = table[NK_COLOR_TEXT];
+    win->header.label_hover = table[NK_COLOR_TEXT];
+    win->header.label_active = table[NK_COLOR_TEXT];
+    win->header.label_padding = nk_vec2(4,4);
+    win->header.padding = nk_vec2(4,4);
+    win->header.spacing = nk_vec2(0,0);
+
+    /* window header close button */
+    button = &style->window.header.close_button;
+    nk_zero_struct(*button);
+    button->normal          = nk_style_item_color(table[NK_COLOR_HEADER]);
+    button->hover           = nk_style_item_color(table[NK_COLOR_HEADER]);
+    button->active          = nk_style_item_color(table[NK_COLOR_HEADER]);
+    button->border_color    = nk_rgba(0,0,0,0);
+    button->text_background = table[NK_COLOR_HEADER];
+    button->text_normal     = table[NK_COLOR_TEXT];
+    button->text_hover      = table[NK_COLOR_TEXT];
+    button->text_active     = table[NK_COLOR_TEXT];
+    button->padding         = nk_vec2(0.0f,0.0f);
+    button->touch_padding   = nk_vec2(0.0f,0.0f);
+    button->userdata        = nk_handle_ptr(0);
+    button->text_alignment  = NK_TEXT_CENTERED;
+    button->border          = 0.0f;
+    button->rounding        = 0.0f;
+    button->draw_begin      = 0;
+    button->draw_end        = 0;
+
+    /* window header minimize button */
+    button = &style->window.header.minimize_button;
+    nk_zero_struct(*button);
+    button->normal          = nk_style_item_color(table[NK_COLOR_HEADER]);
+    button->hover           = nk_style_item_color(table[NK_COLOR_HEADER]);
+    button->active          = nk_style_item_color(table[NK_COLOR_HEADER]);
+    button->border_color    = nk_rgba(0,0,0,0);
+    button->text_background = table[NK_COLOR_HEADER];
+    button->text_normal     = table[NK_COLOR_TEXT];
+    button->text_hover      = table[NK_COLOR_TEXT];
+    button->text_active     = table[NK_COLOR_TEXT];
+    button->padding         = nk_vec2(0.0f,0.0f);
+    button->touch_padding   = nk_vec2(0.0f,0.0f);
+    button->userdata        = nk_handle_ptr(0);
+    button->text_alignment  = NK_TEXT_CENTERED;
+    button->border          = 0.0f;
+    button->rounding        = 0.0f;
+    button->draw_begin      = 0;
+    button->draw_end        = 0;
+
+    /* window */
+    win->background = table[NK_COLOR_WINDOW];
+    win->fixed_background = nk_style_item_color(table[NK_COLOR_WINDOW]);
+    win->border_color = table[NK_COLOR_BORDER];
+    win->popup_border_color = table[NK_COLOR_BORDER];
+    win->combo_border_color = table[NK_COLOR_BORDER];
+    win->contextual_border_color = table[NK_COLOR_BORDER];
+    win->menu_border_color = table[NK_COLOR_BORDER];
+    win->group_border_color = table[NK_COLOR_BORDER];
+    win->tooltip_border_color = table[NK_COLOR_BORDER];
+    win->scaler = nk_style_item_color(table[NK_COLOR_TEXT]);
+
+    win->rounding = 0.0f;
+    win->spacing = nk_vec2(4,4);
+    win->scrollbar_size = nk_vec2(10,10);
+    win->min_size = nk_vec2(64,64);
+
+    win->combo_border = 1.0f;
+    win->contextual_border = 1.0f;
+    win->menu_border = 1.0f;
+    win->group_border = 1.0f;
+    win->tooltip_border = 1.0f;
+    win->popup_border = 1.0f;
+    win->border = 2.0f;
+    win->min_row_height_padding = 8;
+
+    win->padding = nk_vec2(4,4);
+    win->group_padding = nk_vec2(4,4);
+    win->popup_padding = nk_vec2(4,4);
+    win->combo_padding = nk_vec2(4,4);
+    win->contextual_padding = nk_vec2(4,4);
+    win->menu_padding = nk_vec2(4,4);
+    win->tooltip_padding = nk_vec2(4,4);
+}
+
+NK_API void
+nk_style_set_font(struct nk_context *ctx, const struct nk_user_font *font)
+{
+    struct nk_style *style;
+    NK_ASSERT(ctx);
+
+    if (!ctx) return;
+    style = &ctx->style;
+    style->font = font;
+    ctx->stacks.fonts.head = 0;
+    if (ctx->current)
+        nk_layout_reset_min_row_height(ctx);
+}
+
+NK_API int
+nk_style_push_font(struct nk_context *ctx, const struct nk_user_font *font)
+{
+    struct nk_config_stack_user_font *font_stack;
+    struct nk_config_stack_user_font_element *element;
+
+    NK_ASSERT(ctx);
+    if (!ctx) return 0;
+
+    font_stack = &ctx->stacks.fonts;
+    NK_ASSERT(font_stack->head < (int)NK_LEN(font_stack->elements));
+    if (font_stack->head >= (int)NK_LEN(font_stack->elements))
+        return 0;
+
+    element = &font_stack->elements[font_stack->head++];
+    element->address = &ctx->style.font;
+    element->old_value = ctx->style.font;
+    ctx->style.font = font;
+    return 1;
+}
+
+NK_API int
+nk_style_pop_font(struct nk_context *ctx)
+{
+    struct nk_config_stack_user_font *font_stack;
+    struct nk_config_stack_user_font_element *element;
+
+    NK_ASSERT(ctx);
+    if (!ctx) return 0;
+
+    font_stack = &ctx->stacks.fonts;
+    NK_ASSERT(font_stack->head > 0);
+    if (font_stack->head < 1)
+        return 0;
+
+    element = &font_stack->elements[--font_stack->head];
+    *element->address = element->old_value;
+    return 1;
+}
+
+#define NK_STYLE_PUSH_IMPLEMENATION(prefix, type, stack) \
+nk_style_push_##type(struct nk_context *ctx, prefix##_##type *address, prefix##_##type value)\
+{\
+    struct nk_config_stack_##type * type_stack;\
+    struct nk_config_stack_##type##_element *element;\
+    NK_ASSERT(ctx);\
+    if (!ctx) return 0;\
+    type_stack = &ctx->stacks.stack;\
+    NK_ASSERT(type_stack->head < (int)NK_LEN(type_stack->elements));\
+    if (type_stack->head >= (int)NK_LEN(type_stack->elements))\
+        return 0;\
+    element = &type_stack->elements[type_stack->head++];\
+    element->address = address;\
+    element->old_value = *address;\
+    *address = value;\
+    return 1;\
+}
+
+#define NK_STYLE_POP_IMPLEMENATION(type, stack) \
+nk_style_pop_##type(struct nk_context *ctx)\
+{\
+    struct nk_config_stack_##type *type_stack;\
+    struct nk_config_stack_##type##_element *element;\
+    NK_ASSERT(ctx);\
+    if (!ctx) return 0;\
+    type_stack = &ctx->stacks.stack;\
+    NK_ASSERT(type_stack->head > 0);\
+    if (type_stack->head < 1)\
+        return 0;\
+    element = &type_stack->elements[--type_stack->head];\
+    *element->address = element->old_value;\
+    return 1;\
+}
+
+NK_API int NK_STYLE_PUSH_IMPLEMENATION(struct nk, style_item, style_items)
+NK_API int NK_STYLE_PUSH_IMPLEMENATION(nk,float, floats)
+NK_API int NK_STYLE_PUSH_IMPLEMENATION(struct nk, vec2, vectors)
+NK_API int NK_STYLE_PUSH_IMPLEMENATION(nk,flags, flags)
+NK_API int NK_STYLE_PUSH_IMPLEMENATION(struct nk,color, colors)
+
+NK_API int NK_STYLE_POP_IMPLEMENATION(style_item, style_items)
+NK_API int NK_STYLE_POP_IMPLEMENATION(float,floats)
+NK_API int NK_STYLE_POP_IMPLEMENATION(vec2, vectors)
+NK_API int NK_STYLE_POP_IMPLEMENATION(flags,flags)
+NK_API int NK_STYLE_POP_IMPLEMENATION(color,colors)
+
+NK_API int
+nk_style_set_cursor(struct nk_context *ctx, enum nk_style_cursor c)
+{
+    struct nk_style *style;
+    NK_ASSERT(ctx);
+    if (!ctx) return 0;
+    style = &ctx->style;
+    if (style->cursors[c]) {
+        style->cursor_active = style->cursors[c];
+        return 1;
+    }
+    return 0;
+}
+
+NK_API void
+nk_style_show_cursor(struct nk_context *ctx)
+{
+    ctx->style.cursor_visible = nk_true;
+}
+
+NK_API void
+nk_style_hide_cursor(struct nk_context *ctx)
+{
+    ctx->style.cursor_visible = nk_false;
+}
+
+NK_API void
+nk_style_load_cursor(struct nk_context *ctx, enum nk_style_cursor cursor,
+    const struct nk_cursor *c)
+{
+    struct nk_style *style;
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    style = &ctx->style;
+    style->cursors[cursor] = c;
+}
+
+NK_API void
+nk_style_load_all_cursors(struct nk_context *ctx, struct nk_cursor *cursors)
+{
+    int i = 0;
+    struct nk_style *style;
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    style = &ctx->style;
+    for (i = 0; i < NK_CURSOR_COUNT; ++i)
+        style->cursors[i] = &cursors[i];
+    style->cursor_visible = nk_true;
+}
+
+/* ===============================================================
+ *
+ *                          POOL
+ *
+ * ===============================================================*/
+NK_INTERN void
+nk_pool_init(struct nk_pool *pool, struct nk_allocator *alloc,
+    unsigned int capacity)
+{
+    nk_zero(pool, sizeof(*pool));
+    pool->alloc = *alloc;
+    pool->capacity = capacity;
+    pool->type = NK_BUFFER_DYNAMIC;
+    pool->pages = 0;
+}
+
+NK_INTERN void
+nk_pool_free(struct nk_pool *pool)
+{
+    struct nk_page *iter = pool->pages;
+    if (!pool) return;
+    if (pool->type == NK_BUFFER_FIXED) return;
+    while (iter) {
+        struct nk_page *next = iter->next;
+        pool->alloc.free(pool->alloc.userdata, iter);
+        iter = next;
+    }
+}
+
+NK_INTERN void
+nk_pool_init_fixed(struct nk_pool *pool, void *memory, nk_size size)
+{
+    nk_zero(pool, sizeof(*pool));
+    NK_ASSERT(size >= sizeof(struct nk_page));
+    if (size < sizeof(struct nk_page)) return;
+    pool->capacity = (unsigned)(size - sizeof(struct nk_page)) / sizeof(struct nk_page_element);
+    pool->pages = (struct nk_page*)memory;
+    pool->type = NK_BUFFER_FIXED;
+    pool->size = size;
+}
+
+NK_INTERN struct nk_page_element*
+nk_pool_alloc(struct nk_pool *pool)
+{
+    if (!pool->pages || pool->pages->size >= pool->capacity) {
+        /* allocate new page */
+        struct nk_page *page;
+        if (pool->type == NK_BUFFER_FIXED) {
+            if (!pool->pages) {
+                NK_ASSERT(pool->pages);
+                return 0;
+            }
+            NK_ASSERT(pool->pages->size < pool->capacity);
+            return 0;
+        } else {
+            nk_size size = sizeof(struct nk_page);
+            size += NK_POOL_DEFAULT_CAPACITY * sizeof(union nk_page_data);
+            page = (struct nk_page*)pool->alloc.alloc(pool->alloc.userdata,0, size);
+            page->next = pool->pages;
+            pool->pages = page;
+            page->size = 0;
+        }
+    }
+    return &pool->pages->win[pool->pages->size++];
+}
+
+/* ===============================================================
+ *
+ *                          CONTEXT
+ *
+ * ===============================================================*/
+NK_INTERN void* nk_create_window(struct nk_context *ctx);
+NK_INTERN void nk_remove_window(struct nk_context*, struct nk_window*);
+NK_INTERN void nk_free_window(struct nk_context *ctx, struct nk_window *win);
+NK_INTERN void nk_free_table(struct nk_context *ctx, struct nk_table *tbl);
+NK_INTERN void nk_remove_table(struct nk_window *win, struct nk_table *tbl);
+NK_INTERN void* nk_create_panel(struct nk_context *ctx);
+NK_INTERN void nk_free_panel(struct nk_context*, struct nk_panel *pan);
+
+NK_INTERN void
+nk_setup(struct nk_context *ctx, const struct nk_user_font *font)
+{
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    nk_zero_struct(*ctx);
+    nk_style_default(ctx);
+    ctx->seq = 1;
+    if (font) ctx->style.font = font;
+#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT
+    nk_draw_list_init(&ctx->draw_list);
+#endif
+}
+
+#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR
+NK_API int
+nk_init_default(struct nk_context *ctx, const struct nk_user_font *font)
+{
+    struct nk_allocator alloc;
+    alloc.userdata.ptr = 0;
+    alloc.alloc = nk_malloc;
+    alloc.free = nk_mfree;
+    return nk_init(ctx, &alloc, font);
+}
+#endif
+
+NK_API int
+nk_init_fixed(struct nk_context *ctx, void *memory, nk_size size,
+    const struct nk_user_font *font)
+{
+    NK_ASSERT(memory);
+    if (!memory) return 0;
+    nk_setup(ctx, font);
+    nk_buffer_init_fixed(&ctx->memory, memory, size);
+    ctx->use_pool = nk_false;
+    return 1;
+}
+
+NK_API int
+nk_init_custom(struct nk_context *ctx, struct nk_buffer *cmds,
+    struct nk_buffer *pool, const struct nk_user_font *font)
+{
+    NK_ASSERT(cmds);
+    NK_ASSERT(pool);
+    if (!cmds || !pool) return 0;
+
+    nk_setup(ctx, font);
+    ctx->memory = *cmds;
+    if (pool->type == NK_BUFFER_FIXED) {
+        /* take memory from buffer and alloc fixed pool */
+        nk_pool_init_fixed(&ctx->pool, pool->memory.ptr, pool->memory.size);
+    } else {
+        /* create dynamic pool from buffer allocator */
+        struct nk_allocator *alloc = &pool->pool;
+        nk_pool_init(&ctx->pool, alloc, NK_POOL_DEFAULT_CAPACITY);
+    }
+    ctx->use_pool = nk_true;
+    return 1;
+}
+
+NK_API int
+nk_init(struct nk_context *ctx, struct nk_allocator *alloc,
+    const struct nk_user_font *font)
+{
+    NK_ASSERT(alloc);
+    if (!alloc) return 0;
+    nk_setup(ctx, font);
+    nk_buffer_init(&ctx->memory, alloc, NK_DEFAULT_COMMAND_BUFFER_SIZE);
+    nk_pool_init(&ctx->pool, alloc, NK_POOL_DEFAULT_CAPACITY);
+    ctx->use_pool = nk_true;
+    return 1;
+}
+
+#ifdef NK_INCLUDE_COMMAND_USERDATA
+NK_API void
+nk_set_user_data(struct nk_context *ctx, nk_handle handle)
+{
+    if (!ctx) return;
+    ctx->userdata = handle;
+    if (ctx->current)
+        ctx->current->buffer.userdata = handle;
+}
+#endif
+
+NK_API void
+nk_free(struct nk_context *ctx)
+{
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    nk_buffer_free(&ctx->memory);
+    if (ctx->use_pool)
+        nk_pool_free(&ctx->pool);
+
+    nk_zero(&ctx->input, sizeof(ctx->input));
+    nk_zero(&ctx->style, sizeof(ctx->style));
+    nk_zero(&ctx->memory, sizeof(ctx->memory));
+
+    ctx->seq = 0;
+    ctx->build = 0;
+    ctx->begin = 0;
+    ctx->end = 0;
+    ctx->active = 0;
+    ctx->current = 0;
+    ctx->freelist = 0;
+    ctx->count = 0;
+}
+
+NK_API void
+nk_clear(struct nk_context *ctx)
+{
+    struct nk_window *iter;
+    struct nk_window *next;
+    NK_ASSERT(ctx);
+
+    if (!ctx) return;
+    if (ctx->use_pool)
+        nk_buffer_clear(&ctx->memory);
+    else nk_buffer_reset(&ctx->memory, NK_BUFFER_FRONT);
+
+    ctx->build = 0;
+    ctx->memory.calls = 0;
+    ctx->last_widget_state = 0;
+    ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_ARROW];
+    NK_MEMSET(&ctx->overlay, 0, sizeof(ctx->overlay));
+#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT
+    nk_draw_list_clear(&ctx->draw_list);
+#endif
+
+    /* garbage collector */
+    iter = ctx->begin;
+    while (iter) {
+        /* make sure minimized windows do not get removed */
+        if ((iter->flags & NK_WINDOW_MINIMIZED) &&
+            !(iter->flags & NK_WINDOW_CLOSED)) {
+            iter = iter->next;
+            continue;
+        }
+        /* remove hotness from hidden or closed windows*/
+        if (((iter->flags & NK_WINDOW_HIDDEN) ||
+            (iter->flags & NK_WINDOW_CLOSED)) &&
+            iter == ctx->active) {
+            ctx->active = iter->prev;
+            ctx->end = iter->prev;
+            if (ctx->active)
+                ctx->active->flags &= ~NK_WINDOW_ROM;
+        }
+
+        /* free unused popup windows */
+        if (iter->popup.win && iter->popup.win->seq != ctx->seq) {
+            nk_free_window(ctx, iter->popup.win);
+            iter->popup.win = 0;
+        }
+        /* remove unused window state tables */
+        {struct nk_table *n, *it = iter->tables;
+        while (it) {
+            n = it->next;
+            if (it->seq != ctx->seq) {
+                nk_remove_table(iter, it);
+                nk_zero(it, sizeof(union nk_page_data));
+                nk_free_table(ctx, it);
+                if (it == iter->tables)
+                    iter->tables = n;
+            }
+            it = n;
+        }}
+        /* window itself is not used anymore so free */
+        if (iter->seq != ctx->seq || iter->flags & NK_WINDOW_CLOSED) {
+            next = iter->next;
+            nk_remove_window(ctx, iter);
+            nk_free_window(ctx, iter);
+            iter = next;
+        } else iter = iter->next;
+    }
+    ctx->seq++;
+}
+
+/* ----------------------------------------------------------------
+ *
+ *                          BUFFERING
+ *
+ * ---------------------------------------------------------------*/
+NK_INTERN void
+nk_start_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(buffer);
+    if (!ctx || !buffer) return;
+    buffer->begin = ctx->memory.allocated;
+    buffer->end = buffer->begin;
+    buffer->last = buffer->begin;
+    buffer->clip = nk_null_rect;
+}
+
+NK_INTERN void
+nk_start(struct nk_context *ctx, struct nk_window *win)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(win);
+    nk_start_buffer(ctx, &win->buffer);
+}
+
+NK_INTERN void
+nk_start_popup(struct nk_context *ctx, struct nk_window *win)
+{
+    struct nk_popup_buffer *buf;
+    NK_ASSERT(ctx);
+    NK_ASSERT(win);
+    if (!ctx || !win) return;
+
+    /* save buffer fill state for popup */
+    buf = &win->popup.buf;
+    buf->begin = win->buffer.end;
+    buf->end = win->buffer.end;
+    buf->parent = win->buffer.last;
+    buf->last = buf->begin;
+    buf->active = nk_true;
+}
+
+NK_INTERN void
+nk_finish_popup(struct nk_context *ctx, struct nk_window *win)
+{
+    struct nk_popup_buffer *buf;
+    NK_ASSERT(ctx);
+    NK_ASSERT(win);
+    if (!ctx || !win) return;
+
+    buf = &win->popup.buf;
+    buf->last = win->buffer.last;
+    buf->end = win->buffer.end;
+}
+
+NK_INTERN void
+nk_finish_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(buffer);
+    if (!ctx || !buffer) return;
+    buffer->end = ctx->memory.allocated;
+}
+
+NK_INTERN void
+nk_finish(struct nk_context *ctx, struct nk_window *win)
+{
+    struct nk_popup_buffer *buf;
+    struct nk_command *parent_last;
+    void *memory;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(win);
+    if (!ctx || !win) return;
+    nk_finish_buffer(ctx, &win->buffer);
+    if (!win->popup.buf.active) return;
+
+    buf = &win->popup.buf;
+    memory = ctx->memory.memory.ptr;
+    parent_last = nk_ptr_add(struct nk_command, memory, buf->parent);
+    parent_last->next = buf->end;
+}
+
+NK_INTERN void
+nk_build(struct nk_context *ctx)
+{
+    struct nk_window *iter = 0;
+    struct nk_command *cmd = 0;
+    nk_byte *buffer = 0;
+
+    /* draw cursor overlay */
+    if (!ctx->style.cursor_active)
+        ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_ARROW];
+    if (ctx->style.cursor_active && !ctx->input.mouse.grabbed && ctx->style.cursor_visible) {
+        struct nk_rect mouse_bounds;
+        const struct nk_cursor *cursor = ctx->style.cursor_active;
+        nk_command_buffer_init(&ctx->overlay, &ctx->memory, NK_CLIPPING_OFF);
+        nk_start_buffer(ctx, &ctx->overlay);
+
+        mouse_bounds.x = ctx->input.mouse.pos.x - cursor->offset.x;
+        mouse_bounds.y = ctx->input.mouse.pos.y - cursor->offset.y;
+        mouse_bounds.w = cursor->size.x;
+        mouse_bounds.h = cursor->size.y;
+
+        nk_draw_image(&ctx->overlay, mouse_bounds, &cursor->img, nk_white);
+        nk_finish_buffer(ctx, &ctx->overlay);
+    }
+    /* build one big draw command list out of all window buffers */
+    iter = ctx->begin;
+    buffer = (nk_byte*)ctx->memory.memory.ptr;
+    while (iter != 0) {
+        struct nk_window *next = iter->next;
+        if (iter->buffer.last == iter->buffer.begin || (iter->flags & NK_WINDOW_HIDDEN)||
+            iter->seq != ctx->seq)
+            goto cont;
+
+        cmd = nk_ptr_add(struct nk_command, buffer, iter->buffer.last);
+        while (next && ((next->buffer.last == next->buffer.begin) ||
+            (next->flags & NK_WINDOW_HIDDEN)))
+            next = next->next; /* skip empty command buffers */
+
+        if (next) cmd->next = next->buffer.begin;
+        cont: iter = next;
+    }
+    /* append all popup draw commands into lists */
+    iter = ctx->begin;
+    while (iter != 0) {
+        struct nk_window *next = iter->next;
+        struct nk_popup_buffer *buf;
+        if (!iter->popup.buf.active)
+            goto skip;
+
+        buf = &iter->popup.buf;
+        cmd->next = buf->begin;
+        cmd = nk_ptr_add(struct nk_command, buffer, buf->last);
+        buf->active = nk_false;
+        skip: iter = next;
+    }
+    /* append overlay commands */
+    if (cmd) {
+        if (ctx->overlay.end != ctx->overlay.begin)
+            cmd->next = ctx->overlay.begin;
+        else cmd->next = ctx->memory.allocated;
+    }
+}
+
+NK_API const struct nk_command*
+nk__begin(struct nk_context *ctx)
+{
+    struct nk_window *iter;
+    nk_byte *buffer;
+    NK_ASSERT(ctx);
+    if (!ctx) return 0;
+    if (!ctx->count) return 0;
+
+    buffer = (nk_byte*)ctx->memory.memory.ptr;
+    if (!ctx->build) {
+        nk_build(ctx);
+        ctx->build = nk_true;
+    }
+    iter = ctx->begin;
+    while (iter && ((iter->buffer.begin == iter->buffer.end) || (iter->flags & NK_WINDOW_HIDDEN)))
+        iter = iter->next;
+    if (!iter) return 0;
+    return nk_ptr_add_const(struct nk_command, buffer, iter->buffer.begin);
+}
+
+NK_API const struct nk_command*
+nk__next(struct nk_context *ctx, const struct nk_command *cmd)
+{
+    nk_byte *buffer;
+    const struct nk_command *next;
+    NK_ASSERT(ctx);
+    if (!ctx || !cmd || !ctx->count) return 0;
+    if (cmd->next >= ctx->memory.allocated) return 0;
+    buffer = (nk_byte*)ctx->memory.memory.ptr;
+    next = nk_ptr_add_const(struct nk_command, buffer, cmd->next);
+    return next;
+}
+
+/* ----------------------------------------------------------------
+ *
+ *                          PANEL
+ *
+ * ---------------------------------------------------------------*/
+static int
+nk_panel_has_header(nk_flags flags, const char *title)
+{
+    int active = 0;
+    active = (flags & (NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE));
+    active = active || (flags & NK_WINDOW_TITLE);
+    active = active && !(flags & NK_WINDOW_HIDDEN) && title;
+    return active;
+}
+
+NK_INTERN struct nk_vec2
+nk_panel_get_padding(const struct nk_style *style, enum nk_panel_type type)
+{
+    switch (type) {
+    default:
+    case NK_PANEL_WINDOW: return style->window.padding;
+    case NK_PANEL_GROUP: return style->window.group_padding;
+    case NK_PANEL_POPUP: return style->window.popup_padding;
+    case NK_PANEL_CONTEXTUAL: return style->window.contextual_padding;
+    case NK_PANEL_COMBO: return style->window.combo_padding;
+    case NK_PANEL_MENU: return style->window.menu_padding;
+    case NK_PANEL_TOOLTIP: return style->window.menu_padding;
+    }
+}
+
+NK_INTERN float
+nk_panel_get_border(const struct nk_style *style, nk_flags flags,
+    enum nk_panel_type type)
+{
+    if (flags & NK_WINDOW_BORDER) {
+        switch (type) {
+        default:
+        case NK_PANEL_WINDOW: return style->window.border;
+        case NK_PANEL_GROUP: return style->window.group_border;
+        case NK_PANEL_POPUP: return style->window.popup_border;
+        case NK_PANEL_CONTEXTUAL: return style->window.contextual_border;
+        case NK_PANEL_COMBO: return style->window.combo_border;
+        case NK_PANEL_MENU: return style->window.menu_border;
+        case NK_PANEL_TOOLTIP: return style->window.menu_border;
+    }} else return 0;
+}
+
+NK_INTERN struct nk_color
+nk_panel_get_border_color(const struct nk_style *style, enum nk_panel_type type)
+{
+    switch (type) {
+    default:
+    case NK_PANEL_WINDOW: return style->window.border_color;
+    case NK_PANEL_GROUP: return style->window.group_border_color;
+    case NK_PANEL_POPUP: return style->window.popup_border_color;
+    case NK_PANEL_CONTEXTUAL: return style->window.contextual_border_color;
+    case NK_PANEL_COMBO: return style->window.combo_border_color;
+    case NK_PANEL_MENU: return style->window.menu_border_color;
+    case NK_PANEL_TOOLTIP: return style->window.menu_border_color;
+    }
+}
+
+NK_INTERN int
+nk_panel_is_sub(enum nk_panel_type type)
+{
+    return (type & NK_PANEL_SET_SUB)?1:0;
+}
+
+NK_INTERN int
+nk_panel_is_nonblock(enum nk_panel_type type)
+{
+    return (type & NK_PANEL_SET_NONBLOCK)?1:0;
+}
+
+NK_INTERN int
+nk_panel_begin(struct nk_context *ctx, const char *title, enum nk_panel_type panel_type)
+{
+    struct nk_input *in;
+    struct nk_window *win;
+    struct nk_panel *layout;
+    struct nk_command_buffer *out;
+    const struct nk_style *style;
+    const struct nk_user_font *font;
+
+    struct nk_vec2 scrollbar_size;
+    struct nk_vec2 panel_padding;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout) return 0;
+    nk_zero(ctx->current->layout, sizeof(*ctx->current->layout));
+    if ((ctx->current->flags & NK_WINDOW_HIDDEN) || (ctx->current->flags & NK_WINDOW_CLOSED)) {
+        nk_zero(ctx->current->layout, sizeof(struct nk_panel));
+        ctx->current->layout->type = panel_type;
+        return 0;
+    }
+    /* pull state into local stack */
+    style = &ctx->style;
+    font = style->font;
+    win = ctx->current;
+    layout = win->layout;
+    out = &win->buffer;
+    in = (win->flags & NK_WINDOW_NO_INPUT) ? 0: &ctx->input;
+#ifdef NK_INCLUDE_COMMAND_USERDATA
+    win->buffer.userdata = ctx->userdata;
+#endif
+    /* pull style configuration into local stack */
+    scrollbar_size = style->window.scrollbar_size;
+    panel_padding = nk_panel_get_padding(style, panel_type);
+
+    /* window movement */
+    if ((win->flags & NK_WINDOW_MOVABLE) && !(win->flags & NK_WINDOW_ROM)) {
+        int left_mouse_down;
+        int left_mouse_click_in_cursor;
+
+        /* calculate draggable window space */
+        struct nk_rect header;
+        header.x = win->bounds.x;
+        header.y = win->bounds.y;
+        header.w = win->bounds.w;
+        if (nk_panel_has_header(win->flags, title)) {
+            header.h = font->height + 2.0f * style->window.header.padding.y;
+            header.h += 2.0f * style->window.header.label_padding.y;
+        } else header.h = panel_padding.y;
+
+        /* window movement by dragging */
+        left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down;
+        left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in,
+            NK_BUTTON_LEFT, header, nk_true);
+        if (left_mouse_down && left_mouse_click_in_cursor) {
+            win->bounds.x = win->bounds.x + in->mouse.delta.x;
+            win->bounds.y = win->bounds.y + in->mouse.delta.y;
+            in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x += in->mouse.delta.x;
+            in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y += in->mouse.delta.y;
+            ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_MOVE];
+        }
+    }
+
+    /* setup panel */
+    layout->type = panel_type;
+    layout->flags = win->flags;
+    layout->bounds = win->bounds;
+    layout->bounds.x += panel_padding.x;
+    layout->bounds.w -= 2*panel_padding.x;
+    if (win->flags & NK_WINDOW_BORDER) {
+        layout->border = nk_panel_get_border(style, win->flags, panel_type);
+        layout->bounds = nk_shrink_rect(layout->bounds, layout->border);
+    } else layout->border = 0;
+    layout->at_y = layout->bounds.y;
+    layout->at_x = layout->bounds.x;
+    layout->max_x = 0;
+    layout->header_height = 0;
+    layout->footer_height = 0;
+    nk_layout_reset_min_row_height(ctx);
+    layout->row.index = 0;
+    layout->row.columns = 0;
+    layout->row.ratio = 0;
+    layout->row.item_width = 0;
+    layout->row.tree_depth = 0;
+    layout->row.height = panel_padding.y;
+    layout->has_scrolling = nk_true;
+    if (!(win->flags & NK_WINDOW_NO_SCROLLBAR))
+        layout->bounds.w -= scrollbar_size.x;
+    if (!nk_panel_is_nonblock(panel_type)) {
+        layout->footer_height = 0;
+        if (!(win->flags & NK_WINDOW_NO_SCROLLBAR) || win->flags & NK_WINDOW_SCALABLE)
+            layout->footer_height = scrollbar_size.y;
+        layout->bounds.h -= layout->footer_height;
+    }
+
+    /* panel header */
+    if (nk_panel_has_header(win->flags, title))
+    {
+        struct nk_text text;
+        struct nk_rect header;
+        const struct nk_style_item *background = 0;
+
+        /* calculate header bounds */
+        header.x = win->bounds.x;
+        header.y = win->bounds.y;
+        header.w = win->bounds.w;
+        header.h = font->height + 2.0f * style->window.header.padding.y;
+        header.h += (2.0f * style->window.header.label_padding.y);
+
+        /* shrink panel by header */
+        layout->header_height = header.h;
+        layout->bounds.y += header.h;
+        layout->bounds.h -= header.h;
+        layout->at_y += header.h;
+
+        /* select correct header background and text color */
+        if (ctx->active == win) {
+            background = &style->window.header.active;
+            text.text = style->window.header.label_active;
+        } else if (nk_input_is_mouse_hovering_rect(&ctx->input, header)) {
+            background = &style->window.header.hover;
+            text.text = style->window.header.label_hover;
+        } else {
+            background = &style->window.header.normal;
+            text.text = style->window.header.label_normal;
+        }
+
+        /* draw header background */
+        header.h += 1.0f;
+        if (background->type == NK_STYLE_ITEM_IMAGE) {
+            text.background = nk_rgba(0,0,0,0);
+            nk_draw_image(&win->buffer, header, &background->data.image, nk_white);
+        } else {
+            text.background = background->data.color;
+            nk_fill_rect(out, header, 0, background->data.color);
+        }
+
+        /* window close button */
+        {struct nk_rect button;
+        button.y = header.y + style->window.header.padding.y;
+        button.h = header.h - 2 * style->window.header.padding.y;
+        button.w = button.h;
+        if (win->flags & NK_WINDOW_CLOSABLE) {
+            nk_flags ws = 0;
+            if (style->window.header.align == NK_HEADER_RIGHT) {
+                button.x = (header.w + header.x) - (button.w + style->window.header.padding.x);
+                header.w -= button.w + style->window.header.spacing.x + style->window.header.padding.x;
+            } else {
+                button.x = header.x + style->window.header.padding.x;
+                header.x += button.w + style->window.header.spacing.x + style->window.header.padding.x;
+            }
+
+            if (nk_do_button_symbol(&ws, &win->buffer, button,
+                style->window.header.close_symbol, NK_BUTTON_DEFAULT,
+                &style->window.header.close_button, in, style->font) && !(win->flags & NK_WINDOW_ROM))
+            {
+                layout->flags |= NK_WINDOW_HIDDEN;
+                layout->flags &= (nk_flags)~NK_WINDOW_MINIMIZED;
+            }
+        }
+
+        /* window minimize button */
+        if (win->flags & NK_WINDOW_MINIMIZABLE) {
+            nk_flags ws = 0;
+            if (style->window.header.align == NK_HEADER_RIGHT) {
+                button.x = (header.w + header.x) - button.w;
+                if (!(win->flags & NK_WINDOW_CLOSABLE)) {
+                    button.x -= style->window.header.padding.x;
+                    header.w -= style->window.header.padding.x;
+                }
+                header.w -= button.w + style->window.header.spacing.x;
+            } else {
+                button.x = header.x;
+                header.x += button.w + style->window.header.spacing.x + style->window.header.padding.x;
+            }
+            if (nk_do_button_symbol(&ws, &win->buffer, button, (layout->flags & NK_WINDOW_MINIMIZED)?
+                style->window.header.maximize_symbol: style->window.header.minimize_symbol,
+                NK_BUTTON_DEFAULT, &style->window.header.minimize_button, in, style->font) && !(win->flags & NK_WINDOW_ROM))
+                layout->flags = (layout->flags & NK_WINDOW_MINIMIZED) ?
+                    layout->flags & (nk_flags)~NK_WINDOW_MINIMIZED:
+                    layout->flags | NK_WINDOW_MINIMIZED;
+        }}
+
+        {/* window header title */
+        int text_len = nk_strlen(title);
+        struct nk_rect label = {0,0,0,0};
+        float t = font->width(font->userdata, font->height, title, text_len);
+        text.padding = nk_vec2(0,0);
+
+        label.x = header.x + style->window.header.padding.x;
+        label.x += style->window.header.label_padding.x;
+        label.y = header.y + style->window.header.label_padding.y;
+        label.h = font->height + 2 * style->window.header.label_padding.y;
+        label.w = t + 2 * style->window.header.spacing.x;
+        label.w = NK_CLAMP(0, label.w, header.x + header.w - label.x);
+        nk_widget_text(out, label,(const char*)title, text_len, &text, NK_TEXT_LEFT, font);}
+    }
+
+    /* draw window background */
+    if (!(layout->flags & NK_WINDOW_MINIMIZED) && !(layout->flags & NK_WINDOW_DYNAMIC)) {
+        struct nk_rect body;
+        body.x = win->bounds.x;
+        body.w = win->bounds.w;
+        body.y = (win->bounds.y + layout->header_height);
+        body.h = (win->bounds.h - layout->header_height);
+        if (style->window.fixed_background.type == NK_STYLE_ITEM_IMAGE)
+            nk_draw_image(out, body, &style->window.fixed_background.data.image, nk_white);
+        else nk_fill_rect(out, body, 0, style->window.fixed_background.data.color);
+    }
+
+    /* set clipping rectangle */
+    {struct nk_rect clip;
+    layout->clip = layout->bounds;
+    nk_unify(&clip, &win->buffer.clip, layout->clip.x, layout->clip.y,
+        layout->clip.x + layout->clip.w, layout->clip.y + layout->clip.h);
+    nk_push_scissor(out, clip);
+    layout->clip = clip;}
+    return !(layout->flags & NK_WINDOW_HIDDEN) && !(layout->flags & NK_WINDOW_MINIMIZED);
+}
+
+NK_INTERN void
+nk_panel_end(struct nk_context *ctx)
+{
+    struct nk_input *in;
+    struct nk_window *window;
+    struct nk_panel *layout;
+    const struct nk_style *style;
+    struct nk_command_buffer *out;
+
+    struct nk_vec2 scrollbar_size;
+    struct nk_vec2 panel_padding;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    window = ctx->current;
+    layout = window->layout;
+    style = &ctx->style;
+    out = &window->buffer;
+    in = (layout->flags & NK_WINDOW_ROM || layout->flags & NK_WINDOW_NO_INPUT) ? 0 :&ctx->input;
+    if (!nk_panel_is_sub(layout->type))
+        nk_push_scissor(out, nk_null_rect);
+
+    /* cache configuration data */
+    scrollbar_size = style->window.scrollbar_size;
+    panel_padding = nk_panel_get_padding(style, layout->type);
+
+    /* update the current cursor Y-position to point over the last added widget */
+    layout->at_y += layout->row.height;
+
+    /* dynamic panels */
+    if (layout->flags & NK_WINDOW_DYNAMIC && !(layout->flags & NK_WINDOW_MINIMIZED))
+    {
+        /* update panel height to fit dynamic growth */
+        struct nk_rect empty_space;
+        if (layout->at_y < (layout->bounds.y + layout->bounds.h))
+            layout->bounds.h = layout->at_y - layout->bounds.y;
+
+        /* fill top empty space */
+        empty_space.x = window->bounds.x;
+        empty_space.y = layout->bounds.y;
+        empty_space.h = panel_padding.y;
+        empty_space.w = window->bounds.w;
+        nk_fill_rect(out, empty_space, 0, style->window.background);
+
+        /* fill left empty space */
+        empty_space.x = window->bounds.x;
+        empty_space.y = layout->bounds.y;
+        empty_space.w = panel_padding.x + layout->border;
+        empty_space.h = layout->bounds.h;
+        nk_fill_rect(out, empty_space, 0, style->window.background);
+
+        /* fill right empty space */
+        empty_space.x = layout->bounds.x + layout->bounds.w - layout->border;
+        empty_space.y = layout->bounds.y;
+        empty_space.w = panel_padding.x + layout->border;
+        empty_space.h = layout->bounds.h;
+        if (*layout->offset_y == 0 && !(layout->flags & NK_WINDOW_NO_SCROLLBAR))
+            empty_space.w += scrollbar_size.x;
+        nk_fill_rect(out, empty_space, 0, style->window.background);
+
+        /* fill bottom empty space */
+        if (*layout->offset_x != 0 && !(layout->flags & NK_WINDOW_NO_SCROLLBAR)) {
+            empty_space.x = window->bounds.x;
+            empty_space.y = layout->bounds.y + layout->bounds.h;
+            empty_space.w = window->bounds.w;
+            empty_space.h = scrollbar_size.y;
+            nk_fill_rect(out, empty_space, 0, style->window.background);
+        }
+    }
+
+    /* scrollbars */
+    if (!(layout->flags & NK_WINDOW_NO_SCROLLBAR) &&
+        !(layout->flags & NK_WINDOW_MINIMIZED) &&
+        window->scrollbar_hiding_timer < NK_SCROLLBAR_HIDING_TIMEOUT)
+    {
+        struct nk_rect scroll;
+        int scroll_has_scrolling;
+        float scroll_target;
+        float scroll_offset;
+        float scroll_step;
+        float scroll_inc;
+
+        /* mouse wheel scrolling */
+        if (nk_panel_is_sub(layout->type))
+        {
+            /* sub-window mouse wheel scrolling */
+            struct nk_window *root_window = window;
+            struct nk_panel *root_panel = window->layout;
+            while (root_panel->parent)
+                root_panel = root_panel->parent;
+            while (root_window->parent)
+                root_window = root_window->parent;
+
+            /* only allow scrolling if parent window is active */
+            scroll_has_scrolling = 0;
+            if ((root_window == ctx->active) && layout->has_scrolling) {
+                /* and panel is being hovered and inside clip rect*/
+                if (nk_input_is_mouse_hovering_rect(in, layout->bounds) &&
+                    NK_INTERSECT(layout->bounds.x, layout->bounds.y, layout->bounds.w, layout->bounds.h,
+                        root_panel->clip.x, root_panel->clip.y, root_panel->clip.w, root_panel->clip.h))
+                {
+                    /* deactivate all parent scrolling */
+                    root_panel = window->layout;
+                    while (root_panel->parent) {
+                        root_panel->has_scrolling = nk_false;
+                        root_panel = root_panel->parent;
+                    }
+                    root_panel->has_scrolling = nk_false;
+                    scroll_has_scrolling = nk_true;
+                }
+            }
+        } else if (!nk_panel_is_sub(layout->type)) {
+            /* window mouse wheel scrolling */
+            scroll_has_scrolling = (window == ctx->active) && layout->has_scrolling;
+            if (in && (in->mouse.scroll_delta.y > 0 || in->mouse.scroll_delta.x > 0) && scroll_has_scrolling)
+                window->scrolled = nk_true;
+            else window->scrolled = nk_false;
+        } else scroll_has_scrolling = nk_false;
+
+        {
+            /* vertical scrollbar */
+            nk_flags state = 0;
+            scroll.x = layout->bounds.x + layout->bounds.w + panel_padding.x;
+            scroll.y = layout->bounds.y;
+            scroll.w = scrollbar_size.x;
+            scroll.h = layout->bounds.h;
+
+            scroll_offset = (float)*layout->offset_y;
+            scroll_step = scroll.h * 0.10f;
+            scroll_inc = scroll.h * 0.01f;
+            scroll_target = (float)(int)(layout->at_y - scroll.y);
+            scroll_offset = nk_do_scrollbarv(&state, out, scroll, scroll_has_scrolling,
+                scroll_offset, scroll_target, scroll_step, scroll_inc,
+                &ctx->style.scrollv, in, style->font);
+            *layout->offset_y = (nk_uint)scroll_offset;
+            if (in && scroll_has_scrolling)
+                in->mouse.scroll_delta.y = 0;
+        }
+        {
+            /* horizontal scrollbar */
+            nk_flags state = 0;
+            scroll.x = layout->bounds.x;
+            scroll.y = layout->bounds.y + layout->bounds.h;
+            scroll.w = layout->bounds.w;
+            scroll.h = scrollbar_size.y;
+
+            scroll_offset = (float)*layout->offset_x;
+            scroll_target = (float)(int)(layout->max_x - scroll.x);
+            scroll_step = layout->max_x * 0.05f;
+            scroll_inc = layout->max_x * 0.005f;
+            scroll_offset = nk_do_scrollbarh(&state, out, scroll, scroll_has_scrolling,
+                scroll_offset, scroll_target, scroll_step, scroll_inc,
+                &ctx->style.scrollh, in, style->font);
+            *layout->offset_x = (nk_uint)scroll_offset;
+        }
+    }
+
+    /* hide scroll if no user input */
+    if (window->flags & NK_WINDOW_SCROLL_AUTO_HIDE) {
+        int has_input = ctx->input.mouse.delta.x != 0 || ctx->input.mouse.delta.y != 0 || ctx->input.mouse.scroll_delta.y != 0;
+        int is_window_hovered = nk_window_is_hovered(ctx);
+        int any_item_active = (ctx->last_widget_state & NK_WIDGET_STATE_MODIFIED);
+        if ((!has_input && is_window_hovered) || (!is_window_hovered && !any_item_active))
+            window->scrollbar_hiding_timer += ctx->delta_time_seconds;
+        else window->scrollbar_hiding_timer = 0;
+    } else window->scrollbar_hiding_timer = 0;
+
+    /* window border */
+    if (layout->flags & NK_WINDOW_BORDER)
+    {
+        struct nk_color border_color = nk_panel_get_border_color(style, layout->type);
+        const float padding_y = (layout->flags & NK_WINDOW_MINIMIZED) ?
+            style->window.border + window->bounds.y + layout->header_height:
+            (layout->flags & NK_WINDOW_DYNAMIC)?
+            layout->bounds.y + layout->bounds.h + layout->footer_height:
+            window->bounds.y + window->bounds.h;
+
+        /* draw border top */
+        nk_stroke_line(out,window->bounds.x,window->bounds.y,
+            window->bounds.x + window->bounds.w, window->bounds.y,
+            layout->border, border_color);
+
+        /* draw bottom border */
+        nk_stroke_line(out, window->bounds.x, padding_y,
+            window->bounds.x + window->bounds.w, padding_y, layout->border, border_color);
+
+        /* draw left border */
+        nk_stroke_line(out, window->bounds.x + layout->border*0.5f,
+            window->bounds.y, window->bounds.x + layout->border*0.5f,
+            padding_y, layout->border, border_color);
+
+        /* draw right border */
+        nk_stroke_line(out, window->bounds.x + window->bounds.w - layout->border*0.5f,
+            window->bounds.y, window->bounds.x + window->bounds.w - layout->border*0.5f,
+            padding_y, layout->border, border_color);
+    }
+
+    /* scaler */
+    if ((layout->flags & NK_WINDOW_SCALABLE) && in && !(layout->flags & NK_WINDOW_MINIMIZED))
+    {
+        /* calculate scaler bounds */
+        struct nk_rect scaler;
+        scaler.w = scrollbar_size.x;
+        scaler.h = scrollbar_size.y;
+        scaler.y = layout->bounds.y + layout->bounds.h;
+        if (layout->flags & NK_WINDOW_SCALE_LEFT)
+            scaler.x = layout->bounds.x - panel_padding.x * 0.5f;
+        else scaler.x = layout->bounds.x + layout->bounds.w + panel_padding.x;
+        if (layout->flags & NK_WINDOW_NO_SCROLLBAR)
+            scaler.x -= scaler.w;
+
+        /* draw scaler */
+        {const struct nk_style_item *item = &style->window.scaler;
+        if (item->type == NK_STYLE_ITEM_IMAGE)
+            nk_draw_image(out, scaler, &item->data.image, nk_white);
+        else {
+            if (layout->flags & NK_WINDOW_SCALE_LEFT) {
+                nk_fill_triangle(out, scaler.x, scaler.y, scaler.x,
+                    scaler.y + scaler.h, scaler.x + scaler.w,
+                    scaler.y + scaler.h, item->data.color);
+            } else {
+                nk_fill_triangle(out, scaler.x + scaler.w, scaler.y, scaler.x + scaler.w,
+                    scaler.y + scaler.h, scaler.x, scaler.y + scaler.h, item->data.color);
+            }
+        }}
+
+        /* do window scaling */
+        if (!(window->flags & NK_WINDOW_ROM)) {
+            struct nk_vec2 window_size = style->window.min_size;
+            int left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down;
+            int left_mouse_click_in_scaler = nk_input_has_mouse_click_down_in_rect(in,
+                    NK_BUTTON_LEFT, scaler, nk_true);
+
+            if (left_mouse_down && left_mouse_click_in_scaler) {
+                float delta_x = in->mouse.delta.x;
+                if (layout->flags & NK_WINDOW_SCALE_LEFT) {
+                    delta_x = -delta_x;
+                    window->bounds.x += in->mouse.delta.x;
+                }
+                /* dragging in x-direction  */
+                if (window->bounds.w + delta_x >= window_size.x) {
+                    if ((delta_x < 0) || (delta_x > 0 && in->mouse.pos.x >= scaler.x)) {
+                        window->bounds.w = window->bounds.w + delta_x;
+                        scaler.x += in->mouse.delta.x;
+                    }
+                }
+                /* dragging in y-direction (only possible if static window) */
+                if (!(layout->flags & NK_WINDOW_DYNAMIC)) {
+                    if (window_size.y < window->bounds.h + in->mouse.delta.y) {
+                        if ((in->mouse.delta.y < 0) || (in->mouse.delta.y > 0 && in->mouse.pos.y >= scaler.y)) {
+                            window->bounds.h = window->bounds.h + in->mouse.delta.y;
+                            scaler.y += in->mouse.delta.y;
+                        }
+                    }
+                }
+                ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_RESIZE_TOP_RIGHT_DOWN_LEFT];
+                in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = scaler.x + scaler.w/2.0f;
+                in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y = scaler.y + scaler.h/2.0f;
+            }
+        }
+    }
+    if (!nk_panel_is_sub(layout->type)) {
+        /* window is hidden so clear command buffer  */
+        if (layout->flags & NK_WINDOW_HIDDEN)
+            nk_command_buffer_reset(&window->buffer);
+        /* window is visible and not tab */
+        else nk_finish(ctx, window);
+    }
+
+    /* NK_WINDOW_REMOVE_ROM flag was set so remove NK_WINDOW_ROM */
+    if (layout->flags & NK_WINDOW_REMOVE_ROM) {
+        layout->flags &= ~(nk_flags)NK_WINDOW_ROM;
+        layout->flags &= ~(nk_flags)NK_WINDOW_REMOVE_ROM;
+    }
+    window->flags = layout->flags;
+
+    /* property garbage collector */
+    if (window->property.active && window->property.old != window->property.seq &&
+        window->property.active == window->property.prev) {
+        nk_zero(&window->property, sizeof(window->property));
+    } else {
+        window->property.old = window->property.seq;
+        window->property.prev = window->property.active;
+        window->property.seq = 0;
+    }
+    /* edit garbage collector */
+    if (window->edit.active && window->edit.old != window->edit.seq &&
+       window->edit.active == window->edit.prev) {
+        nk_zero(&window->edit, sizeof(window->edit));
+    } else {
+        window->edit.old = window->edit.seq;
+        window->edit.prev = window->edit.active;
+        window->edit.seq = 0;
+    }
+    /* contextual garbage collector */
+    if (window->popup.active_con && window->popup.con_old != window->popup.con_count) {
+        window->popup.con_count = 0;
+        window->popup.con_old = 0;
+        window->popup.active_con = 0;
+    } else {
+        window->popup.con_old = window->popup.con_count;
+        window->popup.con_count = 0;
+    }
+    window->popup.combo_count = 0;
+    /* helper to make sure you have a 'nk_tree_push' for every 'nk_tree_pop' */
+    NK_ASSERT(!layout->row.tree_depth);
+}
+
+/* ----------------------------------------------------------------
+ *
+ *                          PAGE ELEMENT
+ *
+ * ---------------------------------------------------------------*/
+NK_INTERN struct nk_page_element*
+nk_create_page_element(struct nk_context *ctx)
+{
+    struct nk_page_element *elem;
+    if (ctx->freelist) {
+        /* unlink page element from free list */
+        elem = ctx->freelist;
+        ctx->freelist = elem->next;
+    } else if (ctx->use_pool) {
+        /* allocate page element from memory pool */
+        elem = nk_pool_alloc(&ctx->pool);
+        NK_ASSERT(elem);
+        if (!elem) return 0;
+    } else {
+        /* allocate new page element from back of fixed size memory buffer */
+        NK_STORAGE const nk_size size = sizeof(struct nk_page_element);
+        NK_STORAGE const nk_size align = NK_ALIGNOF(struct nk_page_element);
+        elem = (struct nk_page_element*)nk_buffer_alloc(&ctx->memory, NK_BUFFER_BACK, size, align);
+        NK_ASSERT(elem);
+        if (!elem) return 0;
+    }
+    nk_zero_struct(*elem);
+    elem->next = 0;
+    elem->prev = 0;
+    return elem;
+}
+
+NK_INTERN void
+nk_link_page_element_into_freelist(struct nk_context *ctx,
+    struct nk_page_element *elem)
+{
+    /* link table into freelist */
+    if (!ctx->freelist) {
+        ctx->freelist = elem;
+    } else {
+        elem->next = ctx->freelist;
+        ctx->freelist = elem;
+    }
+}
+
+NK_INTERN void
+nk_free_page_element(struct nk_context *ctx, struct nk_page_element *elem)
+{
+    /* we have a pool so just add to free list */
+    if (ctx->use_pool) {
+        nk_link_page_element_into_freelist(ctx, elem);
+        return;
+    }
+    /* if possible remove last element from back of fixed memory buffer */
+    {void *elem_end = (void*)(elem + 1);
+    void *buffer_end = (nk_byte*)ctx->memory.memory.ptr + ctx->memory.size;
+    if (elem_end == buffer_end)
+        ctx->memory.size -= sizeof(struct nk_page_element);
+    else nk_link_page_element_into_freelist(ctx, elem);}
+}
+
+/* ----------------------------------------------------------------
+ *
+ *                          PANEL
+ *
+ * ---------------------------------------------------------------*/
+NK_INTERN void*
+nk_create_panel(struct nk_context *ctx)
+{
+    struct nk_page_element *elem;
+    elem = nk_create_page_element(ctx);
+    if (!elem) return 0;
+    nk_zero_struct(*elem);
+    return &elem->data.pan;
+}
+
+NK_INTERN void
+nk_free_panel(struct nk_context *ctx, struct nk_panel *pan)
+{
+    union nk_page_data *pd = NK_CONTAINER_OF(pan, union nk_page_data, pan);
+    struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data);
+    nk_free_page_element(ctx, pe);
+}
+
+/* ----------------------------------------------------------------
+ *
+ *                          TABLES
+ *
+ * ---------------------------------------------------------------*/
+NK_INTERN struct nk_table*
+nk_create_table(struct nk_context *ctx)
+{
+    struct nk_page_element *elem;
+    elem = nk_create_page_element(ctx);
+    if (!elem) return 0;
+    nk_zero_struct(*elem);
+    return &elem->data.tbl;
+}
+
+NK_INTERN void
+nk_free_table(struct nk_context *ctx, struct nk_table *tbl)
+{
+    union nk_page_data *pd = NK_CONTAINER_OF(tbl, union nk_page_data, tbl);
+    struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data);
+    nk_free_page_element(ctx, pe);
+}
+
+NK_INTERN void
+nk_push_table(struct nk_window *win, struct nk_table *tbl)
+{
+    if (!win->tables) {
+        win->tables = tbl;
+        tbl->next = 0;
+        tbl->prev = 0;
+        tbl->size = 0;
+        win->table_count = 1;
+        return;
+    }
+    win->tables->prev = tbl;
+    tbl->next = win->tables;
+    tbl->prev = 0;
+    tbl->size = 0;
+    win->tables = tbl;
+    win->table_count++;
+}
+
+NK_INTERN void
+nk_remove_table(struct nk_window *win, struct nk_table *tbl)
+{
+    if (win->tables == tbl)
+        win->tables = tbl->next;
+    if (tbl->next)
+        tbl->next->prev = tbl->prev;
+    if (tbl->prev)
+        tbl->prev->next = tbl->next;
+    tbl->next = 0;
+    tbl->prev = 0;
+}
+
+NK_INTERN nk_uint*
+nk_add_value(struct nk_context *ctx, struct nk_window *win,
+            nk_hash name, nk_uint value)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(win);
+    if (!win || !ctx) return 0;
+    if (!win->tables || win->tables->size >= NK_VALUE_PAGE_CAPACITY) {
+        struct nk_table *tbl = nk_create_table(ctx);
+        NK_ASSERT(tbl);
+        if (!tbl) return 0;
+        nk_push_table(win, tbl);
+    }
+    win->tables->seq = win->seq;
+    win->tables->keys[win->tables->size] = name;
+    win->tables->values[win->tables->size] = value;
+    return &win->tables->values[win->tables->size++];
+}
+
+NK_INTERN nk_uint*
+nk_find_value(struct nk_window *win, nk_hash name)
+{
+    struct nk_table *iter = win->tables;
+    while (iter) {
+        unsigned int i = 0;
+        unsigned int size = iter->size;
+        for (i = 0; i < size; ++i) {
+            if (iter->keys[i] == name) {
+                iter->seq = win->seq;
+                return &iter->values[i];
+            }
+        } size = NK_VALUE_PAGE_CAPACITY;
+        iter = iter->next;
+    }
+    return 0;
+}
+
+/* ----------------------------------------------------------------
+ *
+ *                          WINDOW
+ *
+ * ---------------------------------------------------------------*/
+NK_INTERN void*
+nk_create_window(struct nk_context *ctx)
+{
+    struct nk_page_element *elem;
+    elem = nk_create_page_element(ctx);
+    if (!elem) return 0;
+    elem->data.win.seq = ctx->seq;
+    return &elem->data.win;
+}
+
+NK_INTERN void
+nk_free_window(struct nk_context *ctx, struct nk_window *win)
+{
+    /* unlink windows from list */
+    struct nk_table *it = win->tables;
+    if (win->popup.win) {
+        nk_free_window(ctx, win->popup.win);
+        win->popup.win = 0;
+    }
+    win->next = 0;
+    win->prev = 0;
+
+    while (it) {
+        /*free window state tables */
+        struct nk_table *n = it->next;
+        nk_remove_table(win, it);
+        nk_free_table(ctx, it);
+        if (it == win->tables)
+            win->tables = n;
+        it = n;
+    }
+
+    /* link windows into freelist */
+    {union nk_page_data *pd = NK_CONTAINER_OF(win, union nk_page_data, win);
+    struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data);
+    nk_free_page_element(ctx, pe);}
+}
+
+NK_INTERN struct nk_window*
+nk_find_window(struct nk_context *ctx, nk_hash hash, const char *name)
+{
+    struct nk_window *iter;
+    iter = ctx->begin;
+    while (iter) {
+        NK_ASSERT(iter != iter->next);
+        if (iter->name == hash) {
+            int max_len = nk_strlen(iter->name_string);
+            if (!nk_stricmpn(iter->name_string, name, max_len))
+                return iter;
+        }
+        iter = iter->next;
+    }
+    return 0;
+}
+
+enum nk_window_insert_location {
+    NK_INSERT_BACK, /* inserts window into the back of list (front of screen) */
+    NK_INSERT_FRONT /* inserts window into the front of list (back of screen) */
+};
+NK_INTERN void
+nk_insert_window(struct nk_context *ctx, struct nk_window *win,
+    enum nk_window_insert_location loc)
+{
+    const struct nk_window *iter;
+    NK_ASSERT(ctx);
+    NK_ASSERT(win);
+    if (!win || !ctx) return;
+
+    iter = ctx->begin;
+    while (iter) {
+        NK_ASSERT(iter != iter->next);
+        NK_ASSERT(iter != win);
+        if (iter == win) return;
+        iter = iter->next;
+    }
+
+    if (!ctx->begin) {
+        win->next = 0;
+        win->prev = 0;
+        ctx->begin = win;
+        ctx->end = win;
+        ctx->count = 1;
+        return;
+    }
+    if (loc == NK_INSERT_BACK) {
+        struct nk_window *end;
+        end = ctx->end;
+        end->flags |= NK_WINDOW_ROM;
+        end->next = win;
+        win->prev = ctx->end;
+        win->next = 0;
+        ctx->end = win;
+        ctx->active = ctx->end;
+        ctx->end->flags &= ~(nk_flags)NK_WINDOW_ROM;
+    } else {
+        /*ctx->end->flags |= NK_WINDOW_ROM;*/
+        ctx->begin->prev = win;
+        win->next = ctx->begin;
+        win->prev = 0;
+        ctx->begin = win;
+        ctx->begin->flags &= ~(nk_flags)NK_WINDOW_ROM;
+    }
+    ctx->count++;
+}
+
+NK_INTERN void
+nk_remove_window(struct nk_context *ctx, struct nk_window *win)
+{
+    if (win == ctx->begin || win == ctx->end) {
+        if (win == ctx->begin) {
+            ctx->begin = win->next;
+            if (win->next)
+                win->next->prev = 0;
+        }
+        if (win == ctx->end) {
+            ctx->end = win->prev;
+            if (win->prev)
+                win->prev->next = 0;
+        }
+    } else {
+        if (win->next)
+            win->next->prev = win->prev;
+        if (win->prev)
+            win->prev->next = win->next;
+    }
+    if (win == ctx->active || !ctx->active) {
+        ctx->active = ctx->end;
+        if (ctx->end)
+            ctx->end->flags &= ~(nk_flags)NK_WINDOW_ROM;
+    }
+    win->next = 0;
+    win->prev = 0;
+    ctx->count--;
+}
+
+NK_API int
+nk_begin(struct nk_context *ctx, const char *title,
+    struct nk_rect bounds, nk_flags flags)
+{
+    return nk_begin_titled(ctx, title, title, bounds, flags);
+}
+
+NK_API int
+nk_begin_titled(struct nk_context *ctx, const char *name, const char *title,
+    struct nk_rect bounds, nk_flags flags)
+{
+    struct nk_window *win;
+    struct nk_style *style;
+    nk_hash title_hash;
+    int title_len;
+    int ret = 0;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(name);
+    NK_ASSERT(title);
+    NK_ASSERT(ctx->style.font && ctx->style.font->width && "if this triggers you forgot to add a font");
+    NK_ASSERT(!ctx->current && "if this triggers you missed a `nk_end` call");
+    if (!ctx || ctx->current || !title || !name)
+        return 0;
+
+    /* find or create window */
+    style = &ctx->style;
+    title_len = (int)nk_strlen(name);
+    title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE);
+    win = nk_find_window(ctx, title_hash, name);
+    if (!win) {
+        /* create new window */
+        nk_size name_length = (nk_size)nk_strlen(name);
+        win = (struct nk_window*)nk_create_window(ctx);
+        NK_ASSERT(win);
+        if (!win) return 0;
+
+        if (flags & NK_WINDOW_BACKGROUND)
+            nk_insert_window(ctx, win, NK_INSERT_FRONT);
+        else nk_insert_window(ctx, win, NK_INSERT_BACK);
+        nk_command_buffer_init(&win->buffer, &ctx->memory, NK_CLIPPING_ON);
+
+        win->flags = flags;
+        win->bounds = bounds;
+        win->name = title_hash;
+        name_length = NK_MIN(name_length, NK_WINDOW_MAX_NAME-1);
+        NK_MEMCPY(win->name_string, name, name_length);
+        win->name_string[name_length] = 0;
+        win->popup.win = 0;
+        if (!ctx->active)
+            ctx->active = win;
+    } else {
+        /* update window */
+        win->flags &= ~(nk_flags)(NK_WINDOW_PRIVATE-1);
+        win->flags |= flags;
+        if (!(win->flags & (NK_WINDOW_MOVABLE | NK_WINDOW_SCALABLE)))
+            win->bounds = bounds;
+        /* If this assert triggers you either:
+         *
+         * I.) Have more than one window with the same name or
+         * II.) You forgot to actually draw the window.
+         *      More specific you did not call `nk_clear` (nk_clear will be
+         *      automatically called for you if you are using one of the
+         *      provided demo backends). */
+        NK_ASSERT(win->seq != ctx->seq);
+        win->seq = ctx->seq;
+        if (!ctx->active && !(win->flags & NK_WINDOW_HIDDEN)) {
+            ctx->active = win;
+            ctx->end = win;
+        }
+    }
+    if (win->flags & NK_WINDOW_HIDDEN) {
+        ctx->current = win;
+        win->layout = 0;
+        return 0;
+    }
+
+    /* window overlapping */
+    if (!(win->flags & NK_WINDOW_HIDDEN) && !(win->flags & NK_WINDOW_NO_INPUT))
+    {
+        int inpanel, ishovered;
+        struct nk_window *iter = win;
+        float h = ctx->style.font->height + 2.0f * style->window.header.padding.y +
+            (2.0f * style->window.header.label_padding.y);
+        struct nk_rect win_bounds = (!(win->flags & NK_WINDOW_MINIMIZED))?
+            win->bounds: nk_rect(win->bounds.x, win->bounds.y, win->bounds.w, h);
+
+        /* activate window if hovered and no other window is overlapping this window */
+        nk_start(ctx, win);
+        inpanel = nk_input_has_mouse_click_down_in_rect(&ctx->input, NK_BUTTON_LEFT, win_bounds, nk_true);
+        inpanel = inpanel && ctx->input.mouse.buttons[NK_BUTTON_LEFT].clicked;
+        ishovered = nk_input_is_mouse_hovering_rect(&ctx->input, win_bounds);
+        if ((win != ctx->active) && ishovered && !ctx->input.mouse.buttons[NK_BUTTON_LEFT].down) {
+            iter = win->next;
+            while (iter) {
+                struct nk_rect iter_bounds = (!(iter->flags & NK_WINDOW_MINIMIZED))?
+                    iter->bounds: nk_rect(iter->bounds.x, iter->bounds.y, iter->bounds.w, h);
+                if (NK_INTERSECT(win_bounds.x, win_bounds.y, win_bounds.w, win_bounds.h,
+                    iter_bounds.x, iter_bounds.y, iter_bounds.w, iter_bounds.h) &&
+                    (!(iter->flags & NK_WINDOW_HIDDEN)))
+                    break;
+
+                if (iter->popup.win && iter->popup.active && !(iter->flags & NK_WINDOW_HIDDEN) &&
+                    NK_INTERSECT(win->bounds.x, win_bounds.y, win_bounds.w, win_bounds.h,
+                    iter->popup.win->bounds.x, iter->popup.win->bounds.y,
+                    iter->popup.win->bounds.w, iter->popup.win->bounds.h))
+                    break;
+                iter = iter->next;
+            }
+        }
+
+        /* activate window if clicked */
+        if (iter && inpanel && (win != ctx->end)) {
+            iter = win->next;
+            while (iter) {
+                /* try to find a panel with higher priority in the same position */
+                struct nk_rect iter_bounds = (!(iter->flags & NK_WINDOW_MINIMIZED))?
+                iter->bounds: nk_rect(iter->bounds.x, iter->bounds.y, iter->bounds.w, h);
+                if (NK_INBOX(ctx->input.mouse.pos.x, ctx->input.mouse.pos.y,
+                    iter_bounds.x, iter_bounds.y, iter_bounds.w, iter_bounds.h) &&
+                    !(iter->flags & NK_WINDOW_HIDDEN))
+                    break;
+                if (iter->popup.win && iter->popup.active && !(iter->flags & NK_WINDOW_HIDDEN) &&
+                    NK_INTERSECT(win_bounds.x, win_bounds.y, win_bounds.w, win_bounds.h,
+                    iter->popup.win->bounds.x, iter->popup.win->bounds.y,
+                    iter->popup.win->bounds.w, iter->popup.win->bounds.h))
+                    break;
+                iter = iter->next;
+            }
+        }
+        if (iter && !(win->flags & NK_WINDOW_ROM) && (win->flags & NK_WINDOW_BACKGROUND)) {
+            win->flags |= (nk_flags)NK_WINDOW_ROM;
+            iter->flags &= ~(nk_flags)NK_WINDOW_ROM;
+            ctx->active = iter;
+            if (!(iter->flags & NK_WINDOW_BACKGROUND)) {
+                /* current window is active in that position so transfer to top
+                 * at the highest priority in stack */
+                nk_remove_window(ctx, iter);
+                nk_insert_window(ctx, iter, NK_INSERT_BACK);
+            }
+        } else {
+            if (!iter && ctx->end != win) {
+                if (!(win->flags & NK_WINDOW_BACKGROUND)) {
+                    /* current window is active in that position so transfer to top
+                     * at the highest priority in stack */
+                    nk_remove_window(ctx, win);
+                    nk_insert_window(ctx, win, NK_INSERT_BACK);
+                }
+                win->flags &= ~(nk_flags)NK_WINDOW_ROM;
+                ctx->active = win;
+            }
+            if (ctx->end != win && !(win->flags & NK_WINDOW_BACKGROUND))
+                win->flags |= NK_WINDOW_ROM;
+        }
+    }
+
+    win->layout = (struct nk_panel*)nk_create_panel(ctx);
+    ctx->current = win;
+    ret = nk_panel_begin(ctx, title, NK_PANEL_WINDOW);
+    win->layout->offset_x = &win->scrollbar.x;
+    win->layout->offset_y = &win->scrollbar.y;
+    return ret;
+}
+
+NK_API void
+nk_end(struct nk_context *ctx)
+{
+    struct nk_panel *layout;
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current && "if this triggers you forgot to call `nk_begin`");
+    if (!ctx || !ctx->current)
+        return;
+
+    layout = ctx->current->layout;
+    if (!layout || (layout->type == NK_PANEL_WINDOW && (ctx->current->flags & NK_WINDOW_HIDDEN))) {
+        ctx->current = 0;
+        return;
+    }
+    nk_panel_end(ctx);
+    nk_free_panel(ctx, ctx->current->layout);
+    ctx->current = 0;
+}
+
+NK_API struct nk_rect
+nk_window_get_bounds(const struct nk_context *ctx)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current) return nk_rect(0,0,0,0);
+    return ctx->current->bounds;
+}
+
+NK_API struct nk_vec2
+nk_window_get_position(const struct nk_context *ctx)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current) return nk_vec2(0,0);
+    return nk_vec2(ctx->current->bounds.x, ctx->current->bounds.y);
+}
+
+NK_API struct nk_vec2
+nk_window_get_size(const struct nk_context *ctx)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current) return nk_vec2(0,0);
+    return nk_vec2(ctx->current->bounds.w, ctx->current->bounds.h);
+}
+
+NK_API float
+nk_window_get_width(const struct nk_context *ctx)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current) return 0;
+    return ctx->current->bounds.w;
+}
+
+NK_API float
+nk_window_get_height(const struct nk_context *ctx)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current) return 0;
+    return ctx->current->bounds.h;
+}
+
+NK_API struct nk_rect
+nk_window_get_content_region(struct nk_context *ctx)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current) return nk_rect(0,0,0,0);
+    return ctx->current->layout->clip;
+}
+
+NK_API struct nk_vec2
+nk_window_get_content_region_min(struct nk_context *ctx)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current) return nk_vec2(0,0);
+    return nk_vec2(ctx->current->layout->clip.x, ctx->current->layout->clip.y);
+}
+
+NK_API struct nk_vec2
+nk_window_get_content_region_max(struct nk_context *ctx)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current) return nk_vec2(0,0);
+    return nk_vec2(ctx->current->layout->clip.x + ctx->current->layout->clip.w,
+        ctx->current->layout->clip.y + ctx->current->layout->clip.h);
+}
+
+NK_API struct nk_vec2
+nk_window_get_content_region_size(struct nk_context *ctx)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current) return nk_vec2(0,0);
+    return nk_vec2(ctx->current->layout->clip.w, ctx->current->layout->clip.h);
+}
+
+NK_API struct nk_command_buffer*
+nk_window_get_canvas(struct nk_context *ctx)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current) return 0;
+    return &ctx->current->buffer;
+}
+
+NK_API struct nk_panel*
+nk_window_get_panel(struct nk_context *ctx)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current) return 0;
+    return ctx->current->layout;
+}
+
+NK_API int
+nk_window_has_focus(const struct nk_context *ctx)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current) return 0;
+    return ctx->current == ctx->active;
+}
+
+NK_API int
+nk_window_is_hovered(struct nk_context *ctx)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current) return 0;
+    if(ctx->current->flags & NK_WINDOW_HIDDEN)
+        return 0;
+    return nk_input_is_mouse_hovering_rect(&ctx->input, ctx->current->bounds);
+}
+
+NK_API int
+nk_window_is_any_hovered(struct nk_context *ctx)
+{
+    struct nk_window *iter;
+    NK_ASSERT(ctx);
+    if (!ctx) return 0;
+    iter = ctx->begin;
+    while (iter) {
+        /* check if window is being hovered */
+        if(!(iter->flags & NK_WINDOW_HIDDEN)) {
+            /* check if window popup is being hovered */
+            if (iter->popup.active && iter->popup.win && nk_input_is_mouse_hovering_rect(&ctx->input, iter->popup.win->bounds))
+                return 1;
+
+            if (iter->flags & NK_WINDOW_MINIMIZED) {
+                struct nk_rect header = iter->bounds;
+                header.h = ctx->style.font->height + 2 * ctx->style.window.header.padding.y;
+                if (nk_input_is_mouse_hovering_rect(&ctx->input, header))
+                    return 1;
+            } else if (nk_input_is_mouse_hovering_rect(&ctx->input, iter->bounds)) {
+                return 1;
+            }
+        }
+        iter = iter->next;
+    }
+    return 0;
+}
+
+NK_API int
+nk_item_is_any_active(struct nk_context *ctx)
+{
+    int any_hovered = nk_window_is_any_hovered(ctx);
+    int any_active = (ctx->last_widget_state & NK_WIDGET_STATE_MODIFIED);
+    return any_hovered || any_active;
+}
+
+NK_API int
+nk_window_is_collapsed(struct nk_context *ctx, const char *name)
+{
+    int title_len;
+    nk_hash title_hash;
+    struct nk_window *win;
+    NK_ASSERT(ctx);
+    if (!ctx) return 0;
+
+    title_len = (int)nk_strlen(name);
+    title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE);
+    win = nk_find_window(ctx, title_hash, name);
+    if (!win) return 0;
+    return win->flags & NK_WINDOW_MINIMIZED;
+}
+
+NK_API int
+nk_window_is_closed(struct nk_context *ctx, const char *name)
+{
+    int title_len;
+    nk_hash title_hash;
+    struct nk_window *win;
+    NK_ASSERT(ctx);
+    if (!ctx) return 1;
+
+    title_len = (int)nk_strlen(name);
+    title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE);
+    win = nk_find_window(ctx, title_hash, name);
+    if (!win) return 1;
+    return (win->flags & NK_WINDOW_CLOSED);
+}
+
+NK_API int
+nk_window_is_hidden(struct nk_context *ctx, const char *name)
+{
+    int title_len;
+    nk_hash title_hash;
+    struct nk_window *win;
+    NK_ASSERT(ctx);
+    if (!ctx) return 1;
+
+    title_len = (int)nk_strlen(name);
+    title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE);
+    win = nk_find_window(ctx, title_hash, name);
+    if (!win) return 1;
+    return (win->flags & NK_WINDOW_HIDDEN);
+}
+
+NK_API int
+nk_window_is_active(struct nk_context *ctx, const char *name)
+{
+    int title_len;
+    nk_hash title_hash;
+    struct nk_window *win;
+    NK_ASSERT(ctx);
+    if (!ctx) return 0;
+
+    title_len = (int)nk_strlen(name);
+    title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE);
+    win = nk_find_window(ctx, title_hash, name);
+    if (!win) return 0;
+    return win == ctx->active;
+}
+
+NK_API struct nk_window*
+nk_window_find(struct nk_context *ctx, const char *name)
+{
+    int title_len;
+    nk_hash title_hash;
+    title_len = (int)nk_strlen(name);
+    title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE);
+    return nk_find_window(ctx, title_hash, name);
+}
+
+NK_API void
+nk_window_close(struct nk_context *ctx, const char *name)
+{
+    struct nk_window *win;
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    win = nk_window_find(ctx, name);
+    if (!win) return;
+    NK_ASSERT(ctx->current != win && "You cannot close a currently active window");
+    if (ctx->current == win) return;
+    win->flags |= NK_WINDOW_HIDDEN;
+    win->flags |= NK_WINDOW_CLOSED;
+}
+
+NK_API void
+nk_window_set_bounds(struct nk_context *ctx,
+    const char *name, struct nk_rect bounds)
+{
+    struct nk_window *win;
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    win = nk_window_find(ctx, name);
+    if (!win) return;
+    NK_ASSERT(ctx->current != win && "You cannot update a currently in procecss window");
+    win->bounds = bounds;
+}
+
+NK_API void
+nk_window_set_position(struct nk_context *ctx,
+    const char *name, struct nk_vec2 pos)
+{
+    struct nk_window *win = nk_window_find(ctx, name);
+    if (!win) return;
+    win->bounds.x = pos.x;
+    win->bounds.y = pos.y;
+}
+
+NK_API void
+nk_window_set_size(struct nk_context *ctx,
+    const char *name, struct nk_vec2 size)
+{
+    struct nk_window *win = nk_window_find(ctx, name);
+    if (!win) return;
+    win->bounds.w = size.x;
+    win->bounds.h = size.y;
+}
+
+NK_API void
+nk_window_collapse(struct nk_context *ctx, const char *name,
+                    enum nk_collapse_states c)
+{
+    int title_len;
+    nk_hash title_hash;
+    struct nk_window *win;
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+
+    title_len = (int)nk_strlen(name);
+    title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE);
+    win = nk_find_window(ctx, title_hash, name);
+    if (!win) return;
+    if (c == NK_MINIMIZED)
+        win->flags |= NK_WINDOW_MINIMIZED;
+    else win->flags &= ~(nk_flags)NK_WINDOW_MINIMIZED;
+}
+
+NK_API void
+nk_window_collapse_if(struct nk_context *ctx, const char *name,
+    enum nk_collapse_states c, int cond)
+{
+    NK_ASSERT(ctx);
+    if (!ctx || !cond) return;
+    nk_window_collapse(ctx, name, c);
+}
+
+NK_API void
+nk_window_show(struct nk_context *ctx, const char *name, enum nk_show_states s)
+{
+    int title_len;
+    nk_hash title_hash;
+    struct nk_window *win;
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+
+    title_len = (int)nk_strlen(name);
+    title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE);
+    win = nk_find_window(ctx, title_hash, name);
+    if (!win) return;
+    if (s == NK_HIDDEN) {
+        win->flags |= NK_WINDOW_HIDDEN;
+    } else win->flags &= ~(nk_flags)NK_WINDOW_HIDDEN;
+}
+
+NK_API void
+nk_window_show_if(struct nk_context *ctx, const char *name,
+    enum nk_show_states s, int cond)
+{
+    NK_ASSERT(ctx);
+    if (!ctx || !cond) return;
+    nk_window_show(ctx, name, s);
+}
+
+NK_API void
+nk_window_set_focus(struct nk_context *ctx, const char *name)
+{
+    int title_len;
+    nk_hash title_hash;
+    struct nk_window *win;
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+
+    title_len = (int)nk_strlen(name);
+    title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE);
+    win = nk_find_window(ctx, title_hash, name);
+    if (win && ctx->end != win) {
+        nk_remove_window(ctx, win);
+        nk_insert_window(ctx, win, NK_INSERT_BACK);
+    }
+    ctx->active = win;
+}
+
+/*----------------------------------------------------------------
+ *
+ *                          MENUBAR
+ *
+ * --------------------------------------------------------------*/
+NK_API void
+nk_menubar_begin(struct nk_context *ctx)
+{
+    struct nk_panel *layout;
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    layout = ctx->current->layout;
+    NK_ASSERT(layout->at_y == layout->bounds.y);
+    /* if this assert triggers you allocated space between nk_begin and nk_menubar_begin.
+    If you want a menubar the first nuklear function after `nk_begin` has to be a
+    `nk_menubar_begin` call. Inside the menubar you then have to allocate space for
+    widgets (also supports multiple rows).
+    Example:
+        if (nk_begin(...)) {
+            nk_menubar_begin(...);
+                nk_layout_xxxx(...);
+                nk_button(...);
+                nk_layout_xxxx(...);
+                nk_button(...);
+            nk_menubar_end(...);
+        }
+        nk_end(...);
+    */
+    if (layout->flags & NK_WINDOW_HIDDEN || layout->flags & NK_WINDOW_MINIMIZED)
+        return;
+
+    layout->menu.x = layout->at_x;
+    layout->menu.y = layout->at_y + layout->row.height;
+    layout->menu.w = layout->bounds.w;
+    layout->menu.offset.x = *layout->offset_x;
+    layout->menu.offset.y = *layout->offset_y;
+    *layout->offset_y = 0;
+}
+
+NK_API void
+nk_menubar_end(struct nk_context *ctx)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    struct nk_command_buffer *out;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    out = &win->buffer;
+    layout = win->layout;
+    if (layout->flags & NK_WINDOW_HIDDEN || layout->flags & NK_WINDOW_MINIMIZED)
+        return;
+
+    layout->menu.h = layout->at_y - layout->menu.y;
+    layout->bounds.y += layout->menu.h + ctx->style.window.spacing.y + layout->row.height;
+    layout->bounds.h -= layout->menu.h + ctx->style.window.spacing.y + layout->row.height;
+
+    *layout->offset_x = layout->menu.offset.x;
+    *layout->offset_y = layout->menu.offset.y;
+    layout->at_y = layout->bounds.y - layout->row.height;
+
+    layout->clip.y = layout->bounds.y;
+    layout->clip.h = layout->bounds.h;
+    nk_push_scissor(out, layout->clip);
+}
+/* -------------------------------------------------------------
+ *
+ *                          LAYOUT
+ *
+ * --------------------------------------------------------------*/
+NK_API void
+nk_layout_set_min_row_height(struct nk_context *ctx, float height)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    layout->row.min_height = height;
+}
+
+NK_API void
+nk_layout_reset_min_row_height(struct nk_context *ctx)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    layout->row.min_height = ctx->style.font->height;
+    layout->row.min_height += ctx->style.text.padding.y*2;
+    layout->row.min_height += ctx->style.window.min_row_height_padding*2;
+}
+
+NK_INTERN float
+nk_layout_row_calculate_usable_space(const struct nk_style *style, enum nk_panel_type type,
+    float total_space, int columns)
+{
+    float panel_padding;
+    float panel_spacing;
+    float panel_space;
+
+    struct nk_vec2 spacing;
+    struct nk_vec2 padding;
+
+    spacing = style->window.spacing;
+    padding = nk_panel_get_padding(style, type);
+
+    /* calculate the usable panel space */
+    panel_padding = 2 * padding.x;
+    panel_spacing = (float)NK_MAX(columns - 1, 0) * spacing.x;
+    panel_space  = total_space - panel_padding - panel_spacing;
+    return panel_space;
+}
+
+NK_INTERN void
+nk_panel_layout(const struct nk_context *ctx, struct nk_window *win,
+    float height, int cols)
+{
+    struct nk_panel *layout;
+    const struct nk_style *style;
+    struct nk_command_buffer *out;
+
+    struct nk_vec2 item_spacing;
+    struct nk_color color;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    /* prefetch some configuration data */
+    layout = win->layout;
+    style = &ctx->style;
+    out = &win->buffer;
+    color = style->window.background;
+    item_spacing = style->window.spacing;
+
+    /*  if one of these triggers you forgot to add an `if` condition around either
+        a window, group, popup, combobox or contextual menu `begin` and `end` block.
+        Example:
+            if (nk_begin(...) {...} nk_end(...); or
+            if (nk_group_begin(...) { nk_group_end(...);} */
+    NK_ASSERT(!(layout->flags & NK_WINDOW_MINIMIZED));
+    NK_ASSERT(!(layout->flags & NK_WINDOW_HIDDEN));
+    NK_ASSERT(!(layout->flags & NK_WINDOW_CLOSED));
+
+    /* update the current row and set the current row layout */
+    layout->row.index = 0;
+    layout->at_y += layout->row.height;
+    layout->row.columns = cols;
+    if (height == 0.0f)
+        layout->row.height = NK_MAX(height, layout->row.min_height) + item_spacing.y;
+    else layout->row.height = height + item_spacing.y;
+
+    layout->row.item_offset = 0;
+    if (layout->flags & NK_WINDOW_DYNAMIC) {
+        /* draw background for dynamic panels */
+        struct nk_rect background;
+        background.x = win->bounds.x;
+        background.w = win->bounds.w;
+        background.y = layout->at_y - 1.0f;
+        background.h = layout->row.height + 1.0f;
+        nk_fill_rect(out, background, 0, color);
+    }
+}
+
+NK_INTERN void
+nk_row_layout(struct nk_context *ctx, enum nk_layout_format fmt,
+    float height, int cols, int width)
+{
+    /* update the current row and set the current row layout */
+    struct nk_window *win;
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    nk_panel_layout(ctx, win, height, cols);
+    if (fmt == NK_DYNAMIC)
+        win->layout->row.type = NK_LAYOUT_DYNAMIC_FIXED;
+    else win->layout->row.type = NK_LAYOUT_STATIC_FIXED;
+
+    win->layout->row.ratio = 0;
+    win->layout->row.filled = 0;
+    win->layout->row.item_offset = 0;
+    win->layout->row.item_width = (float)width;
+}
+
+NK_API float
+nk_layout_ratio_from_pixel(struct nk_context *ctx, float pixel_width)
+{
+    struct nk_window *win;
+    NK_ASSERT(ctx);
+    NK_ASSERT(pixel_width);
+    if (!ctx || !ctx->current || !ctx->current->layout) return 0;
+    win = ctx->current;
+    return NK_CLAMP(0.0f, pixel_width/win->bounds.x, 1.0f);
+}
+
+NK_API void
+nk_layout_row_dynamic(struct nk_context *ctx, float height, int cols)
+{
+    nk_row_layout(ctx, NK_DYNAMIC, height, cols, 0);
+}
+
+NK_API void
+nk_layout_row_static(struct nk_context *ctx, float height, int item_width, int cols)
+{
+    nk_row_layout(ctx, NK_STATIC, height, cols, item_width);
+}
+
+NK_API void
+nk_layout_row_begin(struct nk_context *ctx, enum nk_layout_format fmt,
+    float row_height, int cols)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    nk_panel_layout(ctx, win, row_height, cols);
+    if (fmt == NK_DYNAMIC)
+        layout->row.type = NK_LAYOUT_DYNAMIC_ROW;
+    else layout->row.type = NK_LAYOUT_STATIC_ROW;
+
+    layout->row.ratio = 0;
+    layout->row.filled = 0;
+    layout->row.item_width = 0;
+    layout->row.item_offset = 0;
+    layout->row.columns = cols;
+}
+
+NK_API void
+nk_layout_row_push(struct nk_context *ctx, float ratio_or_width)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    NK_ASSERT(layout->row.type == NK_LAYOUT_STATIC_ROW || layout->row.type == NK_LAYOUT_DYNAMIC_ROW);
+    if (layout->row.type != NK_LAYOUT_STATIC_ROW && layout->row.type != NK_LAYOUT_DYNAMIC_ROW)
+        return;
+
+    if (layout->row.type == NK_LAYOUT_DYNAMIC_ROW) {
+        float ratio = ratio_or_width;
+        if ((ratio + layout->row.filled) > 1.0f) return;
+        if (ratio > 0.0f)
+            layout->row.item_width = NK_SATURATE(ratio);
+        else layout->row.item_width = 1.0f - layout->row.filled;
+    } else layout->row.item_width = ratio_or_width;
+}
+
+NK_API void
+nk_layout_row_end(struct nk_context *ctx)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    NK_ASSERT(layout->row.type == NK_LAYOUT_STATIC_ROW || layout->row.type == NK_LAYOUT_DYNAMIC_ROW);
+    if (layout->row.type != NK_LAYOUT_STATIC_ROW && layout->row.type != NK_LAYOUT_DYNAMIC_ROW)
+        return;
+    layout->row.item_width = 0;
+    layout->row.item_offset = 0;
+}
+
+NK_API void
+nk_layout_row(struct nk_context *ctx, enum nk_layout_format fmt,
+    float height, int cols, const float *ratio)
+{
+    int i;
+    int n_undef = 0;
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    nk_panel_layout(ctx, win, height, cols);
+    if (fmt == NK_DYNAMIC) {
+        /* calculate width of undefined widget ratios */
+        float r = 0;
+        layout->row.ratio = ratio;
+        for (i = 0; i < cols; ++i) {
+            if (ratio[i] < 0.0f)
+                n_undef++;
+            else r += ratio[i];
+        }
+        r = NK_SATURATE(1.0f - r);
+        layout->row.type = NK_LAYOUT_DYNAMIC;
+        layout->row.item_width = (r > 0 && n_undef > 0) ? (r / (float)n_undef):0;
+    } else {
+        layout->row.ratio = ratio;
+        layout->row.type = NK_LAYOUT_STATIC;
+        layout->row.item_width = 0;
+        layout->row.item_offset = 0;
+    }
+    layout->row.item_offset = 0;
+    layout->row.filled = 0;
+}
+
+NK_API void
+nk_layout_row_template_begin(struct nk_context *ctx, float height)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    nk_panel_layout(ctx, win, height, 1);
+    layout->row.type = NK_LAYOUT_TEMPLATE;
+    layout->row.columns = 0;
+    layout->row.ratio = 0;
+    layout->row.item_width = 0;
+    layout->row.item_height = 0;
+    layout->row.item_offset = 0;
+    layout->row.filled = 0;
+    layout->row.item.x = 0;
+    layout->row.item.y = 0;
+    layout->row.item.w = 0;
+    layout->row.item.h = 0;
+}
+
+NK_API void
+nk_layout_row_template_push_dynamic(struct nk_context *ctx)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    NK_ASSERT(layout->row.type == NK_LAYOUT_TEMPLATE);
+    NK_ASSERT(layout->row.columns < NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS);
+    if (layout->row.type != NK_LAYOUT_TEMPLATE) return;
+    if (layout->row.columns >= NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS) return;
+    layout->row.templates[layout->row.columns++] = -1.0f;
+}
+
+NK_API void
+nk_layout_row_template_push_variable(struct nk_context *ctx, float min_width)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    NK_ASSERT(layout->row.type == NK_LAYOUT_TEMPLATE);
+    NK_ASSERT(layout->row.columns < NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS);
+    if (layout->row.type != NK_LAYOUT_TEMPLATE) return;
+    if (layout->row.columns >= NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS) return;
+    layout->row.templates[layout->row.columns++] = -min_width;
+}
+
+NK_API void
+nk_layout_row_template_push_static(struct nk_context *ctx, float width)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    NK_ASSERT(layout->row.type == NK_LAYOUT_TEMPLATE);
+    NK_ASSERT(layout->row.columns < NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS);
+    if (layout->row.type != NK_LAYOUT_TEMPLATE) return;
+    if (layout->row.columns >= NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS) return;
+    layout->row.templates[layout->row.columns++] = width;
+}
+
+NK_API void
+nk_layout_row_template_end(struct nk_context *ctx)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    int i = 0;
+    int variable_count = 0;
+    int min_variable_count = 0;
+    float min_fixed_width = 0.0f;
+    float total_fixed_width = 0.0f;
+    float max_variable_width = 0.0f;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    NK_ASSERT(layout->row.type == NK_LAYOUT_TEMPLATE);
+    if (layout->row.type != NK_LAYOUT_TEMPLATE) return;
+    for (i = 0; i < layout->row.columns; ++i) {
+        float width = layout->row.templates[i];
+        if (width >= 0.0f) {
+            total_fixed_width += width;
+            min_fixed_width += width;
+        } else if (width < -1.0f) {
+            width = -width;
+            total_fixed_width += width;
+            max_variable_width = NK_MAX(max_variable_width, width);
+            variable_count++;
+        } else {
+            min_variable_count++;
+            variable_count++;
+        }
+    }
+    if (variable_count) {
+        float space = nk_layout_row_calculate_usable_space(&ctx->style, layout->type,
+                            layout->bounds.w, layout->row.columns);
+        float var_width = (NK_MAX(space-min_fixed_width,0.0f)) / (float)variable_count;
+        int enough_space = var_width >= max_variable_width;
+        if (!enough_space)
+            var_width = (NK_MAX(space-total_fixed_width,0)) / (float)min_variable_count;
+        for (i = 0; i < layout->row.columns; ++i) {
+            float *width = &layout->row.templates[i];
+            *width = (*width >= 0.0f)? *width: (*width < -1.0f && !enough_space)? -(*width): var_width;
+        }
+    }
+}
+
+NK_API void
+nk_layout_space_begin(struct nk_context *ctx, enum nk_layout_format fmt,
+    float height, int widget_count)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    nk_panel_layout(ctx, win, height, widget_count);
+    if (fmt == NK_STATIC)
+        layout->row.type = NK_LAYOUT_STATIC_FREE;
+    else layout->row.type = NK_LAYOUT_DYNAMIC_FREE;
+
+    layout->row.ratio = 0;
+    layout->row.filled = 0;
+    layout->row.item_width = 0;
+    layout->row.item_offset = 0;
+}
+
+NK_API void
+nk_layout_space_end(struct nk_context *ctx)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    layout->row.item_width = 0;
+    layout->row.item_height = 0;
+    layout->row.item_offset = 0;
+    nk_zero(&layout->row.item, sizeof(layout->row.item));
+}
+
+NK_API void
+nk_layout_space_push(struct nk_context *ctx, struct nk_rect rect)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    layout->row.item = rect;
+}
+
+NK_API struct nk_rect
+nk_layout_space_bounds(struct nk_context *ctx)
+{
+    struct nk_rect ret;
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    win = ctx->current;
+    layout = win->layout;
+
+    ret.x = layout->clip.x;
+    ret.y = layout->clip.y;
+    ret.w = layout->clip.w;
+    ret.h = layout->row.height;
+    return ret;
+}
+
+NK_API struct nk_rect
+nk_layout_widget_bounds(struct nk_context *ctx)
+{
+    struct nk_rect ret;
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    win = ctx->current;
+    layout = win->layout;
+
+    ret.x = layout->at_x;
+    ret.y = layout->at_y;
+    ret.w = layout->bounds.w - NK_MAX(layout->at_x - layout->bounds.x,0);
+    ret.h = layout->row.height;
+    return ret;
+}
+
+NK_API struct nk_vec2
+nk_layout_space_to_screen(struct nk_context *ctx, struct nk_vec2 ret)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    win = ctx->current;
+    layout = win->layout;
+
+    ret.x += layout->at_x - (float)*layout->offset_x;
+    ret.y += layout->at_y - (float)*layout->offset_y;
+    return ret;
+}
+
+NK_API struct nk_vec2
+nk_layout_space_to_local(struct nk_context *ctx, struct nk_vec2 ret)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    win = ctx->current;
+    layout = win->layout;
+
+    ret.x += -layout->at_x + (float)*layout->offset_x;
+    ret.y += -layout->at_y + (float)*layout->offset_y;
+    return ret;
+}
+
+NK_API struct nk_rect
+nk_layout_space_rect_to_screen(struct nk_context *ctx, struct nk_rect ret)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    win = ctx->current;
+    layout = win->layout;
+
+    ret.x += layout->at_x - (float)*layout->offset_x;
+    ret.y += layout->at_y - (float)*layout->offset_y;
+    return ret;
+}
+
+NK_API struct nk_rect
+nk_layout_space_rect_to_local(struct nk_context *ctx, struct nk_rect ret)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    win = ctx->current;
+    layout = win->layout;
+
+    ret.x += -layout->at_x + (float)*layout->offset_x;
+    ret.y += -layout->at_y + (float)*layout->offset_y;
+    return ret;
+}
+
+NK_INTERN void
+nk_panel_alloc_row(const struct nk_context *ctx, struct nk_window *win)
+{
+    struct nk_panel *layout = win->layout;
+    struct nk_vec2 spacing = ctx->style.window.spacing;
+    const float row_height = layout->row.height - spacing.y;
+    nk_panel_layout(ctx, win, row_height, layout->row.columns);
+}
+
+NK_INTERN void
+nk_layout_widget_space(struct nk_rect *bounds, const struct nk_context *ctx,
+    struct nk_window *win, int modify)
+{
+    struct nk_panel *layout;
+    const struct nk_style *style;
+
+    struct nk_vec2 spacing;
+    struct nk_vec2 padding;
+
+    float item_offset = 0;
+    float item_width = 0;
+    float item_spacing = 0;
+    float panel_space = 0;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    style = &ctx->style;
+    NK_ASSERT(bounds);
+
+    spacing = style->window.spacing;
+    padding = nk_panel_get_padding(style, layout->type);
+    panel_space = nk_layout_row_calculate_usable_space(&ctx->style, layout->type,
+                                            layout->bounds.w, layout->row.columns);
+
+    /* calculate the width of one item inside the current layout space */
+    switch (layout->row.type) {
+    case NK_LAYOUT_DYNAMIC_FIXED: {
+        /* scaling fixed size widgets item width */
+        item_width = NK_MAX(1.0f,panel_space-1.0f) / (float)layout->row.columns;
+        item_offset = (float)layout->row.index * item_width;
+        item_spacing = (float)layout->row.index * spacing.x;
+    } break;
+    case NK_LAYOUT_DYNAMIC_ROW: {
+        /* scaling single ratio widget width */
+        item_width = layout->row.item_width * panel_space;
+        item_offset = layout->row.item_offset;
+        item_spacing = 0;
+
+        if (modify) {
+            layout->row.item_offset += item_width + spacing.x;
+            layout->row.filled += layout->row.item_width;
+            layout->row.index = 0;
+        }
+    } break;
+    case NK_LAYOUT_DYNAMIC_FREE: {
+        /* panel width depended free widget placing */
+        bounds->x = layout->at_x + (layout->bounds.w * layout->row.item.x);
+        bounds->x -= (float)*layout->offset_x;
+        bounds->y = layout->at_y + (layout->row.height * layout->row.item.y);
+        bounds->y -= (float)*layout->offset_y;
+        bounds->w = layout->bounds.w  * layout->row.item.w;
+        bounds->h = layout->row.height * layout->row.item.h;
+        return;
+    } break;
+    case NK_LAYOUT_DYNAMIC: {
+        /* scaling arrays of panel width ratios for every widget */
+        float ratio;
+        NK_ASSERT(layout->row.ratio);
+        ratio = (layout->row.ratio[layout->row.index] < 0) ?
+            layout->row.item_width : layout->row.ratio[layout->row.index];
+
+        item_spacing = (float)layout->row.index * spacing.x;
+        item_width = (ratio * panel_space);
+        item_offset = layout->row.item_offset;
+
+        if (modify) {
+            layout->row.item_offset += item_width;
+            layout->row.filled += ratio;
+        }
+    } break;
+    case NK_LAYOUT_STATIC_FIXED: {
+        /* non-scaling fixed widgets item width */
+        item_width = layout->row.item_width;
+        item_offset = (float)layout->row.index * item_width;
+        item_spacing = (float)layout->row.index * spacing.x;
+    } break;
+    case NK_LAYOUT_STATIC_ROW: {
+        /* scaling single ratio widget width */
+        item_width = layout->row.item_width;
+        item_offset = layout->row.item_offset;
+        item_spacing = (float)layout->row.index * spacing.x;
+        if (modify) layout->row.item_offset += item_width;
+    } break;
+    case NK_LAYOUT_STATIC_FREE: {
+        /* free widget placing */
+        bounds->x = layout->at_x + layout->row.item.x;
+        bounds->w = layout->row.item.w;
+        if (((bounds->x + bounds->w) > layout->max_x) && modify)
+            layout->max_x = (bounds->x + bounds->w);
+        bounds->x -= (float)*layout->offset_x;
+        bounds->y = layout->at_y + layout->row.item.y;
+        bounds->y -= (float)*layout->offset_y;
+        bounds->h = layout->row.item.h;
+        return;
+    } break;
+    case NK_LAYOUT_STATIC: {
+        /* non-scaling array of panel pixel width for every widget */
+        item_spacing = (float)layout->row.index * spacing.x;
+        item_width = layout->row.ratio[layout->row.index];
+        item_offset = layout->row.item_offset;
+        if (modify) layout->row.item_offset += item_width;
+    } break;
+    case NK_LAYOUT_TEMPLATE: {
+        /* stretchy row layout with combined dynamic/static widget width*/
+        NK_ASSERT(layout->row.index < layout->row.columns);
+        NK_ASSERT(layout->row.index < NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS);
+        item_width = layout->row.templates[layout->row.index];
+        item_offset = layout->row.item_offset;
+        item_spacing = (float)layout->row.index * spacing.x;
+        if (modify) layout->row.item_offset += item_width;
+    } break;
+    default: NK_ASSERT(0); break;
+    };
+
+    /* set the bounds of the newly allocated widget */
+    bounds->w = item_width;
+    bounds->h = layout->row.height - spacing.y;
+    bounds->y = layout->at_y - (float)*layout->offset_y;
+    bounds->x = layout->at_x + item_offset + item_spacing + padding.x;
+    if (((bounds->x + bounds->w) > layout->max_x) && modify)
+        layout->max_x = bounds->x + bounds->w;
+    bounds->x -= (float)*layout->offset_x;
+}
+
+NK_INTERN void
+nk_panel_alloc_space(struct nk_rect *bounds, const struct nk_context *ctx)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    /* check if the end of the row has been hit and begin new row if so */
+    win = ctx->current;
+    layout = win->layout;
+    if (layout->row.index >= layout->row.columns)
+        nk_panel_alloc_row(ctx, win);
+
+    /* calculate widget position and size */
+    nk_layout_widget_space(bounds, ctx, win, nk_true);
+    layout->row.index++;
+}
+
+NK_INTERN void
+nk_layout_peek(struct nk_rect *bounds, struct nk_context *ctx)
+{
+    float y;
+    int index;
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    y = layout->at_y;
+    index = layout->row.index;
+    if (layout->row.index >= layout->row.columns) {
+        layout->at_y += layout->row.height;
+        layout->row.index = 0;
+    }
+    nk_layout_widget_space(bounds, ctx, win, nk_false);
+    layout->at_y = y;
+    layout->row.index = index;
+}
+
+NK_INTERN int
+nk_tree_state_base(struct nk_context *ctx, enum nk_tree_type type,
+    struct nk_image *img, const char *title, enum nk_collapse_states *state)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    const struct nk_style *style;
+    struct nk_command_buffer *out;
+    const struct nk_input *in;
+    const struct nk_style_button *button;
+    enum nk_symbol_type symbol;
+    float row_height;
+
+    struct nk_vec2 item_spacing;
+    struct nk_rect header = {0,0,0,0};
+    struct nk_rect sym = {0,0,0,0};
+    struct nk_text text;
+
+    nk_flags ws = 0;
+    enum nk_widget_layout_states widget_state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    /* cache some data */
+    win = ctx->current;
+    layout = win->layout;
+    out = &win->buffer;
+    style = &ctx->style;
+    item_spacing = style->window.spacing;
+
+    /* calculate header bounds and draw background */
+    row_height = style->font->height + 2 * style->tab.padding.y;
+    nk_layout_set_min_row_height(ctx, row_height);
+    nk_layout_row_dynamic(ctx, row_height, 1);
+    nk_layout_reset_min_row_height(ctx);
+
+    widget_state = nk_widget(&header, ctx);
+    if (type == NK_TREE_TAB) {
+        const struct nk_style_item *background = &style->tab.background;
+        if (background->type == NK_STYLE_ITEM_IMAGE) {
+            nk_draw_image(out, header, &background->data.image, nk_white);
+            text.background = nk_rgba(0,0,0,0);
+        } else {
+            text.background = background->data.color;
+            nk_fill_rect(out, header, 0, style->tab.border_color);
+            nk_fill_rect(out, nk_shrink_rect(header, style->tab.border),
+                style->tab.rounding, background->data.color);
+        }
+    } else text.background = style->window.background;
+
+    /* update node state */
+    in = (!(layout->flags & NK_WINDOW_ROM)) ? &ctx->input: 0;
+    in = (in && widget_state == NK_WIDGET_VALID) ? &ctx->input : 0;
+    if (nk_button_behavior(&ws, header, in, NK_BUTTON_DEFAULT))
+        *state = (*state == NK_MAXIMIZED) ? NK_MINIMIZED : NK_MAXIMIZED;
+
+    /* select correct button style */
+    if (*state == NK_MAXIMIZED) {
+        symbol = style->tab.sym_maximize;
+        if (type == NK_TREE_TAB)
+            button = &style->tab.tab_maximize_button;
+        else button = &style->tab.node_maximize_button;
+    } else {
+        symbol = style->tab.sym_minimize;
+        if (type == NK_TREE_TAB)
+            button = &style->tab.tab_minimize_button;
+        else button = &style->tab.node_minimize_button;
+    }
+
+    {/* draw triangle button */
+    sym.w = sym.h = style->font->height;
+    sym.y = header.y + style->tab.padding.y;
+    sym.x = header.x + style->tab.padding.x;
+    nk_do_button_symbol(&ws, &win->buffer, sym, symbol, NK_BUTTON_DEFAULT,
+        button, 0, style->font);
+
+    if (img) {
+        /* draw optional image icon */
+        sym.x = sym.x + sym.w + 4 * item_spacing.x;
+        nk_draw_image(&win->buffer, sym, img, nk_white);
+        sym.w = style->font->height + style->tab.spacing.x;}
+    }
+
+    {/* draw label */
+    struct nk_rect label;
+    header.w = NK_MAX(header.w, sym.w + item_spacing.x);
+    label.x = sym.x + sym.w + item_spacing.x;
+    label.y = sym.y;
+    label.w = header.w - (sym.w + item_spacing.y + style->tab.indent);
+    label.h = style->font->height;
+    text.text = style->tab.text;
+    text.padding = nk_vec2(0,0);
+    nk_widget_text(out, label, title, nk_strlen(title), &text,
+        NK_TEXT_LEFT, style->font);}
+
+    /* increase x-axis cursor widget position pointer */
+    if (*state == NK_MAXIMIZED) {
+        layout->at_x = header.x + (float)*layout->offset_x + style->tab.indent;
+        layout->bounds.w = NK_MAX(layout->bounds.w, style->tab.indent);
+        layout->bounds.w -= (style->tab.indent + style->window.padding.x);
+        layout->row.tree_depth++;
+        return nk_true;
+    } else return nk_false;
+}
+
+NK_INTERN int
+nk_tree_base(struct nk_context *ctx, enum nk_tree_type type,
+    struct nk_image *img, const char *title, enum nk_collapse_states initial_state,
+    const char *hash, int len, int line)
+{
+    struct nk_window *win = ctx->current;
+    int title_len = 0;
+    nk_hash tree_hash = 0;
+    nk_uint *state = 0;
+
+    /* retrieve tree state from internal widget state tables */
+    if (!hash) {
+        title_len = (int)nk_strlen(title);
+        tree_hash = nk_murmur_hash(title, (int)title_len, (nk_hash)line);
+    } else tree_hash = nk_murmur_hash(hash, len, (nk_hash)line);
+    state = nk_find_value(win, tree_hash);
+    if (!state) {
+        state = nk_add_value(ctx, win, tree_hash, 0);
+        *state = initial_state;
+    }
+    return nk_tree_state_base(ctx, type, img, title, (enum nk_collapse_states*)state);
+}
+
+NK_API int
+nk_tree_state_push(struct nk_context *ctx, enum nk_tree_type type,
+    const char *title, enum nk_collapse_states *state)
+{return nk_tree_state_base(ctx, type, 0, title, state);}
+
+NK_API int
+nk_tree_state_image_push(struct nk_context *ctx, enum nk_tree_type type,
+    struct nk_image img, const char *title, enum nk_collapse_states *state)
+{return nk_tree_state_base(ctx, type, &img, title, state);}
+
+NK_API void
+nk_tree_state_pop(struct nk_context *ctx)
+{
+    struct nk_window *win = 0;
+    struct nk_panel *layout = 0;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    layout->at_x -= ctx->style.tab.indent + ctx->style.window.padding.x;
+    layout->bounds.w += ctx->style.tab.indent + ctx->style.window.padding.x;
+    NK_ASSERT(layout->row.tree_depth);
+    layout->row.tree_depth--;
+}
+
+NK_API int
+nk_tree_push_hashed(struct nk_context *ctx, enum nk_tree_type type,
+    const char *title, enum nk_collapse_states initial_state,
+    const char *hash, int len, int line)
+{return nk_tree_base(ctx, type, 0, title, initial_state, hash, len, line);}
+
+NK_API int
+nk_tree_image_push_hashed(struct nk_context *ctx, enum nk_tree_type type,
+    struct nk_image img, const char *title, enum nk_collapse_states initial_state,
+    const char *hash, int len,int seed)
+{return nk_tree_base(ctx, type, &img, title, initial_state, hash, len, seed);}
+
+NK_API void
+nk_tree_pop(struct nk_context *ctx)
+{nk_tree_state_pop(ctx);}
+
+/*----------------------------------------------------------------
+ *
+ *                          WIDGETS
+ *
+ * --------------------------------------------------------------*/
+NK_API struct nk_rect
+nk_widget_bounds(struct nk_context *ctx)
+{
+    struct nk_rect bounds;
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current)
+        return nk_rect(0,0,0,0);
+    nk_layout_peek(&bounds, ctx);
+    return bounds;
+}
+
+NK_API struct nk_vec2
+nk_widget_position(struct nk_context *ctx)
+{
+    struct nk_rect bounds;
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current)
+        return nk_vec2(0,0);
+
+    nk_layout_peek(&bounds, ctx);
+    return nk_vec2(bounds.x, bounds.y);
+}
+
+NK_API struct nk_vec2
+nk_widget_size(struct nk_context *ctx)
+{
+    struct nk_rect bounds;
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current)
+        return nk_vec2(0,0);
+
+    nk_layout_peek(&bounds, ctx);
+    return nk_vec2(bounds.w, bounds.h);
+}
+
+NK_API float
+nk_widget_width(struct nk_context *ctx)
+{
+    struct nk_rect bounds;
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current)
+        return 0;
+
+    nk_layout_peek(&bounds, ctx);
+    return bounds.w;
+}
+
+NK_API float
+nk_widget_height(struct nk_context *ctx)
+{
+    struct nk_rect bounds;
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current)
+        return 0;
+
+    nk_layout_peek(&bounds, ctx);
+    return bounds.h;
+}
+
+NK_API int
+nk_widget_is_hovered(struct nk_context *ctx)
+{
+    struct nk_rect c, v;
+    struct nk_rect bounds;
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current || ctx->active != ctx->current)
+        return 0;
+
+    c = ctx->current->layout->clip;
+    c.x = (float)((int)c.x);
+    c.y = (float)((int)c.y);
+    c.w = (float)((int)c.w);
+    c.h = (float)((int)c.h);
+
+    nk_layout_peek(&bounds, ctx);
+    nk_unify(&v, &c, bounds.x, bounds.y, bounds.x + bounds.w, bounds.y + bounds.h);
+    if (!NK_INTERSECT(c.x, c.y, c.w, c.h, bounds.x, bounds.y, bounds.w, bounds.h))
+        return 0;
+    return nk_input_is_mouse_hovering_rect(&ctx->input, bounds);
+}
+
+NK_API int
+nk_widget_is_mouse_clicked(struct nk_context *ctx, enum nk_buttons btn)
+{
+    struct nk_rect c, v;
+    struct nk_rect bounds;
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current || ctx->active != ctx->current)
+        return 0;
+
+    c = ctx->current->layout->clip;
+    c.x = (float)((int)c.x);
+    c.y = (float)((int)c.y);
+    c.w = (float)((int)c.w);
+    c.h = (float)((int)c.h);
+
+    nk_layout_peek(&bounds, ctx);
+    nk_unify(&v, &c, bounds.x, bounds.y, bounds.x + bounds.w, bounds.y + bounds.h);
+    if (!NK_INTERSECT(c.x, c.y, c.w, c.h, bounds.x, bounds.y, bounds.w, bounds.h))
+        return 0;
+    return nk_input_mouse_clicked(&ctx->input, btn, bounds);
+}
+
+NK_API int
+nk_widget_has_mouse_click_down(struct nk_context *ctx, enum nk_buttons btn, int down)
+{
+    struct nk_rect c, v;
+    struct nk_rect bounds;
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current || ctx->active != ctx->current)
+        return 0;
+
+    c = ctx->current->layout->clip;
+    c.x = (float)((int)c.x);
+    c.y = (float)((int)c.y);
+    c.w = (float)((int)c.w);
+    c.h = (float)((int)c.h);
+
+    nk_layout_peek(&bounds, ctx);
+    nk_unify(&v, &c, bounds.x, bounds.y, bounds.x + bounds.w, bounds.y + bounds.h);
+    if (!NK_INTERSECT(c.x, c.y, c.w, c.h, bounds.x, bounds.y, bounds.w, bounds.h))
+        return 0;
+    return nk_input_has_mouse_click_down_in_rect(&ctx->input, btn, bounds, down);
+}
+
+NK_API enum nk_widget_layout_states
+nk_widget(struct nk_rect *bounds, const struct nk_context *ctx)
+{
+    struct nk_rect c, v;
+    struct nk_window *win;
+    struct nk_panel *layout;
+    const struct nk_input *in;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return NK_WIDGET_INVALID;
+
+    /* allocate space and check if the widget needs to be updated and drawn */
+    nk_panel_alloc_space(bounds, ctx);
+    win = ctx->current;
+    layout = win->layout;
+    in = &ctx->input;
+    c = layout->clip;
+
+    /*  if one of these triggers you forgot to add an `if` condition around either
+        a window, group, popup, combobox or contextual menu `begin` and `end` block.
+        Example:
+            if (nk_begin(...) {...} nk_end(...); or
+            if (nk_group_begin(...) { nk_group_end(...);} */
+    NK_ASSERT(!(layout->flags & NK_WINDOW_MINIMIZED));
+    NK_ASSERT(!(layout->flags & NK_WINDOW_HIDDEN));
+    NK_ASSERT(!(layout->flags & NK_WINDOW_CLOSED));
+
+    /* need to convert to int here to remove floating point errors */
+    bounds->x = (float)((int)bounds->x);
+    bounds->y = (float)((int)bounds->y);
+    bounds->w = (float)((int)bounds->w);
+    bounds->h = (float)((int)bounds->h);
+
+    c.x = (float)((int)c.x);
+    c.y = (float)((int)c.y);
+    c.w = (float)((int)c.w);
+    c.h = (float)((int)c.h);
+
+    nk_unify(&v, &c, bounds->x, bounds->y, bounds->x + bounds->w, bounds->y + bounds->h);
+    if (!NK_INTERSECT(c.x, c.y, c.w, c.h, bounds->x, bounds->y, bounds->w, bounds->h))
+        return NK_WIDGET_INVALID;
+    if (!NK_INBOX(in->mouse.pos.x, in->mouse.pos.y, v.x, v.y, v.w, v.h))
+        return NK_WIDGET_ROM;
+    return NK_WIDGET_VALID;
+}
+
+NK_API enum nk_widget_layout_states
+nk_widget_fitting(struct nk_rect *bounds, struct nk_context *ctx,
+    struct nk_vec2 item_padding)
+{
+    /* update the bounds to stand without padding  */
+    struct nk_window *win;
+    struct nk_style *style;
+    struct nk_panel *layout;
+    enum nk_widget_layout_states state;
+    struct nk_vec2 panel_padding;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return NK_WIDGET_INVALID;
+
+    win = ctx->current;
+    style = &ctx->style;
+    layout = win->layout;
+    state = nk_widget(bounds, ctx);
+
+    panel_padding = nk_panel_get_padding(style, layout->type);
+    if (layout->row.index == 1) {
+        bounds->w += panel_padding.x;
+        bounds->x -= panel_padding.x;
+    } else bounds->x -= item_padding.x;
+
+    if (layout->row.index == layout->row.columns)
+        bounds->w += panel_padding.x;
+    else bounds->w += item_padding.x;
+    return state;
+}
+
+/*----------------------------------------------------------------
+ *
+ *                          MISC
+ *
+ * --------------------------------------------------------------*/
+NK_API void
+nk_spacing(struct nk_context *ctx, int cols)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    struct nk_rect none;
+    int i, index, rows;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    /* spacing over row boundaries */
+    win = ctx->current;
+    layout = win->layout;
+    index = (layout->row.index + cols) % layout->row.columns;
+    rows = (layout->row.index + cols) / layout->row.columns;
+    if (rows) {
+        for (i = 0; i < rows; ++i)
+            nk_panel_alloc_row(ctx, win);
+        cols = index;
+    }
+    /* non table layout need to allocate space */
+    if (layout->row.type != NK_LAYOUT_DYNAMIC_FIXED &&
+        layout->row.type != NK_LAYOUT_STATIC_FIXED) {
+        for (i = 0; i < cols; ++i)
+            nk_panel_alloc_space(&none, ctx);
+    }
+    layout->row.index = index;
+}
+
+/*----------------------------------------------------------------
+ *
+ *                          TEXT
+ *
+ * --------------------------------------------------------------*/
+NK_API void
+nk_text_colored(struct nk_context *ctx, const char *str, int len,
+    nk_flags alignment, struct nk_color color)
+{
+    struct nk_window *win;
+    const struct nk_style *style;
+
+    struct nk_vec2 item_padding;
+    struct nk_rect bounds;
+    struct nk_text text;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout) return;
+
+    win = ctx->current;
+    style = &ctx->style;
+    nk_panel_alloc_space(&bounds, ctx);
+    item_padding = style->text.padding;
+
+    text.padding.x = item_padding.x;
+    text.padding.y = item_padding.y;
+    text.background = style->window.background;
+    text.text = color;
+    nk_widget_text(&win->buffer, bounds, str, len, &text, alignment, style->font);
+}
+
+NK_API void
+nk_text_wrap_colored(struct nk_context *ctx, const char *str,
+    int len, struct nk_color color)
+{
+    struct nk_window *win;
+    const struct nk_style *style;
+
+    struct nk_vec2 item_padding;
+    struct nk_rect bounds;
+    struct nk_text text;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout) return;
+
+    win = ctx->current;
+    style = &ctx->style;
+    nk_panel_alloc_space(&bounds, ctx);
+    item_padding = style->text.padding;
+
+    text.padding.x = item_padding.x;
+    text.padding.y = item_padding.y;
+    text.background = style->window.background;
+    text.text = color;
+    nk_widget_text_wrap(&win->buffer, bounds, str, len, &text, style->font);
+}
+
+#ifdef NK_INCLUDE_STANDARD_VARARGS
+NK_API void
+nk_labelf_colored(struct nk_context *ctx, nk_flags flags,
+    struct nk_color color, const char *fmt, ...)
+{
+    char buf[256];
+    va_list args;
+    va_start(args, fmt);
+    nk_strfmt(buf, NK_LEN(buf), fmt, args);
+    nk_label_colored(ctx, buf, flags, color);
+    va_end(args);
+}
+
+NK_API void
+nk_labelf_colored_wrap(struct nk_context *ctx, struct nk_color color,
+    const char *fmt, ...)
+{
+    char buf[256];
+    va_list args;
+    va_start(args, fmt);
+    nk_strfmt(buf, NK_LEN(buf), fmt, args);
+    nk_label_colored_wrap(ctx, buf, color);
+    va_end(args);
+}
+
+NK_API void
+nk_labelf(struct nk_context *ctx, nk_flags flags, const char *fmt, ...)
+{
+    char buf[256];
+    va_list args;
+    va_start(args, fmt);
+    nk_strfmt(buf, NK_LEN(buf), fmt, args);
+    nk_label(ctx, buf, flags);
+    va_end(args);
+}
+
+NK_API void
+nk_labelf_wrap(struct nk_context *ctx, const char *fmt,...)
+{
+    char buf[256];
+    va_list args;
+    va_start(args, fmt);
+    nk_strfmt(buf, NK_LEN(buf), fmt, args);
+    nk_label_wrap(ctx, buf);
+    va_end(args);
+}
+
+NK_API void
+nk_value_bool(struct nk_context *ctx, const char *prefix, int value)
+{nk_labelf(ctx, NK_TEXT_LEFT, "%s: %s", prefix, ((value) ? "true": "false"));}
+
+NK_API void
+nk_value_int(struct nk_context *ctx, const char *prefix, int value)
+{nk_labelf(ctx, NK_TEXT_LEFT, "%s: %d", prefix, value);}
+
+NK_API void
+nk_value_uint(struct nk_context *ctx, const char *prefix, unsigned int value)
+{nk_labelf(ctx, NK_TEXT_LEFT, "%s: %u", prefix, value);}
+
+NK_API void
+nk_value_float(struct nk_context *ctx, const char *prefix, float value)
+{
+    double double_value = (double)value;
+    nk_labelf(ctx, NK_TEXT_LEFT, "%s: %.3f", prefix, double_value);
+}
+
+NK_API void
+nk_value_color_byte(struct nk_context *ctx, const char *p, struct nk_color c)
+{nk_labelf(ctx, NK_TEXT_LEFT, "%s: (%d, %d, %d, %d)", p, c.r, c.g, c.b, c.a);}
+
+NK_API void
+nk_value_color_float(struct nk_context *ctx, const char *p, struct nk_color color)
+{
+    double c[4]; nk_color_dv(c, color);
+    nk_labelf(ctx, NK_TEXT_LEFT, "%s: (%.2f, %.2f, %.2f, %.2f)",
+        p, c[0], c[1], c[2], c[3]);
+}
+
+NK_API void
+nk_value_color_hex(struct nk_context *ctx, const char *prefix, struct nk_color color)
+{
+    char hex[16];
+    nk_color_hex_rgba(hex, color);
+    nk_labelf(ctx, NK_TEXT_LEFT, "%s: %s", prefix, hex);
+}
+#endif
+
+NK_API void
+nk_text(struct nk_context *ctx, const char *str, int len, nk_flags alignment)
+{
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    nk_text_colored(ctx, str, len, alignment, ctx->style.text.color);
+}
+
+NK_API void
+nk_text_wrap(struct nk_context *ctx, const char *str, int len)
+{
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    nk_text_wrap_colored(ctx, str, len, ctx->style.text.color);
+}
+
+NK_API void
+nk_label(struct nk_context *ctx, const char *str, nk_flags alignment)
+{nk_text(ctx, str, nk_strlen(str), alignment);}
+
+NK_API void
+nk_label_colored(struct nk_context *ctx, const char *str, nk_flags align,
+    struct nk_color color)
+{nk_text_colored(ctx, str, nk_strlen(str), align, color);}
+
+NK_API void
+nk_label_wrap(struct nk_context *ctx, const char *str)
+{nk_text_wrap(ctx, str, nk_strlen(str));}
+
+NK_API void
+nk_label_colored_wrap(struct nk_context *ctx, const char *str, struct nk_color color)
+{nk_text_wrap_colored(ctx, str, nk_strlen(str), color);}
+
+NK_API void
+nk_image(struct nk_context *ctx, struct nk_image img)
+{
+    struct nk_window *win;
+    struct nk_rect bounds;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout) return;
+
+    win = ctx->current;
+    if (!nk_widget(&bounds, ctx)) return;
+    nk_draw_image(&win->buffer, bounds, &img, nk_white);
+}
+
+/*----------------------------------------------------------------
+ *
+ *                          BUTTON
+ *
+ * --------------------------------------------------------------*/
+NK_API void
+nk_button_set_behavior(struct nk_context *ctx, enum nk_button_behavior behavior)
+{
+    NK_ASSERT(ctx);
+    if (!ctx) return;
+    ctx->button_behavior = behavior;
+}
+
+NK_API int
+nk_button_push_behavior(struct nk_context *ctx, enum nk_button_behavior behavior)
+{
+    struct nk_config_stack_button_behavior *button_stack;
+    struct nk_config_stack_button_behavior_element *element;
+
+    NK_ASSERT(ctx);
+    if (!ctx) return 0;
+
+    button_stack = &ctx->stacks.button_behaviors;
+    NK_ASSERT(button_stack->head < (int)NK_LEN(button_stack->elements));
+    if (button_stack->head >= (int)NK_LEN(button_stack->elements))
+        return 0;
+
+    element = &button_stack->elements[button_stack->head++];
+    element->address = &ctx->button_behavior;
+    element->old_value = ctx->button_behavior;
+    ctx->button_behavior = behavior;
+    return 1;
+}
+
+NK_API int
+nk_button_pop_behavior(struct nk_context *ctx)
+{
+    struct nk_config_stack_button_behavior *button_stack;
+    struct nk_config_stack_button_behavior_element *element;
+
+    NK_ASSERT(ctx);
+    if (!ctx) return 0;
+
+    button_stack = &ctx->stacks.button_behaviors;
+    NK_ASSERT(button_stack->head > 0);
+    if (button_stack->head < 1)
+        return 0;
+
+    element = &button_stack->elements[--button_stack->head];
+    *element->address = element->old_value;
+    return 1;
+}
+
+NK_API int
+nk_button_text_styled(struct nk_context *ctx,
+    const struct nk_style_button *style, const char *title, int len)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    const struct nk_input *in;
+
+    struct nk_rect bounds;
+    enum nk_widget_layout_states state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(style);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!style || !ctx || !ctx->current || !ctx->current->layout) return 0;
+
+    win = ctx->current;
+    layout = win->layout;
+    state = nk_widget(&bounds, ctx);
+
+    if (!state) return 0;
+    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    return nk_do_button_text(&ctx->last_widget_state, &win->buffer, bounds,
+                    title, len, style->text_alignment, ctx->button_behavior,
+                    style, in, ctx->style.font);
+}
+
+NK_API int
+nk_button_text(struct nk_context *ctx, const char *title, int len)
+{
+    NK_ASSERT(ctx);
+    if (!ctx) return 0;
+    return nk_button_text_styled(ctx, &ctx->style.button, title, len);
+}
+
+NK_API int nk_button_label_styled(struct nk_context *ctx,
+    const struct nk_style_button *style, const char *title)
+{return nk_button_text_styled(ctx, style, title, nk_strlen(title));}
+
+NK_API int nk_button_label(struct nk_context *ctx, const char *title)
+{return nk_button_text(ctx, title, nk_strlen(title));}
+
+NK_API int
+nk_button_color(struct nk_context *ctx, struct nk_color color)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    const struct nk_input *in;
+    struct nk_style_button button;
+
+    int ret = 0;
+    struct nk_rect bounds;
+    struct nk_rect content;
+    enum nk_widget_layout_states state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    layout = win->layout;
+
+    state = nk_widget(&bounds, ctx);
+    if (!state) return 0;
+    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+
+    button = ctx->style.button;
+    button.normal = nk_style_item_color(color);
+    button.hover = nk_style_item_color(color);
+    button.active = nk_style_item_color(color);
+    ret = nk_do_button(&ctx->last_widget_state, &win->buffer, bounds,
+                &button, in, ctx->button_behavior, &content);
+    nk_draw_button(&win->buffer, &bounds, ctx->last_widget_state, &button);
+    return ret;
+}
+
+NK_API int
+nk_button_symbol_styled(struct nk_context *ctx,
+    const struct nk_style_button *style, enum nk_symbol_type symbol)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    const struct nk_input *in;
+
+    struct nk_rect bounds;
+    enum nk_widget_layout_states state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    layout = win->layout;
+    state = nk_widget(&bounds, ctx);
+    if (!state) return 0;
+    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    return nk_do_button_symbol(&ctx->last_widget_state, &win->buffer, bounds,
+            symbol, ctx->button_behavior, style, in, ctx->style.font);
+}
+
+NK_API int
+nk_button_symbol(struct nk_context *ctx, enum nk_symbol_type symbol)
+{
+    NK_ASSERT(ctx);
+    if (!ctx) return 0;
+    return nk_button_symbol_styled(ctx, &ctx->style.button, symbol);
+}
+
+NK_API int
+nk_button_image_styled(struct nk_context *ctx, const struct nk_style_button *style,
+    struct nk_image img)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    const struct nk_input *in;
+
+    struct nk_rect bounds;
+    enum nk_widget_layout_states state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    layout = win->layout;
+
+    state = nk_widget(&bounds, ctx);
+    if (!state) return 0;
+    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    return nk_do_button_image(&ctx->last_widget_state, &win->buffer, bounds,
+                img, ctx->button_behavior, style, in);
+}
+
+NK_API int
+nk_button_image(struct nk_context *ctx, struct nk_image img)
+{
+    NK_ASSERT(ctx);
+    if (!ctx) return 0;
+    return nk_button_image_styled(ctx, &ctx->style.button, img);
+}
+
+NK_API int
+nk_button_symbol_text_styled(struct nk_context *ctx,
+    const struct nk_style_button *style, enum nk_symbol_type symbol,
+    const char *text, int len, nk_flags align)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    const struct nk_input *in;
+
+    struct nk_rect bounds;
+    enum nk_widget_layout_states state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    layout = win->layout;
+
+    state = nk_widget(&bounds, ctx);
+    if (!state) return 0;
+    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    return nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, bounds,
+                symbol, text, len, align, ctx->button_behavior,
+                style, ctx->style.font, in);
+}
+
+NK_API int
+nk_button_symbol_text(struct nk_context *ctx, enum nk_symbol_type symbol,
+    const char* text, int len, nk_flags align)
+{
+    NK_ASSERT(ctx);
+    if (!ctx) return 0;
+    return nk_button_symbol_text_styled(ctx, &ctx->style.button, symbol, text, len, align);
+}
+
+NK_API int nk_button_symbol_label(struct nk_context *ctx, enum nk_symbol_type symbol,
+    const char *label, nk_flags align)
+{return nk_button_symbol_text(ctx, symbol, label, nk_strlen(label), align);}
+
+NK_API int 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)
+{return nk_button_symbol_text_styled(ctx, style, symbol, title, nk_strlen(title), align);}
+
+NK_API int
+nk_button_image_text_styled(struct nk_context *ctx,
+    const struct nk_style_button *style, struct nk_image img, const char *text,
+    int len, nk_flags align)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    const struct nk_input *in;
+
+    struct nk_rect bounds;
+    enum nk_widget_layout_states state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    layout = win->layout;
+
+    state = nk_widget(&bounds, ctx);
+    if (!state) return 0;
+    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    return nk_do_button_text_image(&ctx->last_widget_state, &win->buffer,
+            bounds, img, text, len, align, ctx->button_behavior,
+            style, ctx->style.font, in);
+}
+
+NK_API int
+nk_button_image_text(struct nk_context *ctx, struct nk_image img,
+    const char *text, int len, nk_flags align)
+{return nk_button_image_text_styled(ctx, &ctx->style.button,img, text, len, align);}
+
+
+NK_API int nk_button_image_label(struct nk_context *ctx, struct nk_image img,
+    const char *label, nk_flags align)
+{return nk_button_image_text(ctx, img, label, nk_strlen(label), align);}
+
+NK_API int nk_button_image_label_styled(struct nk_context *ctx,
+    const struct nk_style_button *style, struct nk_image img,
+    const char *label, nk_flags text_alignment)
+{return nk_button_image_text_styled(ctx, style, img, label, nk_strlen(label), text_alignment);}
+
+/*----------------------------------------------------------------
+ *
+ *                          SELECTABLE
+ *
+ * --------------------------------------------------------------*/
+NK_API int
+nk_selectable_text(struct nk_context *ctx, const char *str, int len,
+    nk_flags align, int *value)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    const struct nk_input *in;
+    const struct nk_style *style;
+
+    enum nk_widget_layout_states state;
+    struct nk_rect bounds;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(value);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout || !value)
+        return 0;
+
+    win = ctx->current;
+    layout = win->layout;
+    style = &ctx->style;
+
+    state = nk_widget(&bounds, ctx);
+    if (!state) return 0;
+    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    return nk_do_selectable(&ctx->last_widget_state, &win->buffer, bounds,
+                str, len, align, value, &style->selectable, in, style->font);
+}
+
+NK_API int
+nk_selectable_image_text(struct nk_context *ctx, struct nk_image img,
+    const char *str, int len, nk_flags align, int *value)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    const struct nk_input *in;
+    const struct nk_style *style;
+
+    enum nk_widget_layout_states state;
+    struct nk_rect bounds;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(value);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout || !value)
+        return 0;
+
+    win = ctx->current;
+    layout = win->layout;
+    style = &ctx->style;
+
+    state = nk_widget(&bounds, ctx);
+    if (!state) return 0;
+    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    return nk_do_selectable_image(&ctx->last_widget_state, &win->buffer, bounds,
+                str, len, align, value, &img, &style->selectable, in, style->font);
+}
+
+NK_API int nk_select_text(struct nk_context *ctx, const char *str, int len,
+    nk_flags align, int value)
+{nk_selectable_text(ctx, str, len, align, &value);return value;}
+
+NK_API int nk_selectable_label(struct nk_context *ctx, const char *str, nk_flags align, int *value)
+{return nk_selectable_text(ctx, str, nk_strlen(str), align, value);}
+
+NK_API int nk_selectable_image_label(struct nk_context *ctx,struct nk_image img,
+    const char *str, nk_flags align, int *value)
+{return nk_selectable_image_text(ctx, img, str, nk_strlen(str), align, value);}
+
+NK_API int nk_select_label(struct nk_context *ctx, const char *str, nk_flags align, int value)
+{nk_selectable_text(ctx, str, nk_strlen(str), align, &value);return value;}
+
+NK_API int nk_select_image_label(struct nk_context *ctx, struct nk_image img,
+    const char *str, nk_flags align, int value)
+{nk_selectable_image_text(ctx, img, str, nk_strlen(str), align, &value);return value;}
+
+NK_API int nk_select_image_text(struct nk_context *ctx, struct nk_image img,
+    const char *str, int len, nk_flags align, int value)
+{nk_selectable_image_text(ctx, img, str, len, align, &value);return value;}
+
+/*----------------------------------------------------------------
+ *
+ *                          CHECKBOX
+ *
+ * --------------------------------------------------------------*/
+NK_API int
+nk_check_text(struct nk_context *ctx, const char *text, int len, int active)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    const struct nk_input *in;
+    const struct nk_style *style;
+
+    struct nk_rect bounds;
+    enum nk_widget_layout_states state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return active;
+
+    win = ctx->current;
+    style = &ctx->style;
+    layout = win->layout;
+
+    state = nk_widget(&bounds, ctx);
+    if (!state) return active;
+    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    nk_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &active,
+        text, len, NK_TOGGLE_CHECK, &style->checkbox, in, style->font);
+    return active;
+}
+
+NK_API unsigned int
+nk_check_flags_text(struct nk_context *ctx, const char *text, int len,
+    unsigned int flags, unsigned int value)
+{
+    int old_active;
+    NK_ASSERT(ctx);
+    NK_ASSERT(text);
+    if (!ctx || !text) return flags;
+    old_active = (int)((flags & value) & value);
+    if (nk_check_text(ctx, text, len, old_active))
+        flags |= value;
+    else flags &= ~value;
+    return flags;
+}
+
+NK_API int
+nk_checkbox_text(struct nk_context *ctx, const char *text, int len, int *active)
+{
+    int old_val;
+    NK_ASSERT(ctx);
+    NK_ASSERT(text);
+    NK_ASSERT(active);
+    if (!ctx || !text || !active) return 0;
+    old_val = *active;
+    *active = nk_check_text(ctx, text, len, *active);
+    return old_val != *active;
+}
+
+NK_API int
+nk_checkbox_flags_text(struct nk_context *ctx, const char *text, int len,
+    unsigned int *flags, unsigned int value)
+{
+    int active;
+    NK_ASSERT(ctx);
+    NK_ASSERT(text);
+    NK_ASSERT(flags);
+    if (!ctx || !text || !flags) return 0;
+
+    active = (int)((*flags & value) & value);
+    if (nk_checkbox_text(ctx, text, len, &active)) {
+        if (active) *flags |= value;
+        else *flags &= ~value;
+        return 1;
+    }
+    return 0;
+}
+
+NK_API int nk_check_label(struct nk_context *ctx, const char *label, int active)
+{return nk_check_text(ctx, label, nk_strlen(label), active);}
+
+NK_API unsigned int nk_check_flags_label(struct nk_context *ctx, const char *label,
+    unsigned int flags, unsigned int value)
+{return nk_check_flags_text(ctx, label, nk_strlen(label), flags, value);}
+
+NK_API int nk_checkbox_label(struct nk_context *ctx, const char *label, int *active)
+{return nk_checkbox_text(ctx, label, nk_strlen(label), active);}
+
+NK_API int nk_checkbox_flags_label(struct nk_context *ctx, const char *label,
+    unsigned int *flags, unsigned int value)
+{return nk_checkbox_flags_text(ctx, label, nk_strlen(label), flags, value);}
+
+/*----------------------------------------------------------------
+ *
+ *                          OPTION
+ *
+ * --------------------------------------------------------------*/
+NK_API int
+nk_option_text(struct nk_context *ctx, const char *text, int len, int is_active)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    const struct nk_input *in;
+    const struct nk_style *style;
+
+    struct nk_rect bounds;
+    enum nk_widget_layout_states state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return is_active;
+
+    win = ctx->current;
+    style = &ctx->style;
+    layout = win->layout;
+
+    state = nk_widget(&bounds, ctx);
+    if (!state) return state;
+    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    nk_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &is_active,
+        text, len, NK_TOGGLE_OPTION, &style->option, in, style->font);
+    return is_active;
+}
+
+NK_API int
+nk_radio_text(struct nk_context *ctx, const char *text, int len, int *active)
+{
+    int old_value;
+    NK_ASSERT(ctx);
+    NK_ASSERT(text);
+    NK_ASSERT(active);
+    if (!ctx || !text || !active) return 0;
+    old_value = *active;
+    *active = nk_option_text(ctx, text, len, old_value);
+    return old_value != *active;
+}
+
+NK_API int
+nk_option_label(struct nk_context *ctx, const char *label, int active)
+{return nk_option_text(ctx, label, nk_strlen(label), active);}
+
+NK_API int
+nk_radio_label(struct nk_context *ctx, const char *label, int *active)
+{return nk_radio_text(ctx, label, nk_strlen(label), active);}
+
+/*----------------------------------------------------------------
+ *
+ *                          SLIDER
+ *
+ * --------------------------------------------------------------*/
+NK_API int
+nk_slider_float(struct nk_context *ctx, float min_value, float *value, float max_value,
+    float value_step)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    struct nk_input *in;
+    const struct nk_style *style;
+
+    int ret = 0;
+    float old_value;
+    struct nk_rect bounds;
+    enum nk_widget_layout_states state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    NK_ASSERT(value);
+    if (!ctx || !ctx->current || !ctx->current->layout || !value)
+        return ret;
+
+    win = ctx->current;
+    style = &ctx->style;
+    layout = win->layout;
+
+    state = nk_widget(&bounds, ctx);
+    if (!state) return ret;
+    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+
+    old_value = *value;
+    *value = nk_do_slider(&ctx->last_widget_state, &win->buffer, bounds, min_value,
+                old_value, max_value, value_step, &style->slider, in, style->font);
+    return (old_value > *value || old_value < *value);
+}
+
+NK_API float
+nk_slide_float(struct nk_context *ctx, float min, float val, float max, float step)
+{
+    nk_slider_float(ctx, min, &val, max, step); return val;
+}
+
+NK_API int
+nk_slide_int(struct nk_context *ctx, int min, int val, int max, int step)
+{
+    float value = (float)val;
+    nk_slider_float(ctx, (float)min, &value, (float)max, (float)step);
+    return (int)value;
+}
+
+NK_API int
+nk_slider_int(struct nk_context *ctx, int min, int *val, int max, int step)
+{
+    int ret;
+    float value = (float)*val;
+    ret = nk_slider_float(ctx, (float)min, &value, (float)max, (float)step);
+    *val =  (int)value;
+    return ret;
+}
+
+/*----------------------------------------------------------------
+ *
+ *                          PROGRESSBAR
+ *
+ * --------------------------------------------------------------*/
+NK_API int
+nk_progress(struct nk_context *ctx, nk_size *cur, nk_size max, int is_modifyable)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    const struct nk_style *style;
+    const struct nk_input *in;
+
+    struct nk_rect bounds;
+    enum nk_widget_layout_states state;
+    nk_size old_value;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(cur);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout || !cur)
+        return 0;
+
+    win = ctx->current;
+    style = &ctx->style;
+    layout = win->layout;
+    state = nk_widget(&bounds, ctx);
+    if (!state) return 0;
+
+    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    old_value = *cur;
+    *cur = nk_do_progress(&ctx->last_widget_state, &win->buffer, bounds,
+            *cur, max, is_modifyable, &style->progress, in);
+    return (*cur != old_value);
+}
+
+NK_API nk_size nk_prog(struct nk_context *ctx, nk_size cur, nk_size max, int modifyable)
+{nk_progress(ctx, &cur, max, modifyable);return cur;}
+
+/*----------------------------------------------------------------
+ *
+ *                          EDIT
+ *
+ * --------------------------------------------------------------*/
+NK_API void
+nk_edit_focus(struct nk_context *ctx, nk_flags flags)
+{
+    nk_hash hash;
+    struct nk_window *win;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current) return;
+
+    win = ctx->current;
+    hash = win->edit.seq;
+    win->edit.active = nk_true;
+    win->edit.name = hash;
+    if (flags & NK_EDIT_ALWAYS_INSERT_MODE)
+        win->edit.mode = NK_TEXT_EDIT_MODE_INSERT;
+}
+
+NK_API void
+nk_edit_unfocus(struct nk_context *ctx)
+{
+    struct nk_window *win;
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current) return;
+
+    win = ctx->current;
+    win->edit.active = nk_false;
+    win->edit.name = 0;
+}
+
+NK_API nk_flags
+nk_edit_string(struct nk_context *ctx, nk_flags flags,
+    char *memory, int *len, int max, nk_plugin_filter filter)
+{
+    nk_hash hash;
+    nk_flags state;
+    struct nk_text_edit *edit;
+    struct nk_window *win;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(memory);
+    NK_ASSERT(len);
+    if (!ctx || !memory || !len)
+        return 0;
+
+    filter = (!filter) ? nk_filter_default: filter;
+    win = ctx->current;
+    hash = win->edit.seq;
+    edit = &ctx->text_edit;
+    nk_textedit_clear_state(&ctx->text_edit, (flags & NK_EDIT_MULTILINE)?
+        NK_TEXT_EDIT_MULTI_LINE: NK_TEXT_EDIT_SINGLE_LINE, filter);
+
+    if (win->edit.active && hash == win->edit.name) {
+        if (flags & NK_EDIT_NO_CURSOR)
+            edit->cursor = nk_utf_len(memory, *len);
+        else edit->cursor = win->edit.cursor;
+        if (!(flags & NK_EDIT_SELECTABLE)) {
+            edit->select_start = win->edit.cursor;
+            edit->select_end = win->edit.cursor;
+        } else {
+            edit->select_start = win->edit.sel_start;
+            edit->select_end = win->edit.sel_end;
+        }
+        edit->mode = win->edit.mode;
+        edit->scrollbar.x = (float)win->edit.scrollbar.x;
+        edit->scrollbar.y = (float)win->edit.scrollbar.y;
+        edit->active = nk_true;
+    } else edit->active = nk_false;
+
+    max = NK_MAX(1, max);
+    *len = NK_MIN(*len, max-1);
+    nk_str_init_fixed(&edit->string, memory, (nk_size)max);
+    edit->string.buffer.allocated = (nk_size)*len;
+    edit->string.len = nk_utf_len(memory, *len);
+    state = nk_edit_buffer(ctx, flags, edit, filter);
+    *len = (int)edit->string.buffer.allocated;
+
+    if (edit->active) {
+        win->edit.cursor = edit->cursor;
+        win->edit.sel_start = edit->select_start;
+        win->edit.sel_end = edit->select_end;
+        win->edit.mode = edit->mode;
+        win->edit.scrollbar.x = (nk_uint)edit->scrollbar.x;
+        win->edit.scrollbar.y = (nk_uint)edit->scrollbar.y;
+    }
+    return state;
+}
+
+NK_API nk_flags
+nk_edit_buffer(struct nk_context *ctx, nk_flags flags,
+    struct nk_text_edit *edit, nk_plugin_filter filter)
+{
+    struct nk_window *win;
+    struct nk_style *style;
+    struct nk_input *in;
+
+    enum nk_widget_layout_states state;
+    struct nk_rect bounds;
+
+    nk_flags ret_flags = 0;
+    unsigned char prev_state;
+    nk_hash hash;
+
+    /* make sure correct values */
+    NK_ASSERT(ctx);
+    NK_ASSERT(edit);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    style = &ctx->style;
+    state = nk_widget(&bounds, ctx);
+    if (!state) return state;
+    in = (win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+
+    /* check if edit is currently hot item */
+    hash = win->edit.seq++;
+    if (win->edit.active && hash == win->edit.name) {
+        if (flags & NK_EDIT_NO_CURSOR)
+            edit->cursor = edit->string.len;
+        if (!(flags & NK_EDIT_SELECTABLE)) {
+            edit->select_start = edit->cursor;
+            edit->select_end = edit->cursor;
+        }
+        if (flags & NK_EDIT_CLIPBOARD)
+            edit->clip = ctx->clip;
+        edit->active = win->edit.active;
+    } else edit->active = nk_false;
+    edit->mode = win->edit.mode;
+
+    filter = (!filter) ? nk_filter_default: filter;
+    prev_state = (unsigned char)edit->active;
+    in = (flags & NK_EDIT_READ_ONLY) ? 0: in;
+    ret_flags = nk_do_edit(&ctx->last_widget_state, &win->buffer, bounds, flags,
+                    filter, edit, &style->edit, in, style->font);
+
+    if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER)
+        ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_TEXT];
+    if (edit->active && prev_state != edit->active) {
+        /* current edit is now hot */
+        win->edit.active = nk_true;
+        win->edit.name = hash;
+    } else if (prev_state && !edit->active) {
+        /* current edit is now cold */
+        win->edit.active = nk_false;
+    }
+    return ret_flags;
+}
+
+NK_API nk_flags
+nk_edit_string_zero_terminated(struct nk_context *ctx, nk_flags flags,
+    char *buffer, int max, nk_plugin_filter filter)
+{
+    nk_flags result;
+    int len = nk_strlen(buffer);
+    result = nk_edit_string(ctx, flags, buffer, &len, max, filter);
+    buffer[NK_MIN(NK_MAX(max-1,0), len)] = '\0';
+    return result;
+}
+
+/*----------------------------------------------------------------
+ *
+ *                          PROPERTY
+ *
+ * --------------------------------------------------------------*/
+NK_INTERN struct nk_property_variant
+nk_property_variant_int(int value, int min_value, int max_value, int step)
+{
+    struct nk_property_variant result;
+    result.kind = NK_PROPERTY_INT;
+    result.value.i = value;
+    result.min_value.i = min_value;
+    result.max_value.i = max_value;
+    result.step.i = step;
+    return result;
+}
+
+NK_INTERN struct nk_property_variant
+nk_property_variant_float(float value, float min_value, float max_value, float step)
+{
+    struct nk_property_variant result;
+    result.kind = NK_PROPERTY_FLOAT;
+    result.value.f = value;
+    result.min_value.f = min_value;
+    result.max_value.f = max_value;
+    result.step.f = step;
+    return result;
+}
+
+NK_INTERN struct nk_property_variant
+nk_property_variant_double(double value, double min_value, double max_value,
+    double step)
+{
+    struct nk_property_variant result;
+    result.kind = NK_PROPERTY_DOUBLE;
+    result.value.d = value;
+    result.min_value.d = min_value;
+    result.max_value.d = max_value;
+    result.step.d = step;
+    return result;
+}
+
+NK_INTERN void
+nk_property(struct nk_context *ctx, const char *name, struct nk_property_variant *variant,
+    float inc_per_pixel, const enum nk_property_filter filter)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    struct nk_input *in;
+    const struct nk_style *style;
+
+    struct nk_rect bounds;
+    enum nk_widget_layout_states s;
+
+    int *state = 0;
+    nk_hash hash = 0;
+    char *buffer = 0;
+    int *len = 0;
+    int *cursor = 0;
+    int *select_begin = 0;
+    int *select_end = 0;
+    int old_state;
+
+    char dummy_buffer[NK_MAX_NUMBER_BUFFER];
+    int dummy_state = NK_PROPERTY_DEFAULT;
+    int dummy_length = 0;
+    int dummy_cursor = 0;
+    int dummy_select_begin = 0;
+    int dummy_select_end = 0;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    win = ctx->current;
+    layout = win->layout;
+    style = &ctx->style;
+    s = nk_widget(&bounds, ctx);
+    if (!s) return;
+
+    /* calculate hash from name */
+    if (name[0] == '#') {
+        hash = nk_murmur_hash(name, (int)nk_strlen(name), win->property.seq++);
+        name++; /* special number hash */
+    } else hash = nk_murmur_hash(name, (int)nk_strlen(name), 42);
+
+    /* check if property is currently hot item */
+    if (win->property.active && hash == win->property.name) {
+        buffer = win->property.buffer;
+        len = &win->property.length;
+        cursor = &win->property.cursor;
+        state = &win->property.state;
+        select_begin = &win->property.select_start;
+        select_end = &win->property.select_end;
+    } else {
+        buffer = dummy_buffer;
+        len = &dummy_length;
+        cursor = &dummy_cursor;
+        state = &dummy_state;
+        select_begin =  &dummy_select_begin;
+        select_end = &dummy_select_end;
+    }
+
+    /* execute property widget */
+    old_state = *state;
+    ctx->text_edit.clip = ctx->clip;
+    in = ((s == NK_WIDGET_ROM && !win->property.active) ||
+        layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    nk_do_property(&ctx->last_widget_state, &win->buffer, bounds, name,
+        variant, inc_per_pixel, buffer, len, state, cursor, select_begin,
+        select_end, &style->property, filter, in, style->font, &ctx->text_edit,
+        ctx->button_behavior);
+
+    if (in && *state != NK_PROPERTY_DEFAULT && !win->property.active) {
+        /* current property is now hot */
+        win->property.active = 1;
+        NK_MEMCPY(win->property.buffer, buffer, (nk_size)*len);
+        win->property.length = *len;
+        win->property.cursor = *cursor;
+        win->property.state = *state;
+        win->property.name = hash;
+        win->property.select_start = *select_begin;
+        win->property.select_end = *select_end;
+        if (*state == NK_PROPERTY_DRAG) {
+            ctx->input.mouse.grab = nk_true;
+            ctx->input.mouse.grabbed = nk_true;
+        }
+    }
+    /* check if previously active property is now inactive */
+    if (*state == NK_PROPERTY_DEFAULT && old_state != NK_PROPERTY_DEFAULT) {
+        if (old_state == NK_PROPERTY_DRAG) {
+            ctx->input.mouse.grab = nk_false;
+            ctx->input.mouse.grabbed = nk_false;
+            ctx->input.mouse.ungrab = nk_true;
+        }
+        win->property.select_start = 0;
+        win->property.select_end = 0;
+        win->property.active = 0;
+    }
+}
+
+NK_API void
+nk_property_int(struct nk_context *ctx, const char *name,
+    int min, int *val, int max, int step, float inc_per_pixel)
+{
+    struct nk_property_variant variant;
+    NK_ASSERT(ctx);
+    NK_ASSERT(name);
+    NK_ASSERT(val);
+
+    if (!ctx || !ctx->current || !name || !val) return;
+    variant = nk_property_variant_int(*val, min, max, step);
+    nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_INT);
+    *val = variant.value.i;
+}
+
+NK_API void
+nk_property_float(struct nk_context *ctx, const char *name,
+    float min, float *val, float max, float step, float inc_per_pixel)
+{
+    struct nk_property_variant variant;
+    NK_ASSERT(ctx);
+    NK_ASSERT(name);
+    NK_ASSERT(val);
+
+    if (!ctx || !ctx->current || !name || !val) return;
+    variant = nk_property_variant_float(*val, min, max, step);
+    nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT);
+    *val = variant.value.f;
+}
+
+NK_API void
+nk_property_double(struct nk_context *ctx, const char *name,
+    double min, double *val, double max, double step, float inc_per_pixel)
+{
+    struct nk_property_variant variant;
+    NK_ASSERT(ctx);
+    NK_ASSERT(name);
+    NK_ASSERT(val);
+
+    if (!ctx || !ctx->current || !name || !val) return;
+    variant = nk_property_variant_double(*val, min, max, step);
+    nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT);
+    *val = variant.value.d;
+}
+
+NK_API int
+nk_propertyi(struct nk_context *ctx, const char *name, int min, int val,
+    int max, int step, float inc_per_pixel)
+{
+    struct nk_property_variant variant;
+    NK_ASSERT(ctx);
+    NK_ASSERT(name);
+
+    if (!ctx || !ctx->current || !name) return val;
+    variant = nk_property_variant_int(val, min, max, step);
+    nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_INT);
+    val = variant.value.i;
+    return val;
+}
+
+NK_API float
+nk_propertyf(struct nk_context *ctx, const char *name, float min,
+    float val, float max, float step, float inc_per_pixel)
+{
+    struct nk_property_variant variant;
+    NK_ASSERT(ctx);
+    NK_ASSERT(name);
+
+    if (!ctx || !ctx->current || !name) return val;
+    variant = nk_property_variant_float(val, min, max, step);
+    nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT);
+    val = variant.value.f;
+    return val;
+}
+
+NK_API double
+nk_propertyd(struct nk_context *ctx, const char *name, double min,
+    double val, double max, double step, float inc_per_pixel)
+{
+    struct nk_property_variant variant;
+    NK_ASSERT(ctx);
+    NK_ASSERT(name);
+
+    if (!ctx || !ctx->current || !name) return val;
+    variant = nk_property_variant_double(val, min, max, step);
+    nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT);
+    val = variant.value.d;
+    return val;
+}
+
+/*----------------------------------------------------------------
+ *
+ *                          COLOR PICKER
+ *
+ * --------------------------------------------------------------*/
+NK_API int
+nk_color_pick(struct nk_context * ctx, struct nk_color *color,
+    enum nk_color_format fmt)
+{
+    struct nk_window *win;
+    struct nk_panel *layout;
+    const struct nk_style *config;
+    const struct nk_input *in;
+
+    enum nk_widget_layout_states state;
+    struct nk_rect bounds;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(color);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout || !color)
+        return 0;
+
+    win = ctx->current;
+    config = &ctx->style;
+    layout = win->layout;
+    state = nk_widget(&bounds, ctx);
+    if (!state) return 0;
+    in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    return nk_do_color_picker(&ctx->last_widget_state, &win->buffer, color, fmt, bounds,
+                nk_vec2(0,0), in, config->font);
+}
+
+NK_API struct nk_color
+nk_color_picker(struct nk_context *ctx, struct nk_color color,
+    enum nk_color_format fmt)
+{
+    nk_color_pick(ctx, &color, fmt);
+    return color;
+}
+
+/* -------------------------------------------------------------
+ *
+ *                          CHART
+ *
+ * --------------------------------------------------------------*/
+NK_API int
+nk_chart_begin_colored(struct nk_context *ctx, enum nk_chart_type type,
+    struct nk_color color, struct nk_color highlight,
+    int count, float min_value, float max_value)
+{
+    struct nk_window *win;
+    struct nk_chart *chart;
+    const struct nk_style *config;
+    const struct nk_style_chart *style;
+
+    const struct nk_style_item *background;
+    struct nk_rect bounds = {0, 0, 0, 0};
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+
+    if (!ctx || !ctx->current || !ctx->current->layout) return 0;
+    if (!nk_widget(&bounds, ctx)) {
+        chart = &ctx->current->layout->chart;
+        nk_zero(chart, sizeof(*chart));
+        return 0;
+    }
+
+    win = ctx->current;
+    config = &ctx->style;
+    chart = &win->layout->chart;
+    style = &config->chart;
+
+    /* setup basic generic chart  */
+    nk_zero(chart, sizeof(*chart));
+    chart->x = bounds.x + style->padding.x;
+    chart->y = bounds.y + style->padding.y;
+    chart->w = bounds.w - 2 * style->padding.x;
+    chart->h = bounds.h - 2 * style->padding.y;
+    chart->w = NK_MAX(chart->w, 2 * style->padding.x);
+    chart->h = NK_MAX(chart->h, 2 * style->padding.y);
+
+    /* add first slot into chart */
+    {struct nk_chart_slot *slot = &chart->slots[chart->slot++];
+    slot->type = type;
+    slot->count = count;
+    slot->color = color;
+    slot->highlight = highlight;
+    slot->min = NK_MIN(min_value, max_value);
+    slot->max = NK_MAX(min_value, max_value);
+    slot->range = slot->max - slot->min;}
+
+    /* draw chart background */
+    background = &style->background;
+    if (background->type == NK_STYLE_ITEM_IMAGE) {
+        nk_draw_image(&win->buffer, bounds, &background->data.image, nk_white);
+    } else {
+        nk_fill_rect(&win->buffer, bounds, style->rounding, style->border_color);
+        nk_fill_rect(&win->buffer, nk_shrink_rect(bounds, style->border),
+            style->rounding, style->background.data.color);
+    }
+    return 1;
+}
+
+NK_API int
+nk_chart_begin(struct nk_context *ctx, const enum nk_chart_type type,
+    int count, float min_value, float max_value)
+{return nk_chart_begin_colored(ctx, type, ctx->style.chart.color, ctx->style.chart.selected_color, count, min_value, max_value);}
+
+NK_API void
+nk_chart_add_slot_colored(struct nk_context *ctx, const enum nk_chart_type type,
+    struct nk_color color, struct nk_color highlight,
+    int count, float min_value, float max_value)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    NK_ASSERT(ctx->current->layout->chart.slot < NK_CHART_MAX_SLOT);
+    if (!ctx || !ctx->current || !ctx->current->layout) return;
+    if (ctx->current->layout->chart.slot >= NK_CHART_MAX_SLOT) return;
+
+    /* add another slot into the graph */
+    {struct nk_chart *chart = &ctx->current->layout->chart;
+    struct nk_chart_slot *slot = &chart->slots[chart->slot++];
+    slot->type = type;
+    slot->count = count;
+    slot->color = color;
+    slot->highlight = highlight;
+    slot->min = NK_MIN(min_value, max_value);
+    slot->max = NK_MAX(min_value, max_value);
+    slot->range = slot->max - slot->min;}
+}
+
+NK_API void
+nk_chart_add_slot(struct nk_context *ctx, const enum nk_chart_type type,
+    int count, float min_value, float max_value)
+{nk_chart_add_slot_colored(ctx, type, ctx->style.chart.color, ctx->style.chart.selected_color, count, min_value, max_value);}
+
+NK_INTERN nk_flags
+nk_chart_push_line(struct nk_context *ctx, struct nk_window *win,
+    struct nk_chart *g, float value, int slot)
+{
+    struct nk_panel *layout = win->layout;
+    const struct nk_input *i = &ctx->input;
+    struct nk_command_buffer *out = &win->buffer;
+
+    nk_flags ret = 0;
+    struct nk_vec2 cur;
+    struct nk_rect bounds;
+    struct nk_color color;
+    float step;
+    float range;
+    float ratio;
+
+    NK_ASSERT(slot >= 0 && slot < NK_CHART_MAX_SLOT);
+    step = g->w / (float)g->slots[slot].count;
+    range = g->slots[slot].max - g->slots[slot].min;
+    ratio = (value - g->slots[slot].min) / range;
+
+    if (g->slots[slot].index == 0) {
+        /* first data point does not have a connection */
+        g->slots[slot].last.x = g->x;
+        g->slots[slot].last.y = (g->y + g->h) - ratio * (float)g->h;
+
+        bounds.x = g->slots[slot].last.x - 2;
+        bounds.y = g->slots[slot].last.y - 2;
+        bounds.w = bounds.h = 4;
+
+        color = g->slots[slot].color;
+        if (!(layout->flags & NK_WINDOW_ROM) &&
+            NK_INBOX(i->mouse.pos.x,i->mouse.pos.y, g->slots[slot].last.x-3, g->slots[slot].last.y-3, 6, 6)){
+            ret = nk_input_is_mouse_hovering_rect(i, bounds) ? NK_CHART_HOVERING : 0;
+            ret |= (i->mouse.buttons[NK_BUTTON_LEFT].down &&
+                i->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0;
+            color = g->slots[slot].highlight;
+        }
+        nk_fill_rect(out, bounds, 0, color);
+        g->slots[slot].index += 1;
+        return ret;
+    }
+
+    /* draw a line between the last data point and the new one */
+    color = g->slots[slot].color;
+    cur.x = g->x + (float)(step * (float)g->slots[slot].index);
+    cur.y = (g->y + g->h) - (ratio * (float)g->h);
+    nk_stroke_line(out, g->slots[slot].last.x, g->slots[slot].last.y, cur.x, cur.y, 1.0f, color);
+
+    bounds.x = cur.x - 3;
+    bounds.y = cur.y - 3;
+    bounds.w = bounds.h = 6;
+
+    /* user selection of current data point */
+    if (!(layout->flags & NK_WINDOW_ROM)) {
+        if (nk_input_is_mouse_hovering_rect(i, bounds)) {
+            ret = NK_CHART_HOVERING;
+            ret |= (!i->mouse.buttons[NK_BUTTON_LEFT].down &&
+                i->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0;
+            color = g->slots[slot].highlight;
+        }
+    }
+    nk_fill_rect(out, nk_rect(cur.x - 2, cur.y - 2, 4, 4), 0, color);
+
+    /* save current data point position */
+    g->slots[slot].last.x = cur.x;
+    g->slots[slot].last.y = cur.y;
+    g->slots[slot].index  += 1;
+    return ret;
+}
+
+NK_INTERN nk_flags
+nk_chart_push_column(const struct nk_context *ctx, struct nk_window *win,
+    struct nk_chart *chart, float value, int slot)
+{
+    struct nk_command_buffer *out = &win->buffer;
+    const struct nk_input *in = &ctx->input;
+    struct nk_panel *layout = win->layout;
+
+    float ratio;
+    nk_flags ret = 0;
+    struct nk_color color;
+    struct nk_rect item = {0,0,0,0};
+
+    NK_ASSERT(slot >= 0 && slot < NK_CHART_MAX_SLOT);
+    if (chart->slots[slot].index  >= chart->slots[slot].count)
+        return nk_false;
+    if (chart->slots[slot].count) {
+        float padding = (float)(chart->slots[slot].count-1);
+        item.w = (chart->w - padding) / (float)(chart->slots[slot].count);
+    }
+
+    /* calculate bounds of current bar chart entry */
+    color = chart->slots[slot].color;;
+    item.h = chart->h * NK_ABS((value/chart->slots[slot].range));
+    if (value >= 0) {
+        ratio = (value + NK_ABS(chart->slots[slot].min)) / NK_ABS(chart->slots[slot].range);
+        item.y = (chart->y + chart->h) - chart->h * ratio;
+    } else {
+        ratio = (value - chart->slots[slot].max) / chart->slots[slot].range;
+        item.y = chart->y + (chart->h * NK_ABS(ratio)) - item.h;
+    }
+    item.x = chart->x + ((float)chart->slots[slot].index * item.w);
+    item.x = item.x + ((float)chart->slots[slot].index);
+
+    /* user chart bar selection */
+    if (!(layout->flags & NK_WINDOW_ROM) &&
+        NK_INBOX(in->mouse.pos.x,in->mouse.pos.y,item.x,item.y,item.w,item.h)) {
+        ret = NK_CHART_HOVERING;
+        ret |= (!in->mouse.buttons[NK_BUTTON_LEFT].down &&
+                in->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0;
+        color = chart->slots[slot].highlight;
+    }
+    nk_fill_rect(out, item, 0, color);
+    chart->slots[slot].index += 1;
+    return ret;
+}
+
+NK_API nk_flags
+nk_chart_push_slot(struct nk_context *ctx, float value, int slot)
+{
+    nk_flags flags;
+    struct nk_window *win;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(slot >= 0 && slot < NK_CHART_MAX_SLOT);
+    NK_ASSERT(slot < ctx->current->layout->chart.slot);
+    if (!ctx || !ctx->current || slot >= NK_CHART_MAX_SLOT) return nk_false;
+    if (slot >= ctx->current->layout->chart.slot) return nk_false;
+
+    win = ctx->current;
+    if (win->layout->chart.slot < slot) return nk_false;
+    switch (win->layout->chart.slots[slot].type) {
+    case NK_CHART_LINES:
+        flags = nk_chart_push_line(ctx, win, &win->layout->chart, value, slot); break;
+    case NK_CHART_COLUMN:
+        flags = nk_chart_push_column(ctx, win, &win->layout->chart, value, slot); break;
+    default:
+    case NK_CHART_MAX:
+        flags = 0;
+    }
+    return flags;
+}
+
+NK_API nk_flags
+nk_chart_push(struct nk_context *ctx, float value)
+{return nk_chart_push_slot(ctx, value, 0);}
+
+NK_API void
+nk_chart_end(struct nk_context *ctx)
+{
+    struct nk_window *win;
+    struct nk_chart *chart;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current)
+        return;
+
+    win = ctx->current;
+    chart = &win->layout->chart;
+    NK_MEMSET(chart, 0, sizeof(*chart));
+    return;
+}
+
+NK_API void
+nk_plot(struct nk_context *ctx, enum nk_chart_type type, const float *values,
+    int count, int offset)
+{
+    int i = 0;
+    float min_value;
+    float max_value;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(values);
+    if (!ctx || !values || !count) return;
+
+    min_value = values[offset];
+    max_value = values[offset];
+    for (i = 0; i < count; ++i) {
+        min_value = NK_MIN(values[i + offset], min_value);
+        max_value = NK_MAX(values[i + offset], max_value);
+    }
+
+    if (nk_chart_begin(ctx, type, count, min_value, max_value)) {
+        for (i = 0; i < count; ++i)
+            nk_chart_push(ctx, values[i + offset]);
+        nk_chart_end(ctx);
+    }
+}
+
+NK_API void
+nk_plot_function(struct nk_context *ctx, enum nk_chart_type type, void *userdata,
+    float(*value_getter)(void* user, int index), int count, int offset)
+{
+    int i = 0;
+    float min_value;
+    float max_value;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(value_getter);
+    if (!ctx || !value_getter || !count) return;
+
+    max_value = min_value = value_getter(userdata, offset);
+    for (i = 0; i < count; ++i) {
+        float value = value_getter(userdata, i + offset);
+        min_value = NK_MIN(value, min_value);
+        max_value = NK_MAX(value, max_value);
+    }
+
+    if (nk_chart_begin(ctx, type, count, min_value, max_value)) {
+        for (i = 0; i < count; ++i)
+            nk_chart_push(ctx, value_getter(userdata, i + offset));
+        nk_chart_end(ctx);
+    }
+}
+
+/* -------------------------------------------------------------
+ *
+ *                          GROUP
+ *
+ * --------------------------------------------------------------*/
+NK_API int
+nk_group_scrolled_offset_begin(struct nk_context *ctx,
+    nk_uint *x_offset, nk_uint *y_offset, const char *title, nk_flags flags)
+{
+    struct nk_rect bounds;
+    struct nk_window panel;
+    struct nk_window *win;
+
+    win = ctx->current;
+    nk_panel_alloc_space(&bounds, ctx);
+    {const struct nk_rect *c = &win->layout->clip;
+    if (!NK_INTERSECT(c->x, c->y, c->w, c->h, bounds.x, bounds.y, bounds.w, bounds.h) &&
+        !(flags & NK_WINDOW_MOVABLE)) {
+        return 0;
+    }}
+    if (win->flags & NK_WINDOW_ROM)
+        flags |= NK_WINDOW_ROM;
+
+    /* initialize a fake window to create the panel from */
+    nk_zero(&panel, sizeof(panel));
+    panel.bounds = bounds;
+    panel.flags = flags;
+    panel.scrollbar.x = *x_offset;
+    panel.scrollbar.y = *y_offset;
+    panel.buffer = win->buffer;
+    panel.layout = (struct nk_panel*)nk_create_panel(ctx);
+    ctx->current = &panel;
+    nk_panel_begin(ctx, (flags & NK_WINDOW_TITLE) ? title: 0, NK_PANEL_GROUP);
+
+    win->buffer = panel.buffer;
+    win->buffer.clip = panel.layout->clip;
+    panel.layout->offset_x = x_offset;
+    panel.layout->offset_y = y_offset;
+    panel.layout->parent = win->layout;
+    win->layout = panel.layout;
+
+    ctx->current = win;
+    if ((panel.layout->flags & NK_WINDOW_CLOSED) ||
+        (panel.layout->flags & NK_WINDOW_MINIMIZED))
+    {
+        nk_flags f = panel.layout->flags;
+        nk_group_scrolled_end(ctx);
+        if (f & NK_WINDOW_CLOSED)
+            return NK_WINDOW_CLOSED;
+        if (f & NK_WINDOW_MINIMIZED)
+            return NK_WINDOW_MINIMIZED;
+    }
+    return 1;
+}
+
+NK_API void
+nk_group_scrolled_end(struct nk_context *ctx)
+{
+    struct nk_window *win;
+    struct nk_panel *parent;
+    struct nk_panel *g;
+
+    struct nk_rect clip;
+    struct nk_window pan;
+    struct nk_vec2 panel_padding;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current)
+        return;
+
+    /* make sure nk_group_begin was called correctly */
+    NK_ASSERT(ctx->current);
+    win = ctx->current;
+    NK_ASSERT(win->layout);
+    g = win->layout;
+    NK_ASSERT(g->parent);
+    parent = g->parent;
+
+    /* dummy window */
+    nk_zero_struct(pan);
+    panel_padding = nk_panel_get_padding(&ctx->style, NK_PANEL_GROUP);
+    pan.bounds.y = g->bounds.y - (g->header_height + g->menu.h);
+    pan.bounds.x = g->bounds.x - panel_padding.x;
+    pan.bounds.w = g->bounds.w + 2 * panel_padding.x;
+    pan.bounds.h = g->bounds.h + g->header_height + g->menu.h;
+    if (g->flags & NK_WINDOW_BORDER) {
+        pan.bounds.x -= g->border;
+        pan.bounds.y -= g->border;
+        pan.bounds.w += 2*g->border;
+        pan.bounds.h += 2*g->border;
+    }
+    if (!(g->flags & NK_WINDOW_NO_SCROLLBAR)) {
+        pan.bounds.w += ctx->style.window.scrollbar_size.x;
+        pan.bounds.h += ctx->style.window.scrollbar_size.y;
+    }
+    pan.scrollbar.x = *g->offset_x;
+    pan.scrollbar.y = *g->offset_y;
+    pan.flags = g->flags;
+    pan.buffer = win->buffer;
+    pan.layout = g;
+    pan.parent = win;
+    ctx->current = &pan;
+
+    /* make sure group has correct clipping rectangle */
+    nk_unify(&clip, &parent->clip, pan.bounds.x, pan.bounds.y,
+        pan.bounds.x + pan.bounds.w, pan.bounds.y + pan.bounds.h + panel_padding.x);
+    nk_push_scissor(&pan.buffer, clip);
+    nk_end(ctx);
+
+    win->buffer = pan.buffer;
+    nk_push_scissor(&win->buffer, parent->clip);
+    ctx->current = win;
+    win->layout = parent;
+    g->bounds = pan.bounds;
+    return;
+}
+
+NK_API int
+nk_group_scrolled_begin(struct nk_context *ctx,
+    struct nk_scroll *scroll, const char *title, nk_flags flags)
+{return nk_group_scrolled_offset_begin(ctx, &scroll->x, &scroll->y, title, flags);}
+
+NK_API int
+nk_group_begin(struct nk_context *ctx, const char *title, nk_flags flags)
+{
+    int title_len;
+    nk_hash title_hash;
+    struct nk_window *win;
+    nk_uint *x_offset;
+    nk_uint *y_offset;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(title);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout || !title)
+        return 0;
+
+    /* find persistent group scrollbar value */
+    win = ctx->current;
+    title_len = (int)nk_strlen(title);
+    title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_GROUP);
+    x_offset = nk_find_value(win, title_hash);
+    if (!x_offset) {
+        x_offset = nk_add_value(ctx, win, title_hash, 0);
+        y_offset = nk_add_value(ctx, win, title_hash+1, 0);
+
+        NK_ASSERT(x_offset);
+        NK_ASSERT(y_offset);
+        if (!x_offset || !y_offset) return 0;
+        *x_offset = *y_offset = 0;
+    } else y_offset = nk_find_value(win, title_hash+1);
+    return nk_group_scrolled_offset_begin(ctx, x_offset, y_offset, title, flags);
+}
+
+NK_API void
+nk_group_end(struct nk_context *ctx)
+{nk_group_scrolled_end(ctx);}
+
+NK_API int
+nk_list_view_begin(struct nk_context *ctx, struct nk_list_view *view,
+    const char *title, nk_flags flags, int row_height, int row_count)
+{
+    int title_len;
+    nk_hash title_hash;
+    nk_uint *x_offset;
+    nk_uint *y_offset;
+
+    int result;
+    struct nk_window *win;
+    struct nk_panel *layout;
+    const struct nk_style *style;
+    struct nk_vec2 item_spacing;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(view);
+    NK_ASSERT(title);
+    if (!ctx || !view || !title) return 0;
+
+    win = ctx->current;
+    style = &ctx->style;
+    item_spacing = style->window.spacing;
+    row_height += NK_MAX(0, (int)item_spacing.y);
+
+    /* find persistent list view scrollbar offset */
+    title_len = (int)nk_strlen(title);
+    title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_GROUP);
+    x_offset = nk_find_value(win, title_hash);
+    if (!x_offset) {
+        x_offset = nk_add_value(ctx, win, title_hash, 0);
+        y_offset = nk_add_value(ctx, win, title_hash+1, 0);
+
+        NK_ASSERT(x_offset);
+        NK_ASSERT(y_offset);
+        if (!x_offset || !y_offset) return 0;
+        *x_offset = *y_offset = 0;
+    } else y_offset = nk_find_value(win, title_hash+1);
+    view->scroll_value = *y_offset;
+    view->scroll_pointer = y_offset;
+
+    *y_offset = 0;
+    result = nk_group_scrolled_offset_begin(ctx, x_offset, y_offset, title, flags);
+    win = ctx->current;
+    layout = win->layout;
+
+    view->total_height = row_height * NK_MAX(row_count,1);
+    view->begin = (int)NK_MAX(((float)view->scroll_value / (float)row_height), 0.0f);
+    view->count = (int)NK_MAX(nk_iceilf((layout->clip.h)/(float)row_height), 0);
+    view->end = view->begin + view->count;
+    view->ctx = ctx;
+    return result;
+}
+
+NK_API void
+nk_list_view_end(struct nk_list_view *view)
+{
+    struct nk_context *ctx;
+    struct nk_window *win;
+    struct nk_panel *layout;
+
+    NK_ASSERT(view);
+    NK_ASSERT(view->ctx);
+    NK_ASSERT(view->scroll_pointer);
+    if (!view || !view->ctx) return;
+
+    ctx = view->ctx;
+    win = ctx->current;
+    layout = win->layout;
+    layout->at_y = layout->bounds.y + (float)view->total_height;
+    *view->scroll_pointer = *view->scroll_pointer + view->scroll_value;
+    nk_group_end(view->ctx);
+}
+
+/* --------------------------------------------------------------
+ *
+ *                          POPUP
+ *
+ * --------------------------------------------------------------*/
+NK_API int
+nk_popup_begin(struct nk_context *ctx, enum nk_popup_type type,
+    const char *title, nk_flags flags, struct nk_rect rect)
+{
+    struct nk_window *popup;
+    struct nk_window *win;
+    struct nk_panel *panel;
+
+    int title_len;
+    nk_hash title_hash;
+    nk_size allocated;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(title);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    panel = win->layout;
+    NK_ASSERT(!(panel->type & NK_PANEL_SET_POPUP) && "popups are not allowed to have popups");
+    (void)panel;
+    title_len = (int)nk_strlen(title);
+    title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_POPUP);
+
+    popup = win->popup.win;
+    if (!popup) {
+        popup = (struct nk_window*)nk_create_window(ctx);
+        popup->parent = win;
+        win->popup.win = popup;
+        win->popup.active = 0;
+        win->popup.type = NK_PANEL_POPUP;
+    }
+
+    /* make sure we have correct popup */
+    if (win->popup.name != title_hash) {
+        if (!win->popup.active) {
+            nk_zero(popup, sizeof(*popup));
+            win->popup.name = title_hash;
+            win->popup.active = 1;
+            win->popup.type = NK_PANEL_POPUP;
+        } else return 0;
+    }
+
+    /* popup position is local to window */
+    ctx->current = popup;
+    rect.x += win->layout->clip.x;
+    rect.y += win->layout->clip.y;
+
+    /* setup popup data */
+    popup->parent = win;
+    popup->bounds = rect;
+    popup->seq = ctx->seq;
+    popup->layout = (struct nk_panel*)nk_create_panel(ctx);
+    popup->flags = flags;
+    popup->flags |= NK_WINDOW_BORDER;
+    if (type == NK_POPUP_DYNAMIC)
+        popup->flags |= NK_WINDOW_DYNAMIC;
+
+    popup->buffer = win->buffer;
+    nk_start_popup(ctx, win);
+    allocated = ctx->memory.allocated;
+    nk_push_scissor(&popup->buffer, nk_null_rect);
+
+    if (nk_panel_begin(ctx, title, NK_PANEL_POPUP)) {
+        /* popup is running therefore invalidate parent panels */
+        struct nk_panel *root;
+        root = win->layout;
+        while (root) {
+            root->flags |= NK_WINDOW_ROM;
+            root->flags &= ~(nk_flags)NK_WINDOW_REMOVE_ROM;
+            root = root->parent;
+        }
+        win->popup.active = 1;
+        popup->layout->offset_x = &popup->scrollbar.x;
+        popup->layout->offset_y = &popup->scrollbar.y;
+        popup->layout->parent = win->layout;
+        return 1;
+    } else {
+        /* popup was closed/is invalid so cleanup */
+        struct nk_panel *root;
+        root = win->layout;
+        while (root) {
+            root->flags |= NK_WINDOW_REMOVE_ROM;
+            root = root->parent;
+        }
+        win->popup.buf.active = 0;
+        win->popup.active = 0;
+        ctx->memory.allocated = allocated;
+        ctx->current = win;
+        nk_free_panel(ctx, popup->layout);
+        popup->layout = 0;
+        return 0;
+    }
+}
+
+NK_INTERN int
+nk_nonblock_begin(struct nk_context *ctx,
+    nk_flags flags, struct nk_rect body, struct nk_rect header,
+    enum nk_panel_type panel_type)
+{
+    struct nk_window *popup;
+    struct nk_window *win;
+    struct nk_panel *panel;
+    int is_active = nk_true;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    /* popups cannot have popups */
+    win = ctx->current;
+    panel = win->layout;
+    NK_ASSERT(!(panel->type & NK_PANEL_SET_POPUP));
+    (void)panel;
+    popup = win->popup.win;
+    if (!popup) {
+        /* create window for nonblocking popup */
+        popup = (struct nk_window*)nk_create_window(ctx);
+        popup->parent = win;
+        win->popup.win = popup;
+        win->popup.type = panel_type;
+        nk_command_buffer_init(&popup->buffer, &ctx->memory, NK_CLIPPING_ON);
+    } else {
+        /* close the popup if user pressed outside or in the header */
+        int pressed, in_body, in_header;
+        pressed = nk_input_is_mouse_pressed(&ctx->input, NK_BUTTON_LEFT);
+        in_body = nk_input_is_mouse_hovering_rect(&ctx->input, body);
+        in_header = nk_input_is_mouse_hovering_rect(&ctx->input, header);
+        if (pressed && (!in_body || in_header))
+            is_active = nk_false;
+    }
+    win->popup.header = header;
+
+    if (!is_active) {
+        /* remove read only mode from all parent panels */
+        struct nk_panel *root = win->layout;
+        while (root) {
+            root->flags |= NK_WINDOW_REMOVE_ROM;
+            root = root->parent;
+        }
+        return is_active;
+    }
+
+    popup->bounds = body;
+    popup->parent = win;
+    popup->layout = (struct nk_panel*)nk_create_panel(ctx);
+    popup->flags = flags;
+    popup->flags |= NK_WINDOW_BORDER;
+    popup->flags |= NK_WINDOW_DYNAMIC;
+    popup->seq = ctx->seq;
+    win->popup.active = 1;
+    NK_ASSERT(popup->layout);
+
+    nk_start_popup(ctx, win);
+    popup->buffer = win->buffer;
+    nk_push_scissor(&popup->buffer, nk_null_rect);
+    ctx->current = popup;
+
+    nk_panel_begin(ctx, 0, panel_type);
+    win->buffer = popup->buffer;
+    popup->layout->parent = win->layout;
+    popup->layout->offset_x = &popup->scrollbar.x;
+    popup->layout->offset_y = &popup->scrollbar.y;
+
+    /* set read only mode to all parent panels */
+    {struct nk_panel *root;
+    root = win->layout;
+    while (root) {
+        root->flags |= NK_WINDOW_ROM;
+        root = root->parent;
+    }}
+    return is_active;
+}
+
+NK_API void
+nk_popup_close(struct nk_context *ctx)
+{
+    struct nk_window *popup;
+    NK_ASSERT(ctx);
+    if (!ctx || !ctx->current) return;
+
+    popup = ctx->current;
+    NK_ASSERT(popup->parent);
+    NK_ASSERT(popup->layout->type & NK_PANEL_SET_POPUP);
+    popup->flags |= NK_WINDOW_HIDDEN;
+}
+
+NK_API void
+nk_popup_end(struct nk_context *ctx)
+{
+    struct nk_window *win;
+    struct nk_window *popup;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return;
+
+    popup = ctx->current;
+    if (!popup->parent) return;
+    win = popup->parent;
+    if (popup->flags & NK_WINDOW_HIDDEN) {
+        struct nk_panel *root;
+        root = win->layout;
+        while (root) {
+            root->flags |= NK_WINDOW_REMOVE_ROM;
+            root = root->parent;
+        }
+        win->popup.active = 0;
+    }
+    nk_push_scissor(&popup->buffer, nk_null_rect);
+    nk_end(ctx);
+
+    win->buffer = popup->buffer;
+    nk_finish_popup(ctx, win);
+    ctx->current = win;
+    nk_push_scissor(&win->buffer, win->layout->clip);
+}
+/* -------------------------------------------------------------
+ *
+ *                          TOOLTIP
+ *
+ * -------------------------------------------------------------- */
+NK_API int
+nk_tooltip_begin(struct nk_context *ctx, float width)
+{
+    struct nk_window *win;
+    const struct nk_input *in;
+    struct nk_rect bounds;
+    int ret;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    /* make sure that no nonblocking popup is currently active */
+    win = ctx->current;
+    in = &ctx->input;
+    if (win->popup.win && (win->popup.type & NK_PANEL_SET_NONBLOCK))
+        return 0;
+
+    bounds.w = width;
+    bounds.h = nk_null_rect.h;
+    bounds.x = (in->mouse.pos.x + 1) - win->layout->clip.x;
+    bounds.y = (in->mouse.pos.y + 1) - win->layout->clip.y;
+
+    ret = nk_popup_begin(ctx, NK_POPUP_DYNAMIC,
+        "__##Tooltip##__", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER, bounds);
+    if (ret) win->layout->flags &= ~(nk_flags)NK_WINDOW_ROM;
+    win->popup.type = NK_PANEL_TOOLTIP;
+    ctx->current->layout->type = NK_PANEL_TOOLTIP;
+    return ret;
+}
+
+NK_API void
+nk_tooltip_end(struct nk_context *ctx)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current) return;
+    ctx->current->seq--;
+    nk_popup_close(ctx);
+    nk_popup_end(ctx);
+}
+
+NK_API void
+nk_tooltip(struct nk_context *ctx, const char *text)
+{
+    const struct nk_style *style;
+    struct nk_vec2 padding;
+
+    int text_len;
+    float text_width;
+    float text_height;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    NK_ASSERT(text);
+    if (!ctx || !ctx->current || !ctx->current->layout || !text)
+        return;
+
+    /* fetch configuration data */
+    style = &ctx->style;
+    padding = style->window.padding;
+
+    /* calculate size of the text and tooltip */
+    text_len = nk_strlen(text);
+    text_width = style->font->width(style->font->userdata,
+                    style->font->height, text, text_len);
+    text_width += (4 * padding.x);
+    text_height = (style->font->height + 2 * padding.y);
+
+    /* execute tooltip and fill with text */
+    if (nk_tooltip_begin(ctx, (float)text_width)) {
+        nk_layout_row_dynamic(ctx, (float)text_height, 1);
+        nk_text(ctx, text, text_len, NK_TEXT_LEFT);
+        nk_tooltip_end(ctx);
+    }
+}
+/* -------------------------------------------------------------
+ *
+ *                          CONTEXTUAL
+ *
+ * -------------------------------------------------------------- */
+NK_API int
+nk_contextual_begin(struct nk_context *ctx, nk_flags flags, struct nk_vec2 size,
+    struct nk_rect trigger_bounds)
+{
+    struct nk_window *win;
+    struct nk_window *popup;
+    struct nk_rect body;
+
+    NK_STORAGE const struct nk_rect null_rect = {0,0,0,0};
+    int is_clicked = 0;
+    int is_active = 0;
+    int is_open = 0;
+    int ret = 0;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    ++win->popup.con_count;
+
+    /* check if currently active contextual is active */
+    popup = win->popup.win;
+    is_open = (popup && win->popup.type == NK_PANEL_CONTEXTUAL);
+    is_clicked = nk_input_mouse_clicked(&ctx->input, NK_BUTTON_RIGHT, trigger_bounds);
+    if (win->popup.active_con && win->popup.con_count != win->popup.active_con)
+        return 0;
+    if ((is_clicked && is_open && !is_active) || (!is_open && !is_active && !is_clicked))
+        return 0;
+
+    /* calculate contextual position on click */
+    win->popup.active_con = win->popup.con_count;
+    if (is_clicked) {
+        body.x = ctx->input.mouse.pos.x;
+        body.y = ctx->input.mouse.pos.y;
+    } else {
+        body.x = popup->bounds.x;
+        body.y = popup->bounds.y;
+    }
+    body.w = size.x;
+    body.h = size.y;
+
+    /* start nonblocking contextual popup */
+    ret = nk_nonblock_begin(ctx, flags|NK_WINDOW_NO_SCROLLBAR, body,
+            null_rect, NK_PANEL_CONTEXTUAL);
+    if (ret) win->popup.type = NK_PANEL_CONTEXTUAL;
+    else {
+        win->popup.active_con = 0;
+        if (win->popup.win)
+            win->popup.win->flags = 0;
+    }
+    return ret;
+}
+
+NK_API int
+nk_contextual_item_text(struct nk_context *ctx, const char *text, int len,
+    nk_flags alignment)
+{
+    struct nk_window *win;
+    const struct nk_input *in;
+    const struct nk_style *style;
+
+    struct nk_rect bounds;
+    enum nk_widget_layout_states state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    style = &ctx->style;
+    state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding);
+    if (!state) return nk_false;
+
+    in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, bounds,
+        text, len, alignment, NK_BUTTON_DEFAULT, &style->contextual_button, in, style->font)) {
+        nk_contextual_close(ctx);
+        return nk_true;
+    }
+    return nk_false;
+}
+
+NK_API int nk_contextual_item_label(struct nk_context *ctx, const char *label, nk_flags align)
+{return nk_contextual_item_text(ctx, label, nk_strlen(label), align);}
+
+NK_API int
+nk_contextual_item_image_text(struct nk_context *ctx, struct nk_image img,
+    const char *text, int len, nk_flags align)
+{
+    struct nk_window *win;
+    const struct nk_input *in;
+    const struct nk_style *style;
+
+    struct nk_rect bounds;
+    enum nk_widget_layout_states state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    style = &ctx->style;
+    state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding);
+    if (!state) return nk_false;
+
+    in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    if (nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, bounds,
+        img, text, len, align, NK_BUTTON_DEFAULT, &style->contextual_button, style->font, in)){
+        nk_contextual_close(ctx);
+        return nk_true;
+    }
+    return nk_false;
+}
+
+NK_API int nk_contextual_item_image_label(struct nk_context *ctx, struct nk_image img,
+    const char *label, nk_flags align)
+{return nk_contextual_item_image_text(ctx, img, label, nk_strlen(label), align);}
+
+NK_API int
+nk_contextual_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type symbol,
+    const char *text, int len, nk_flags align)
+{
+    struct nk_window *win;
+    const struct nk_input *in;
+    const struct nk_style *style;
+
+    struct nk_rect bounds;
+    enum nk_widget_layout_states state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    style = &ctx->style;
+    state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding);
+    if (!state) return nk_false;
+
+    in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    if (nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, bounds,
+        symbol, text, len, align, NK_BUTTON_DEFAULT, &style->contextual_button, style->font, in)) {
+        nk_contextual_close(ctx);
+        return nk_true;
+    }
+    return nk_false;
+}
+
+NK_API int nk_contextual_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type symbol,
+    const char *text, nk_flags align)
+{return nk_contextual_item_symbol_text(ctx, symbol, text, nk_strlen(text), align);}
+
+NK_API void
+nk_contextual_close(struct nk_context *ctx)
+{
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout) return;
+    nk_popup_close(ctx);
+}
+
+NK_API void
+nk_contextual_end(struct nk_context *ctx)
+{
+    struct nk_window *popup;
+    struct nk_panel *panel;
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !ctx->current) return;
+
+    popup = ctx->current;
+    panel = popup->layout;
+    NK_ASSERT(popup->parent);
+    NK_ASSERT(panel->type & NK_PANEL_SET_POPUP);
+    if (panel->flags & NK_WINDOW_DYNAMIC) {
+        /* Close behavior
+        This is a bit of a hack solution since we do not know before we end our popup
+        how big it will be. We therefore do not directly know when a
+        click outside the non-blocking popup must close it at that direct frame.
+        Instead it will be closed in the next frame.*/
+        struct nk_rect body = {0,0,0,0};
+        if (panel->at_y < (panel->bounds.y + panel->bounds.h)) {
+            struct nk_vec2 padding = nk_panel_get_padding(&ctx->style, panel->type);
+            body = panel->bounds;
+            body.y = (panel->at_y + panel->footer_height + panel->border + padding.y + panel->row.height);
+            body.h = (panel->bounds.y + panel->bounds.h) - body.y;
+        }
+        {int pressed = nk_input_is_mouse_pressed(&ctx->input, NK_BUTTON_LEFT);
+        int in_body = nk_input_is_mouse_hovering_rect(&ctx->input, body);
+        if (pressed && in_body)
+            popup->flags |= NK_WINDOW_HIDDEN;
+        }
+    }
+    if (popup->flags & NK_WINDOW_HIDDEN)
+        popup->seq = 0;
+    nk_popup_end(ctx);
+    return;
+}
+/* -------------------------------------------------------------
+ *
+ *                          COMBO
+ *
+ * --------------------------------------------------------------*/
+NK_INTERN int
+nk_combo_begin(struct nk_context *ctx, struct nk_window *win,
+    struct nk_vec2 size, int is_clicked, struct nk_rect header)
+{
+    struct nk_window *popup;
+    int is_open = 0;
+    int is_active = 0;
+    struct nk_rect body;
+    nk_hash hash;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    popup = win->popup.win;
+    body.x = header.x;
+    body.w = size.x;
+    body.y = header.y + header.h-ctx->style.window.combo_border;
+    body.h = size.y;
+
+    hash = win->popup.combo_count++;
+    is_open = (popup) ? nk_true:nk_false;
+    is_active = (popup && (win->popup.name == hash) && win->popup.type == NK_PANEL_COMBO);
+    if ((is_clicked && is_open && !is_active) || (is_open && !is_active) ||
+        (!is_open && !is_active && !is_clicked)) return 0;
+    if (!nk_nonblock_begin(ctx, 0, body,
+        (is_clicked && is_open)?nk_rect(0,0,0,0):header, NK_PANEL_COMBO)) return 0;
+
+    win->popup.type = NK_PANEL_COMBO;
+    win->popup.name = hash;
+    return 1;
+}
+
+NK_API int
+nk_combo_begin_text(struct nk_context *ctx, const char *selected, int len,
+    struct nk_vec2 size)
+{
+    const struct nk_input *in;
+    struct nk_window *win;
+    struct nk_style *style;
+
+    enum nk_widget_layout_states s;
+    int is_clicked = nk_false;
+    struct nk_rect header;
+    const struct nk_style_item *background;
+    struct nk_text text;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(selected);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout || !selected)
+        return 0;
+
+    win = ctx->current;
+    style = &ctx->style;
+    s = nk_widget(&header, ctx);
+    if (s == NK_WIDGET_INVALID)
+        return 0;
+
+    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input;
+    if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT))
+        is_clicked = nk_true;
+
+    /* draw combo box header background and border */
+    if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) {
+        background = &style->combo.active;
+        text.text = style->combo.label_active;
+    } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) {
+        background = &style->combo.hover;
+        text.text = style->combo.label_hover;
+    } else {
+        background = &style->combo.normal;
+        text.text = style->combo.label_normal;
+    }
+    if (background->type == NK_STYLE_ITEM_IMAGE) {
+        text.background = nk_rgba(0,0,0,0);
+        nk_draw_image(&win->buffer, header, &background->data.image, nk_white);
+    } else {
+        text.background = background->data.color;
+        nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color);
+        nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color);
+    }
+    {
+        /* print currently selected text item */
+        struct nk_rect label;
+        struct nk_rect button;
+        struct nk_rect content;
+
+        enum nk_symbol_type sym;
+        if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER)
+            sym = style->combo.sym_hover;
+        else if (is_clicked)
+            sym = style->combo.sym_active;
+        else sym = style->combo.sym_normal;
+
+        /* calculate button */
+        button.w = header.h - 2 * style->combo.button_padding.y;
+        button.x = (header.x + header.w - header.h) - style->combo.button_padding.x;
+        button.y = header.y + style->combo.button_padding.y;
+        button.h = button.w;
+
+        content.x = button.x + style->combo.button.padding.x;
+        content.y = button.y + style->combo.button.padding.y;
+        content.w = button.w - 2 * style->combo.button.padding.x;
+        content.h = button.h - 2 * style->combo.button.padding.y;
+
+        /* draw selected label */
+        text.padding = nk_vec2(0,0);
+        label.x = header.x + style->combo.content_padding.x;
+        label.y = header.y + style->combo.content_padding.y;
+        label.w = button.x - (style->combo.content_padding.x + style->combo.spacing.x) - label.x;;
+        label.h = header.h - 2 * style->combo.content_padding.y;
+        nk_widget_text(&win->buffer, label, selected, len, &text,
+            NK_TEXT_LEFT, ctx->style.font);
+
+        /* draw open/close button */
+        nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state,
+            &ctx->style.combo.button, sym, style->font);
+    }
+    return nk_combo_begin(ctx, win, size, is_clicked, header);
+}
+
+NK_API int nk_combo_begin_label(struct nk_context *ctx, const char *selected, struct nk_vec2 size)
+{return nk_combo_begin_text(ctx, selected, nk_strlen(selected), size);}
+
+NK_API int
+nk_combo_begin_color(struct nk_context *ctx, struct nk_color color, struct nk_vec2 size)
+{
+    struct nk_window *win;
+    struct nk_style *style;
+    const struct nk_input *in;
+
+    struct nk_rect header;
+    int is_clicked = nk_false;
+    enum nk_widget_layout_states s;
+    const struct nk_style_item *background;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    style = &ctx->style;
+    s = nk_widget(&header, ctx);
+    if (s == NK_WIDGET_INVALID)
+        return 0;
+
+    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input;
+    if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT))
+        is_clicked = nk_true;
+
+    /* draw combo box header background and border */
+    if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED)
+        background = &style->combo.active;
+    else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER)
+        background = &style->combo.hover;
+    else background = &style->combo.normal;
+
+    if (background->type == NK_STYLE_ITEM_IMAGE) {
+        nk_draw_image(&win->buffer, header, &background->data.image,nk_white);
+    } else {
+        nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color);
+        nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color);
+    }
+    {
+        struct nk_rect content;
+        struct nk_rect button;
+        struct nk_rect bounds;
+
+        enum nk_symbol_type sym;
+        if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER)
+            sym = style->combo.sym_hover;
+        else if (is_clicked)
+            sym = style->combo.sym_active;
+        else sym = style->combo.sym_normal;
+
+        /* calculate button */
+        button.w = header.h - 2 * style->combo.button_padding.y;
+        button.x = (header.x + header.w - header.h) - style->combo.button_padding.x;
+        button.y = header.y + style->combo.button_padding.y;
+        button.h = button.w;
+
+        content.x = button.x + style->combo.button.padding.x;
+        content.y = button.y + style->combo.button.padding.y;
+        content.w = button.w - 2 * style->combo.button.padding.x;
+        content.h = button.h - 2 * style->combo.button.padding.y;
+
+        /* draw color */
+        bounds.h = header.h - 4 * style->combo.content_padding.y;
+        bounds.y = header.y + 2 * style->combo.content_padding.y;
+        bounds.x = header.x + 2 * style->combo.content_padding.x;
+        bounds.w = (button.x - (style->combo.content_padding.x + style->combo.spacing.x)) - bounds.x;
+        nk_fill_rect(&win->buffer, bounds, 0, color);
+
+        /* draw open/close button */
+        nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state,
+            &ctx->style.combo.button, sym, style->font);
+    }
+    return nk_combo_begin(ctx, win, size, is_clicked, header);
+}
+
+NK_API int
+nk_combo_begin_symbol(struct nk_context *ctx, enum nk_symbol_type symbol, struct nk_vec2 size)
+{
+    struct nk_window *win;
+    struct nk_style *style;
+    const struct nk_input *in;
+
+    struct nk_rect header;
+    int is_clicked = nk_false;
+    enum nk_widget_layout_states s;
+    const struct nk_style_item *background;
+    struct nk_color sym_background;
+    struct nk_color symbol_color;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    style = &ctx->style;
+    s = nk_widget(&header, ctx);
+    if (s == NK_WIDGET_INVALID)
+        return 0;
+
+    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input;
+    if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT))
+        is_clicked = nk_true;
+
+    /* draw combo box header background and border */
+    if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) {
+        background = &style->combo.active;
+        symbol_color = style->combo.symbol_active;
+    } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) {
+        background = &style->combo.hover;
+        symbol_color = style->combo.symbol_hover;
+    } else {
+        background = &style->combo.normal;
+        symbol_color = style->combo.symbol_hover;
+    }
+
+    if (background->type == NK_STYLE_ITEM_IMAGE) {
+        sym_background = nk_rgba(0,0,0,0);
+        nk_draw_image(&win->buffer, header, &background->data.image, nk_white);
+    } else {
+        sym_background = background->data.color;
+        nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color);
+        nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color);
+    }
+    {
+        struct nk_rect bounds = {0,0,0,0};
+        struct nk_rect content;
+        struct nk_rect button;
+
+        enum nk_symbol_type sym;
+        if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER)
+            sym = style->combo.sym_hover;
+        else if (is_clicked)
+            sym = style->combo.sym_active;
+        else sym = style->combo.sym_normal;
+
+        /* calculate button */
+        button.w = header.h - 2 * style->combo.button_padding.y;
+        button.x = (header.x + header.w - header.h) - style->combo.button_padding.y;
+        button.y = header.y + style->combo.button_padding.y;
+        button.h = button.w;
+
+        content.x = button.x + style->combo.button.padding.x;
+        content.y = button.y + style->combo.button.padding.y;
+        content.w = button.w - 2 * style->combo.button.padding.x;
+        content.h = button.h - 2 * style->combo.button.padding.y;
+
+        /* draw symbol */
+        bounds.h = header.h - 2 * style->combo.content_padding.y;
+        bounds.y = header.y + style->combo.content_padding.y;
+        bounds.x = header.x + style->combo.content_padding.x;
+        bounds.w = (button.x - style->combo.content_padding.y) - bounds.x;
+        nk_draw_symbol(&win->buffer, symbol, bounds, sym_background, symbol_color,
+            1.0f, style->font);
+
+        /* draw open/close button */
+        nk_draw_button_symbol(&win->buffer, &bounds, &content, ctx->last_widget_state,
+            &ctx->style.combo.button, sym, style->font);
+    }
+    return nk_combo_begin(ctx, win, size, is_clicked, header);
+}
+
+NK_API int
+nk_combo_begin_symbol_text(struct nk_context *ctx, const char *selected, int len,
+    enum nk_symbol_type symbol, struct nk_vec2 size)
+{
+    struct nk_window *win;
+    struct nk_style *style;
+    struct nk_input *in;
+
+    struct nk_rect header;
+    int is_clicked = nk_false;
+    enum nk_widget_layout_states s;
+    const struct nk_style_item *background;
+    struct nk_color symbol_color;
+    struct nk_text text;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    style = &ctx->style;
+    s = nk_widget(&header, ctx);
+    if (!s) return 0;
+
+    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input;
+    if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT))
+        is_clicked = nk_true;
+
+    /* draw combo box header background and border */
+    if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) {
+        background = &style->combo.active;
+        symbol_color = style->combo.symbol_active;
+        text.text = style->combo.label_active;
+    } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) {
+        background = &style->combo.hover;
+        symbol_color = style->combo.symbol_hover;
+        text.text = style->combo.label_hover;
+    } else {
+        background = &style->combo.normal;
+        symbol_color = style->combo.symbol_normal;
+        text.text = style->combo.label_normal;
+    }
+    if (background->type == NK_STYLE_ITEM_IMAGE) {
+        text.background = nk_rgba(0,0,0,0);
+        nk_draw_image(&win->buffer, header, &background->data.image, nk_white);
+    } else {
+        text.background = background->data.color;
+        nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color);
+        nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color);
+    }
+    {
+        struct nk_rect content;
+        struct nk_rect button;
+        struct nk_rect label;
+        struct nk_rect image;
+
+        enum nk_symbol_type sym;
+        if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER)
+            sym = style->combo.sym_hover;
+        else if (is_clicked)
+            sym = style->combo.sym_active;
+        else sym = style->combo.sym_normal;
+
+        /* calculate button */
+        button.w = header.h - 2 * style->combo.button_padding.y;
+        button.x = (header.x + header.w - header.h) - style->combo.button_padding.x;
+        button.y = header.y + style->combo.button_padding.y;
+        button.h = button.w;
+
+        content.x = button.x + style->combo.button.padding.x;
+        content.y = button.y + style->combo.button.padding.y;
+        content.w = button.w - 2 * style->combo.button.padding.x;
+        content.h = button.h - 2 * style->combo.button.padding.y;
+        nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state,
+            &ctx->style.combo.button, sym, style->font);
+
+        /* draw symbol */
+        image.x = header.x + style->combo.content_padding.x;
+        image.y = header.y + style->combo.content_padding.y;
+        image.h = header.h - 2 * style->combo.content_padding.y;
+        image.w = image.h;
+        nk_draw_symbol(&win->buffer, symbol, image, text.background, symbol_color,
+            1.0f, style->font);
+
+        /* draw label */
+        text.padding = nk_vec2(0,0);
+        label.x = image.x + image.w + style->combo.spacing.x + style->combo.content_padding.x;
+        label.y = header.y + style->combo.content_padding.y;
+        label.w = (button.x - style->combo.content_padding.x) - label.x;
+        label.h = header.h - 2 * style->combo.content_padding.y;
+        nk_widget_text(&win->buffer, label, selected, len, &text, NK_TEXT_LEFT, style->font);
+    }
+    return nk_combo_begin(ctx, win, size, is_clicked, header);
+}
+
+NK_API int
+nk_combo_begin_image(struct nk_context *ctx, struct nk_image img, struct nk_vec2 size)
+{
+    struct nk_window *win;
+    struct nk_style *style;
+    const struct nk_input *in;
+
+    struct nk_rect header;
+    int is_clicked = nk_false;
+    enum nk_widget_layout_states s;
+    const struct nk_style_item *background;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    style = &ctx->style;
+    s = nk_widget(&header, ctx);
+    if (s == NK_WIDGET_INVALID)
+        return 0;
+
+    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input;
+    if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT))
+        is_clicked = nk_true;
+
+    /* draw combo box header background and border */
+    if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED)
+        background = &style->combo.active;
+    else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER)
+        background = &style->combo.hover;
+    else background = &style->combo.normal;
+
+    if (background->type == NK_STYLE_ITEM_IMAGE) {
+        nk_draw_image(&win->buffer, header, &background->data.image, nk_white);
+    } else {
+        nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color);
+        nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color);
+    }
+    {
+        struct nk_rect bounds = {0,0,0,0};
+        struct nk_rect content;
+        struct nk_rect button;
+
+        enum nk_symbol_type sym;
+        if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER)
+            sym = style->combo.sym_hover;
+        else if (is_clicked)
+            sym = style->combo.sym_active;
+        else sym = style->combo.sym_normal;
+
+        /* calculate button */
+        button.w = header.h - 2 * style->combo.button_padding.y;
+        button.x = (header.x + header.w - header.h) - style->combo.button_padding.y;
+        button.y = header.y + style->combo.button_padding.y;
+        button.h = button.w;
+
+        content.x = button.x + style->combo.button.padding.x;
+        content.y = button.y + style->combo.button.padding.y;
+        content.w = button.w - 2 * style->combo.button.padding.x;
+        content.h = button.h - 2 * style->combo.button.padding.y;
+
+        /* draw image */
+        bounds.h = header.h - 2 * style->combo.content_padding.y;
+        bounds.y = header.y + style->combo.content_padding.y;
+        bounds.x = header.x + style->combo.content_padding.x;
+        bounds.w = (button.x - style->combo.content_padding.y) - bounds.x;
+        nk_draw_image(&win->buffer, bounds, &img, nk_white);
+
+        /* draw open/close button */
+        nk_draw_button_symbol(&win->buffer, &bounds, &content, ctx->last_widget_state,
+            &ctx->style.combo.button, sym, style->font);
+    }
+    return nk_combo_begin(ctx, win, size, is_clicked, header);
+}
+
+NK_API int
+nk_combo_begin_image_text(struct nk_context *ctx, const char *selected, int len,
+    struct nk_image img, struct nk_vec2 size)
+{
+    struct nk_window *win;
+    struct nk_style *style;
+    struct nk_input *in;
+
+    struct nk_rect header;
+    int is_clicked = nk_false;
+    enum nk_widget_layout_states s;
+    const struct nk_style_item *background;
+    struct nk_text text;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    style = &ctx->style;
+    s = nk_widget(&header, ctx);
+    if (!s) return 0;
+
+    in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input;
+    if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT))
+        is_clicked = nk_true;
+
+    /* draw combo box header background and border */
+    if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) {
+        background = &style->combo.active;
+        text.text = style->combo.label_active;
+    } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) {
+        background = &style->combo.hover;
+        text.text = style->combo.label_hover;
+    } else {
+        background = &style->combo.normal;
+        text.text = style->combo.label_normal;
+    }
+    if (background->type == NK_STYLE_ITEM_IMAGE) {
+        text.background = nk_rgba(0,0,0,0);
+        nk_draw_image(&win->buffer, header, &background->data.image, nk_white);
+    } else {
+        text.background = background->data.color;
+        nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color);
+        nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color);
+    }
+    {
+        struct nk_rect content;
+        struct nk_rect button;
+        struct nk_rect label;
+        struct nk_rect image;
+
+        enum nk_symbol_type sym;
+        if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER)
+            sym = style->combo.sym_hover;
+        else if (is_clicked)
+            sym = style->combo.sym_active;
+        else sym = style->combo.sym_normal;
+
+        /* calculate button */
+        button.w = header.h - 2 * style->combo.button_padding.y;
+        button.x = (header.x + header.w - header.h) - style->combo.button_padding.x;
+        button.y = header.y + style->combo.button_padding.y;
+        button.h = button.w;
+
+        content.x = button.x + style->combo.button.padding.x;
+        content.y = button.y + style->combo.button.padding.y;
+        content.w = button.w - 2 * style->combo.button.padding.x;
+        content.h = button.h - 2 * style->combo.button.padding.y;
+        nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state,
+            &ctx->style.combo.button, sym, style->font);
+
+        /* draw image */
+        image.x = header.x + style->combo.content_padding.x;
+        image.y = header.y + style->combo.content_padding.y;
+        image.h = header.h - 2 * style->combo.content_padding.y;
+        image.w = image.h;
+        nk_draw_image(&win->buffer, image, &img, nk_white);
+
+        /* draw label */
+        text.padding = nk_vec2(0,0);
+        label.x = image.x + image.w + style->combo.spacing.x + style->combo.content_padding.x;
+        label.y = header.y + style->combo.content_padding.y;
+        label.w = (button.x - style->combo.content_padding.x) - label.x;
+        label.h = header.h - 2 * style->combo.content_padding.y;
+        nk_widget_text(&win->buffer, label, selected, len, &text, NK_TEXT_LEFT, style->font);
+    }
+    return nk_combo_begin(ctx, win, size, is_clicked, header);
+}
+
+NK_API int nk_combo_begin_symbol_label(struct nk_context *ctx,
+    const char *selected, enum nk_symbol_type type, struct nk_vec2 size)
+{return nk_combo_begin_symbol_text(ctx, selected, nk_strlen(selected), type, size);}
+
+NK_API int nk_combo_begin_image_label(struct nk_context *ctx,
+    const char *selected, struct nk_image img, struct nk_vec2 size)
+{return nk_combo_begin_image_text(ctx, selected, nk_strlen(selected), img, size);}
+
+NK_API int nk_combo_item_text(struct nk_context *ctx, const char *text, int len,nk_flags align)
+{return nk_contextual_item_text(ctx, text, len, align);}
+
+NK_API int nk_combo_item_label(struct nk_context *ctx, const char *label, nk_flags align)
+{return nk_contextual_item_label(ctx, label, align);}
+
+NK_API int nk_combo_item_image_text(struct nk_context *ctx, struct nk_image img, const char *text,
+    int len, nk_flags alignment)
+{return nk_contextual_item_image_text(ctx, img, text, len, alignment);}
+
+NK_API int nk_combo_item_image_label(struct nk_context *ctx, struct nk_image img,
+    const char *text, nk_flags alignment)
+{return nk_contextual_item_image_label(ctx, img, text, alignment);}
+
+NK_API int nk_combo_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym,
+    const char *text, int len, nk_flags alignment)
+{return nk_contextual_item_symbol_text(ctx, sym, text, len, alignment);}
+
+NK_API int nk_combo_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym,
+    const char *label, nk_flags alignment)
+{return nk_contextual_item_symbol_label(ctx, sym, label, alignment);}
+
+NK_API void nk_combo_end(struct nk_context *ctx)
+{nk_contextual_end(ctx);}
+
+NK_API void nk_combo_close(struct nk_context *ctx)
+{nk_contextual_close(ctx);}
+
+NK_API int
+nk_combo(struct nk_context *ctx, const char **items, int count,
+    int selected, int item_height, struct nk_vec2 size)
+{
+    int i = 0;
+    int max_height;
+    struct nk_vec2 item_spacing;
+    struct nk_vec2 window_padding;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(items);
+    NK_ASSERT(ctx->current);
+    if (!ctx || !items ||!count)
+        return selected;
+
+    item_spacing = ctx->style.window.spacing;
+    window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type);
+    max_height = count * item_height + count * (int)item_spacing.y;
+    max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2;
+    size.y = NK_MIN(size.y, (float)max_height);
+    if (nk_combo_begin_label(ctx, items[selected], size)) {
+        nk_layout_row_dynamic(ctx, (float)item_height, 1);
+        for (i = 0; i < count; ++i) {
+            if (nk_combo_item_label(ctx, items[i], NK_TEXT_LEFT))
+                selected = i;
+        }
+        nk_combo_end(ctx);
+    }
+    return selected;
+}
+
+NK_API int
+nk_combo_separator(struct nk_context *ctx, const char *items_separated_by_separator,
+    int separator, int selected, int count, int item_height, struct nk_vec2 size)
+{
+    int i;
+    int max_height;
+    struct nk_vec2 item_spacing;
+    struct nk_vec2 window_padding;
+    const char *current_item;
+    const char *iter;
+    int length = 0;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(items_separated_by_separator);
+    if (!ctx || !items_separated_by_separator)
+        return selected;
+
+    /* calculate popup window */
+    item_spacing = ctx->style.window.spacing;
+    window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type);
+    max_height = count * item_height + count * (int)item_spacing.y;
+    max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2;
+    size.y = NK_MIN(size.y, (float)max_height);
+
+    /* find selected item */
+    current_item = items_separated_by_separator;
+    for (i = 0; i < count; ++i) {
+        iter = current_item;
+        while (*iter && *iter != separator) iter++;
+        length = (int)(iter - current_item);
+        if (i == selected) break;
+        current_item = iter + 1;
+    }
+
+    if (nk_combo_begin_text(ctx, current_item, length, size)) {
+        current_item = items_separated_by_separator;
+        nk_layout_row_dynamic(ctx, (float)item_height, 1);
+        for (i = 0; i < count; ++i) {
+            iter = current_item;
+            while (*iter && *iter != separator) iter++;
+            length = (int)(iter - current_item);
+            if (nk_combo_item_text(ctx, current_item, length, NK_TEXT_LEFT))
+                selected = i;
+            current_item = current_item + length + 1;
+        }
+        nk_combo_end(ctx);
+    }
+    return selected;
+}
+
+NK_API int
+nk_combo_string(struct nk_context *ctx, const char *items_separated_by_zeros,
+    int selected, int count, int item_height, struct nk_vec2 size)
+{return nk_combo_separator(ctx, items_separated_by_zeros, '\0', selected, count, item_height, size);}
+
+NK_API int
+nk_combo_callback(struct nk_context *ctx, void(*item_getter)(void*, int, const char**),
+    void *userdata, int selected, int count, int item_height, struct nk_vec2 size)
+{
+    int i;
+    int max_height;
+    struct nk_vec2 item_spacing;
+    struct nk_vec2 window_padding;
+    const char *item;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(item_getter);
+    if (!ctx || !item_getter)
+        return selected;
+
+    /* calculate popup window */
+    item_spacing = ctx->style.window.spacing;
+    window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type);
+    max_height = count * item_height + count * (int)item_spacing.y;
+    max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2;
+    size.y = NK_MIN(size.y, (float)max_height);
+
+    item_getter(userdata, selected, &item);
+    if (nk_combo_begin_label(ctx, item, size)) {
+        nk_layout_row_dynamic(ctx, (float)item_height, 1);
+        for (i = 0; i < count; ++i) {
+            item_getter(userdata, i, &item);
+            if (nk_combo_item_label(ctx, item, NK_TEXT_LEFT))
+                selected = i;
+        }
+        nk_combo_end(ctx);
+    }
+    return selected;
+}
+
+NK_API void nk_combobox(struct nk_context *ctx, const char **items, int count,
+    int *selected, int item_height, struct nk_vec2 size)
+{*selected = nk_combo(ctx, items, count, *selected, item_height, size);}
+
+NK_API void nk_combobox_string(struct nk_context *ctx, const char *items_separated_by_zeros,
+    int *selected, int count, int item_height, struct nk_vec2 size)
+{*selected = nk_combo_string(ctx, items_separated_by_zeros, *selected, count, item_height, size);}
+
+NK_API void nk_combobox_separator(struct nk_context *ctx, const char *items_separated_by_separator,
+    int separator,int *selected, int count, int item_height, struct nk_vec2 size)
+{*selected = nk_combo_separator(ctx, items_separated_by_separator, separator,
+    *selected, count, item_height, size);}
+
+NK_API void nk_combobox_callback(struct nk_context *ctx,
+    void(*item_getter)(void* data, int id, const char **out_text),
+    void *userdata, int *selected, int count, int item_height, struct nk_vec2 size)
+{*selected = nk_combo_callback(ctx, item_getter, userdata,  *selected, count, item_height, size);}
+
+/*
+ * -------------------------------------------------------------
+ *
+ *                          MENU
+ *
+ * --------------------------------------------------------------
+ */
+NK_INTERN int
+nk_menu_begin(struct nk_context *ctx, struct nk_window *win,
+    const char *id, int is_clicked, struct nk_rect header, struct nk_vec2 size)
+{
+    int is_open = 0;
+    int is_active = 0;
+    struct nk_rect body;
+    struct nk_window *popup;
+    nk_hash hash = nk_murmur_hash(id, (int)nk_strlen(id), NK_PANEL_MENU);
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    body.x = header.x;
+    body.w = size.x;
+    body.y = header.y + header.h;
+    body.h = size.y;
+
+    popup = win->popup.win;
+    is_open = popup ? nk_true : nk_false;
+    is_active = (popup && (win->popup.name == hash) && win->popup.type == NK_PANEL_MENU);
+    if ((is_clicked && is_open && !is_active) || (is_open && !is_active) ||
+        (!is_open && !is_active && !is_clicked)) return 0;
+    if (!nk_nonblock_begin(ctx, NK_WINDOW_NO_SCROLLBAR, body, header, NK_PANEL_MENU))
+        return 0;
+
+    win->popup.type = NK_PANEL_MENU;
+    win->popup.name = hash;
+    return 1;
+}
+
+NK_API int
+nk_menu_begin_text(struct nk_context *ctx, const char *title, int len,
+    nk_flags align, struct nk_vec2 size)
+{
+    struct nk_window *win;
+    const struct nk_input *in;
+    struct nk_rect header;
+    int is_clicked = nk_false;
+    nk_flags state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    state = nk_widget(&header, ctx);
+    if (!state) return 0;
+    in = (state == NK_WIDGET_ROM || win->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, header,
+        title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, ctx->style.font))
+        is_clicked = nk_true;
+    return nk_menu_begin(ctx, win, title, is_clicked, header, size);
+}
+
+NK_API int nk_menu_begin_label(struct nk_context *ctx,
+    const char *text, nk_flags align, struct nk_vec2 size)
+{return nk_menu_begin_text(ctx, text, nk_strlen(text), align, size);}
+
+NK_API int
+nk_menu_begin_image(struct nk_context *ctx, const char *id, struct nk_image img,
+    struct nk_vec2 size)
+{
+    struct nk_window *win;
+    struct nk_rect header;
+    const struct nk_input *in;
+    int is_clicked = nk_false;
+    nk_flags state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    state = nk_widget(&header, ctx);
+    if (!state) return 0;
+    in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    if (nk_do_button_image(&ctx->last_widget_state, &win->buffer, header,
+        img, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in))
+        is_clicked = nk_true;
+    return nk_menu_begin(ctx, win, id, is_clicked, header, size);
+}
+
+NK_API int
+nk_menu_begin_symbol(struct nk_context *ctx, const char *id,
+    enum nk_symbol_type sym, struct nk_vec2 size)
+{
+    struct nk_window *win;
+    const struct nk_input *in;
+    struct nk_rect header;
+    int is_clicked = nk_false;
+    nk_flags state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    state = nk_widget(&header, ctx);
+    if (!state) return 0;
+    in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    if (nk_do_button_symbol(&ctx->last_widget_state,  &win->buffer, header,
+        sym, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, ctx->style.font))
+        is_clicked = nk_true;
+    return nk_menu_begin(ctx, win, id, is_clicked, header, size);
+}
+
+NK_API int
+nk_menu_begin_image_text(struct nk_context *ctx, const char *title, int len,
+    nk_flags align, struct nk_image img, struct nk_vec2 size)
+{
+    struct nk_window *win;
+    struct nk_rect header;
+    const struct nk_input *in;
+    int is_clicked = nk_false;
+    nk_flags state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    state = nk_widget(&header, ctx);
+    if (!state) return 0;
+    in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    if (nk_do_button_text_image(&ctx->last_widget_state, &win->buffer,
+        header, img, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button,
+        ctx->style.font, in))
+        is_clicked = nk_true;
+    return nk_menu_begin(ctx, win, title, is_clicked, header, size);
+}
+
+NK_API int nk_menu_begin_image_label(struct nk_context *ctx,
+    const char *title, nk_flags align, struct nk_image img, struct nk_vec2 size)
+{return nk_menu_begin_image_text(ctx, title, nk_strlen(title), align, img, size);}
+
+NK_API int
+nk_menu_begin_symbol_text(struct nk_context *ctx, const char *title, int len,
+    nk_flags align, enum nk_symbol_type sym, struct nk_vec2 size)
+{
+    struct nk_window *win;
+    struct nk_rect header;
+    const struct nk_input *in;
+    int is_clicked = nk_false;
+    nk_flags state;
+
+    NK_ASSERT(ctx);
+    NK_ASSERT(ctx->current);
+    NK_ASSERT(ctx->current->layout);
+    if (!ctx || !ctx->current || !ctx->current->layout)
+        return 0;
+
+    win = ctx->current;
+    state = nk_widget(&header, ctx);
+    if (!state) return 0;
+
+    in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
+    if (nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer,
+        header, sym, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button,
+        ctx->style.font, in)) is_clicked = nk_true;
+    return nk_menu_begin(ctx, win, title, is_clicked, header, size);
+}
+
+NK_API int nk_menu_begin_symbol_label(struct nk_context *ctx,
+    const char *title, nk_flags align, enum nk_symbol_type sym, struct nk_vec2 size )
+{return nk_menu_begin_symbol_text(ctx, title, nk_strlen(title), align,sym,size);}
+
+NK_API int nk_menu_item_text(struct nk_context *ctx, const char *title, int len, nk_flags align)
+{return nk_contextual_item_text(ctx, title, len, align);}
+
+NK_API int nk_menu_item_label(struct nk_context *ctx, const char *label, nk_flags align)
+{return nk_contextual_item_label(ctx, label, align);}
+
+NK_API int nk_menu_item_image_label(struct nk_context *ctx, struct nk_image img,
+    const char *label, nk_flags align)
+{return nk_contextual_item_image_label(ctx, img, label, align);}
+
+NK_API int nk_menu_item_image_text(struct nk_context *ctx, struct nk_image img,
+    const char *text, int len, nk_flags align)
+{return nk_contextual_item_image_text(ctx, img, text, len, align);}
+
+NK_API int nk_menu_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym,
+    const char *text, int len, nk_flags align)
+{return nk_contextual_item_symbol_text(ctx, sym, text, len, align);}
+
+NK_API int nk_menu_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym,
+    const char *label, nk_flags align)
+{return nk_contextual_item_symbol_label(ctx, sym, label, align);}
+
+NK_API void nk_menu_close(struct nk_context *ctx)
+{nk_contextual_close(ctx);}
+
+NK_API void
+nk_menu_end(struct nk_context *ctx)
+{nk_contextual_end(ctx);}
+
+#endif /* NK_IMPLEMENTATION */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nuklear_ui/nuklear_sdl_gles2.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,450 @@
+/*
+ * Nuklear - 1.40.8 - public domain
+ * no warrenty implied; use at your own risk.
+ * authored from 2015-2017 by Micha Mettke
+ * emscripten from 2016 by Chris Willcocks
+ * OpenGL ES 2.0 from 2017 by Dmitry Hrabrov a.k.a. DeXPeriX
+ */
+/*
+ * ==============================================================
+ *
+ *                              API
+ *
+ * ===============================================================
+ */
+#ifndef NK_SDL_GLES2_H_
+#define NK_SDL_GLES2_H_
+
+#include <SDL.h>
+#ifndef DISABLE_OPENGL
+#include <GL/glew.h>
+#endif
+
+
+NK_API struct nk_context*   nk_sdl_init(SDL_Window *win);
+NK_API void                 nk_sdl_font_stash_begin(struct nk_font_atlas **atlas);
+NK_API void                 nk_sdl_font_stash_end(void);
+NK_API int                  nk_sdl_handle_event(SDL_Event *evt);
+NK_API void                 nk_sdl_render(enum nk_anti_aliasing , int max_vertex_buffer, int max_element_buffer);
+NK_API void                 nk_sdl_shutdown(void);
+NK_API void                 nk_sdl_device_destroy(void);
+NK_API void                 nk_sdl_device_create(void);
+
+#endif
+
+/*
+ * ==============================================================
+ *
+ *                          IMPLEMENTATION
+ *
+ * ===============================================================
+ */
+#ifdef NK_SDL_GLES2_IMPLEMENTATION
+
+#include <string.h>
+
+struct nk_sdl_device {
+    struct nk_buffer cmds;
+    struct nk_draw_null_texture null;
+    GLuint vbo, ebo;
+    GLuint prog;
+    GLuint vert_shdr;
+    GLuint frag_shdr;
+    GLint attrib_pos;
+    GLint attrib_uv;
+    GLint attrib_col;
+    GLint uniform_tex;
+    GLint uniform_proj;
+    GLuint font_tex;
+    GLsizei vs;
+    size_t vp, vt, vc;
+};
+
+struct nk_sdl_vertex {
+    GLfloat position[2];
+    GLfloat uv[2];
+    nk_byte col[4];
+};
+
+static struct nk_sdl {
+    SDL_Window *win;
+    struct nk_sdl_device ogl;
+    struct nk_context ctx;
+    struct nk_font_atlas atlas;
+} sdl;
+
+#ifdef USE_GLES
+#define NK_SHADER_VERSION "#version 100\n"
+#define DECLARE_PRECISION "precision mediump float;\n"
+#else
+#define NK_SHADER_VERSION "#version 110\n"
+#define DECLARE_PRECISION
+#endif
+
+
+NK_API void
+nk_sdl_device_create(void)
+{
+    GLint status;
+    static const GLchar *vertex_shader =
+        NK_SHADER_VERSION
+        "uniform mat4 ProjMtx;\n"
+        "attribute vec2 Position;\n"
+        "attribute vec2 TexCoord;\n"
+        "attribute vec4 Color;\n"
+        "varying vec2 Frag_UV;\n"
+        "varying vec4 Frag_Color;\n"
+        "void main() {\n"
+        "   Frag_UV = TexCoord;\n"
+        "   Frag_Color = Color;\n"
+        "   gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n"
+        "}\n";
+    static const GLchar *fragment_shader =
+        NK_SHADER_VERSION
+        DECLARE_PRECISION
+        "uniform sampler2D Texture;\n"
+        "varying vec2 Frag_UV;\n"
+        "varying vec4 Frag_Color;\n"
+        "void main(){\n"
+        "   gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV);\n"
+        "}\n";
+
+    struct nk_sdl_device *dev = &sdl.ogl;
+    
+    nk_buffer_init_default(&dev->cmds);
+    dev->prog = glCreateProgram();
+    dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER);
+    dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER);
+    glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0);
+    glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0);
+    glCompileShader(dev->vert_shdr);
+    glCompileShader(dev->frag_shdr);
+    glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status);
+    assert(status == GL_TRUE);
+    glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status);
+    assert(status == GL_TRUE);
+    glAttachShader(dev->prog, dev->vert_shdr);
+    glAttachShader(dev->prog, dev->frag_shdr);
+    glLinkProgram(dev->prog);
+    glGetProgramiv(dev->prog, GL_LINK_STATUS, &status);
+    assert(status == GL_TRUE);
+
+
+    dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture");
+    dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx");
+    dev->attrib_pos = glGetAttribLocation(dev->prog, "Position");
+    dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord");
+    dev->attrib_col = glGetAttribLocation(dev->prog, "Color");
+    {
+        dev->vs = sizeof(struct nk_sdl_vertex);
+        dev->vp = offsetof(struct nk_sdl_vertex, position);
+        dev->vt = offsetof(struct nk_sdl_vertex, uv);
+        dev->vc = offsetof(struct nk_sdl_vertex, col);
+        
+        /* Allocate buffers */
+        glGenBuffers(1, &dev->vbo);
+        glGenBuffers(1, &dev->ebo);
+    }
+    glBindTexture(GL_TEXTURE_2D, 0);
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+NK_INTERN void
+nk_sdl_device_upload_atlas(const void *image, int width, int height)
+{
+    struct nk_sdl_device *dev = &sdl.ogl;
+    glGenTextures(1, &dev->font_tex);
+    glBindTexture(GL_TEXTURE_2D, dev->font_tex);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0,
+                GL_RGBA, GL_UNSIGNED_BYTE, image);
+}
+
+NK_API void
+nk_sdl_device_destroy(void)
+{
+    struct nk_sdl_device *dev = &sdl.ogl;
+    glDetachShader(dev->prog, dev->vert_shdr);
+    glDetachShader(dev->prog, dev->frag_shdr);
+    glDeleteShader(dev->vert_shdr);
+    glDeleteShader(dev->frag_shdr);
+    glDeleteProgram(dev->prog);
+    glDeleteTextures(1, &dev->font_tex);
+    glDeleteBuffers(1, &dev->vbo);
+    glDeleteBuffers(1, &dev->ebo);
+    nk_buffer_free(&dev->cmds);
+}
+
+NK_API void
+nk_sdl_render(enum nk_anti_aliasing AA, int max_vertex_buffer, int max_element_buffer)
+{
+    struct nk_sdl_device *dev = &sdl.ogl;
+    int width, height;
+    int display_width, display_height;
+    struct nk_vec2 scale;
+    GLfloat ortho[4][4] = {
+        {2.0f, 0.0f, 0.0f, 0.0f},
+        {0.0f,-2.0f, 0.0f, 0.0f},
+        {0.0f, 0.0f,-1.0f, 0.0f},
+        {-1.0f,1.0f, 0.0f, 1.0f},
+    };
+    SDL_GetWindowSize(sdl.win, &width, &height);
+    SDL_GL_GetDrawableSize(sdl.win, &display_width, &display_height);
+    ortho[0][0] /= (GLfloat)width;
+    ortho[1][1] /= (GLfloat)height;
+
+    scale.x = (float)display_width/(float)width;
+    scale.y = (float)display_height/(float)height;
+
+    /* setup global state */
+    glViewport(0,0,display_width,display_height);
+    glEnable(GL_BLEND);
+    glBlendEquation(GL_FUNC_ADD);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glDisable(GL_CULL_FACE);
+    glDisable(GL_DEPTH_TEST);
+    glEnable(GL_SCISSOR_TEST);
+    glActiveTexture(GL_TEXTURE0);
+
+    /* setup program */
+    glUseProgram(dev->prog);
+    glUniform1i(dev->uniform_tex, 0);
+    glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]);
+    {
+        /* convert from command queue into draw list and draw to screen */
+        const struct nk_draw_command *cmd;
+        void *vertices, *elements;
+        const nk_draw_index *offset = NULL;
+
+        /* Bind buffers */
+        glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
+        
+        {
+            /* buffer setup */
+            glEnableVertexAttribArray((GLuint)dev->attrib_pos);
+            glEnableVertexAttribArray((GLuint)dev->attrib_uv);
+            glEnableVertexAttribArray((GLuint)dev->attrib_col);
+
+            glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, dev->vs, (void*)dev->vp);
+            glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, dev->vs, (void*)dev->vt);
+            glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, dev->vs, (void*)dev->vc);
+        }
+
+        glBufferData(GL_ARRAY_BUFFER, max_vertex_buffer, NULL, GL_STREAM_DRAW);
+        glBufferData(GL_ELEMENT_ARRAY_BUFFER, max_element_buffer, NULL, GL_STREAM_DRAW);
+
+        /* load vertices/elements directly into vertex/element buffer */
+        vertices = malloc((size_t)max_vertex_buffer);
+        elements = malloc((size_t)max_element_buffer);
+        {
+            /* fill convert configuration */
+            struct nk_convert_config config;
+            static const struct nk_draw_vertex_layout_element vertex_layout[] = {
+                {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_sdl_vertex, position)},
+                {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_sdl_vertex, uv)},
+                {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_sdl_vertex, col)},
+                {NK_VERTEX_LAYOUT_END}
+            };
+            NK_MEMSET(&config, 0, sizeof(config));
+            config.vertex_layout = vertex_layout;
+            config.vertex_size = sizeof(struct nk_sdl_vertex);
+            config.vertex_alignment = NK_ALIGNOF(struct nk_sdl_vertex);
+            config.null = dev->null;
+            config.circle_segment_count = 22;
+            config.curve_segment_count = 22;
+            config.arc_segment_count = 22;
+            config.global_alpha = 1.0f;
+            config.shape_AA = AA;
+            config.line_AA = AA;
+
+            /* setup buffers to load vertices and elements */
+            {struct nk_buffer vbuf, ebuf;
+            nk_buffer_init_fixed(&vbuf, vertices, (nk_size)max_vertex_buffer);
+            nk_buffer_init_fixed(&ebuf, elements, (nk_size)max_element_buffer);
+            nk_convert(&sdl.ctx, &dev->cmds, &vbuf, &ebuf, &config);}
+        }
+        glBufferSubData(GL_ARRAY_BUFFER, 0, (size_t)max_vertex_buffer, vertices);
+        glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, (size_t)max_element_buffer, elements);
+        free(vertices);
+        free(elements);
+
+        /* iterate over and execute each draw command */
+        nk_draw_foreach(cmd, &sdl.ctx, &dev->cmds) {
+            if (!cmd->elem_count) continue;
+            glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id);
+            glScissor((GLint)(cmd->clip_rect.x * scale.x),
+                (GLint)((height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * scale.y),
+                (GLint)(cmd->clip_rect.w * scale.x),
+                (GLint)(cmd->clip_rect.h * scale.y));
+            glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset);
+            offset += cmd->elem_count;
+        }
+        nk_clear(&sdl.ctx);
+    }
+
+    glUseProgram(0);
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+    glDisable(GL_BLEND);
+    glDisable(GL_SCISSOR_TEST);
+}
+
+static void
+nk_sdl_clipbard_paste(nk_handle usr, struct nk_text_edit *edit)
+{
+    const char *text = SDL_GetClipboardText();
+    if (text) nk_textedit_paste(edit, text, nk_strlen(text));
+    (void)usr;
+}
+
+static void
+nk_sdl_clipbard_copy(nk_handle usr, const char *text, int len)
+{
+    char *str = 0;
+    (void)usr;
+    if (!len) return;
+    str = (char*)malloc((size_t)len+1);
+    if (!str) return;
+    memcpy(str, text, (size_t)len);
+    str[len] = '\0';
+    SDL_SetClipboardText(str);
+    free(str);
+}
+
+NK_API struct nk_context*
+nk_sdl_init(SDL_Window *win)
+{
+    sdl.win = win;
+    nk_init_default(&sdl.ctx, 0);
+    sdl.ctx.clip.copy = nk_sdl_clipbard_copy;
+    sdl.ctx.clip.paste = nk_sdl_clipbard_paste;
+    sdl.ctx.clip.userdata = nk_handle_ptr(0);
+    nk_sdl_device_create();
+    return &sdl.ctx;
+}
+
+NK_API void
+nk_sdl_font_stash_begin(struct nk_font_atlas **atlas)
+{
+    nk_font_atlas_init_default(&sdl.atlas);
+    nk_font_atlas_begin(&sdl.atlas);
+    *atlas = &sdl.atlas;
+}
+
+NK_API void
+nk_sdl_font_stash_end(void)
+{
+    const void *image; int w, h;
+    image = nk_font_atlas_bake(&sdl.atlas, &w, &h, NK_FONT_ATLAS_RGBA32);
+    nk_sdl_device_upload_atlas(image, w, h);
+    nk_font_atlas_end(&sdl.atlas, nk_handle_id((int)sdl.ogl.font_tex), &sdl.ogl.null);
+    if (sdl.atlas.default_font)
+        nk_style_set_font(&sdl.ctx, &sdl.atlas.default_font->handle);
+
+}
+
+NK_API int
+nk_sdl_handle_event(SDL_Event *evt)
+{
+    struct nk_context *ctx = &sdl.ctx;
+    if (evt->type == SDL_KEYUP || evt->type == SDL_KEYDOWN) {
+        /* key events */
+        int down = evt->type == SDL_KEYDOWN;
+        const Uint8* state = SDL_GetKeyboardState(0);
+        SDL_Keycode sym = evt->key.keysym.sym;
+        if (sym == SDLK_RSHIFT || sym == SDLK_LSHIFT)
+            nk_input_key(ctx, NK_KEY_SHIFT, down);
+        else if (sym == SDLK_DELETE)
+            nk_input_key(ctx, NK_KEY_DEL, down);
+        else if (sym == SDLK_RETURN)
+            nk_input_key(ctx, NK_KEY_ENTER, down);
+        else if (sym == SDLK_TAB)
+            nk_input_key(ctx, NK_KEY_TAB, down);
+        else if (sym == SDLK_BACKSPACE)
+            nk_input_key(ctx, NK_KEY_BACKSPACE, down);
+        else if (sym == SDLK_HOME) {
+            nk_input_key(ctx, NK_KEY_TEXT_START, down);
+            nk_input_key(ctx, NK_KEY_SCROLL_START, down);
+        } else if (sym == SDLK_END) {
+            nk_input_key(ctx, NK_KEY_TEXT_END, down);
+            nk_input_key(ctx, NK_KEY_SCROLL_END, down);
+        } else if (sym == SDLK_PAGEDOWN) {
+            nk_input_key(ctx, NK_KEY_SCROLL_DOWN, down);
+        } else if (sym == SDLK_PAGEUP) {
+            nk_input_key(ctx, NK_KEY_SCROLL_UP, down);
+        } else if (sym == SDLK_z)
+            nk_input_key(ctx, NK_KEY_TEXT_UNDO, down && state[SDL_SCANCODE_LCTRL]);
+        else if (sym == SDLK_r)
+            nk_input_key(ctx, NK_KEY_TEXT_REDO, down && state[SDL_SCANCODE_LCTRL]);
+        else if (sym == SDLK_c)
+            nk_input_key(ctx, NK_KEY_COPY, down && state[SDL_SCANCODE_LCTRL]);
+        else if (sym == SDLK_v)
+            nk_input_key(ctx, NK_KEY_PASTE, down && state[SDL_SCANCODE_LCTRL]);
+        else if (sym == SDLK_x)
+            nk_input_key(ctx, NK_KEY_CUT, down && state[SDL_SCANCODE_LCTRL]);
+        else if (sym == SDLK_b)
+            nk_input_key(ctx, NK_KEY_TEXT_LINE_START, down && state[SDL_SCANCODE_LCTRL]);
+        else if (sym == SDLK_e)
+            nk_input_key(ctx, NK_KEY_TEXT_LINE_END, down && state[SDL_SCANCODE_LCTRL]);
+        else if (sym == SDLK_UP)
+            nk_input_key(ctx, NK_KEY_UP, down);
+        else if (sym == SDLK_DOWN)
+            nk_input_key(ctx, NK_KEY_DOWN, down);
+        else if (sym == SDLK_LEFT) {
+            if (state[SDL_SCANCODE_LCTRL])
+                nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, down);
+            else nk_input_key(ctx, NK_KEY_LEFT, down);
+        } else if (sym == SDLK_RIGHT) {
+            if (state[SDL_SCANCODE_LCTRL])
+                nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, down);
+            else nk_input_key(ctx, NK_KEY_RIGHT, down);
+        } else return 0;
+        return 1;
+    } else if (evt->type == SDL_MOUSEBUTTONDOWN || evt->type == SDL_MOUSEBUTTONUP) {
+        /* mouse button */
+        int down = evt->type == SDL_MOUSEBUTTONDOWN;
+        const int x = evt->button.x, y = evt->button.y;
+        if (evt->button.button == SDL_BUTTON_LEFT) {
+            if (evt->button.clicks > 1)
+                nk_input_button(ctx, NK_BUTTON_DOUBLE, x, y, down);
+            nk_input_button(ctx, NK_BUTTON_LEFT, x, y, down);
+        } else if (evt->button.button == SDL_BUTTON_MIDDLE)
+            nk_input_button(ctx, NK_BUTTON_MIDDLE, x, y, down);
+        else if (evt->button.button == SDL_BUTTON_RIGHT)
+            nk_input_button(ctx, NK_BUTTON_RIGHT, x, y, down);
+        return 1;
+    } else if (evt->type == SDL_MOUSEMOTION) {
+        /* mouse motion */
+        if (ctx->input.mouse.grabbed) {
+            int x = (int)ctx->input.mouse.prev.x, y = (int)ctx->input.mouse.prev.y;
+            nk_input_motion(ctx, x + evt->motion.xrel, y + evt->motion.yrel);
+        } else nk_input_motion(ctx, evt->motion.x, evt->motion.y);
+        return 1;
+    } else if (evt->type == SDL_TEXTINPUT) {
+        /* text input */
+        nk_glyph glyph;
+        memcpy(glyph, evt->text.text, NK_UTF_SIZE);
+        nk_input_glyph(ctx, glyph);
+        return 1;
+    } else if (evt->type == SDL_MOUSEWHEEL) {
+        /* mouse wheel */
+        nk_input_scroll(ctx,nk_vec2((float)evt->wheel.x,(float)evt->wheel.y));
+        return 1;
+    }
+    return 0;
+}
+
+NK_API
+void nk_sdl_shutdown(void)
+{
+    nk_font_atlas_clear(&sdl.atlas);
+    nk_free(&sdl.ctx);
+    nk_sdl_device_destroy();
+    memset(&sdl, 0, sizeof(sdl));
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nuklear_ui/sfnt.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,273 @@
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include "sfnt.h"
+#include "../util.h"
+
+static uint32_t big32(uint8_t *src)
+{
+	uint32_t ret = *(src++) << 24;
+	ret |= *(src++) << 16;
+	ret |= *(src++) << 8;
+	ret |= *src;
+	return ret;
+}
+
+static uint32_t big16(uint8_t *src)
+{
+	uint32_t ret = *(src++) << 8;
+	ret |= *src;
+	return ret;
+}
+
+#define MIN_RESOURCE_MAP_SIZE (16 + 12 + 2 + 8)
+
+sfnt_container *load_sfnt(uint8_t *buffer, uint32_t size)
+{
+	if (size < 0x100) {
+		return NULL;
+	}
+	uint32_t sfnt_res_count, sfnt_res_offset, res_offset;
+	uint8_t type;
+	if (!memcmp(buffer, "true", 4) || !memcmp(buffer, "OTTO", 4) || !memcmp(buffer, "typ1", 4) || !memcmp(buffer, "\0\x01\0\0", 4)) {
+		type = CONTAINER_TTF;
+	} else if (!memcmp(buffer, "ttcf", 4)) {
+		type = CONTAINER_TTC;
+	} else {
+		static uint8_t all_zeroes[16];
+		uint32_t resource_map_off = big32(buffer + 4);
+		if (resource_map_off + MIN_RESOURCE_MAP_SIZE > size) {
+			return NULL;
+		}
+		//first 16 bytes of map should match header or be all zeroes
+		if (memcmp(buffer, buffer + resource_map_off, 16) && memcmp(all_zeroes, buffer + resource_map_off, 16)) {
+			return NULL;
+		}
+		uint32_t type_start_off = resource_map_off + big16(buffer + resource_map_off + 24);
+		if (type_start_off + sizeof(uint16_t) > size) {
+			return NULL;
+		}
+		uint32_t num_types = 1 + big16(buffer + type_start_off);
+		if (type_start_off + sizeof(uint16_t) + 8 * num_types > size) {
+			return NULL;
+		}
+		res_offset = big32(buffer);
+		if (res_offset > size) {
+			return NULL;
+		}
+		uint8_t *cur = buffer + type_start_off + 2;
+		sfnt_res_count = 0;
+		for (uint32_t i = 0; i < num_types; i++, cur += 8)
+		{
+			if (!memcmp("sfnt", cur, 4)) {
+				sfnt_res_count = 1 + big16(cur + 4);
+				sfnt_res_offset = type_start_off + big16(cur + 6);
+				if (sfnt_res_offset + sfnt_res_count * 12 > size) {
+					return NULL;
+				}
+				type = CONTAINER_DFONT;
+				break;
+			}
+		}
+		if (!sfnt_res_count) {
+			//No "sfnt" resources in this dfont
+			return NULL;
+		}
+	}
+	sfnt_container *sfnt = calloc(1, sizeof(sfnt_container));
+	sfnt->blob = buffer;
+	sfnt->size = size;
+	sfnt->container_type = type;
+	switch (type)
+	{
+	case CONTAINER_TTF:
+		sfnt->num_fonts = 1;
+		sfnt->tables = calloc(1, sizeof(sfnt_table));
+		sfnt->tables->container = sfnt;
+		sfnt->tables->data = buffer + 0xC;
+		sfnt->tables->num_entries = big16(buffer + 4);
+		sfnt->tables->offset = 0;
+		break;
+	case CONTAINER_TTC: {
+		sfnt->num_fonts = big32(buffer+8);
+		sfnt->tables = calloc(sfnt->num_fonts, sizeof(sfnt_table));
+		uint8_t *offsets = buffer + 0xC;
+		for (int i = 0; i < sfnt->num_fonts; i++, offsets += sizeof(uint32_t))
+		{
+			uint32_t offset = big32(offsets);
+			sfnt->tables[i].data = buffer + offset + 0xC;
+			sfnt->tables[i].container = sfnt;
+			sfnt->tables[i].num_entries = big16(buffer + offset + 4);
+			sfnt->tables[i].offset = 0;
+		}
+		break;
+	}
+	case CONTAINER_DFONT:{
+		sfnt->num_fonts = sfnt_res_count;
+		sfnt->tables = calloc(sfnt->num_fonts, sizeof(sfnt_table));
+		uint8_t *cur = buffer + sfnt_res_offset;
+		for (int i = 0; i < sfnt->num_fonts; i++, cur += 12)
+		{
+			uint32_t offset = res_offset + (big32(cur + 4) & 0xFFFFFF);
+			if (offset + 4 > size) {
+				sfnt->tables[i].num_entries = 0;
+				sfnt->tables[i].data = NULL;
+				continue;
+			}
+			uint32_t res_size = big32(buffer + offset);
+			if (offset + 4 + res_size > size || res_size < 0xC) {
+				sfnt->tables[i].num_entries = 0;
+				sfnt->tables[i].data = NULL;
+				continue;
+			}
+			sfnt->tables[i].container = sfnt;
+			sfnt->tables[i].data = buffer + offset + 4 + 0xC;
+			sfnt->tables[i].num_entries = big16(buffer + offset + 4 + 4);
+			sfnt->tables[i].offset = offset + 4;
+		}
+		break;
+	}
+	}
+	return sfnt;
+}
+
+uint8_t *sfnt_find_table(sfnt_table *sfnt, char *table, uint32_t *size_out)
+{
+	uint8_t *entry = sfnt->data;
+	for (int i = 0; i < sfnt->num_entries; i++, entry += 16)
+	{
+		if (!strncmp(entry, table, 4)) {
+			if (size_out) {
+				*size_out = big32(entry + 12);
+			}
+			return sfnt->container->blob + sfnt->offset + big32(entry + 8);
+		}
+	}
+	return NULL;
+}
+
+char *sfnt_name(sfnt_table *sfnt, uint16_t name_type)
+{
+	uint32_t name_size;
+	uint8_t *name_table = sfnt_find_table(sfnt, "name", &name_size);
+	if (!name_table) {
+		return NULL;
+	}
+	uint16_t num_names = big16(name_table + 2);
+	if ((6 + num_names *12) > name_size) {
+		//count is too big for the name table size, abort
+		return NULL;
+	}
+	uint8_t *entry = name_table + 6;
+	uint16_t name_length = 0, name_offset;
+	uint8_t *unicode_entry = NULL, *macroman_entry = NULL, *winunicode_entry = NULL;
+	for (uint16_t i = 0; i < num_names; i++, entry += 12)
+	{
+		if (big16(entry + 6) != name_type) {
+			continue;
+		}
+		uint16_t language_id = big16(entry + 4);
+		if (language_id >= 0x8000) {
+			//ingore language tag records
+			continue;
+		}
+		uint16_t platform_id = big16(entry);
+		if (platform_id == 0) {
+			//prefer Unicode first
+			unicode_entry = entry;
+			break;
+		} else if (platform_id == 3 && big16(entry + 2) < 2) {
+			if (!winunicode_entry || (language_id & 0xFF) == 0x09) {
+				winunicode_entry = entry;
+			}
+		} else if (platform_id == 1 && big16(entry + 2) == 0) {
+			if (!macroman_entry || (language_id == 0)) {
+				macroman_entry = entry;
+			}
+		}
+	}
+	entry = unicode_entry ? unicode_entry : winunicode_entry ? winunicode_entry : macroman_entry;
+	if (entry) {
+		name_length = big16(entry + 8);
+		name_offset = big16(entry + 10);
+	}
+	if (!name_length) {
+		return NULL;
+	}
+	uint32_t full_off = name_offset + big16(name_table + 4);
+	if ((full_off + name_length) > name_size) {
+		return NULL;
+	}
+	if (entry == macroman_entry) {
+		//TODO: convert these properly to UTF-8
+		char *ret = malloc(name_length + 1);
+		memcpy(ret, name_table + full_off, name_length);
+		ret[name_length] = 0;
+		return ret;
+	} else {
+		return utf16be_to_utf8(name_table + full_off, name_length/2);
+	}
+}
+
+uint8_t *sfnt_flatten(sfnt_table *sfnt, uint32_t *size_out)
+{
+	uint8_t *ret = NULL;;
+	sfnt_container *cont = sfnt->container;
+	switch(cont->container_type)
+	{
+	case CONTAINER_TTF:
+		ret = cont->blob;
+		if (size_out) {
+			*size_out = cont->size;
+		}
+		break;
+	case CONTAINER_TTC:
+		memmove(cont->blob, sfnt->data - 0xC, 0xC + sfnt->num_entries * 12);
+		ret = cont->blob;
+		if (size_out) {
+			*size_out = cont->size;
+		}
+		break;
+	case CONTAINER_DFONT:{
+		uint8_t * start = sfnt->data - 0xC;
+		uint32_t size = big32(start - 4);
+		if (size + (start-cont->blob) > cont->size) {
+			size = cont->size - (start-cont->blob);
+		}
+		ret = malloc(size);
+		memcpy(ret, start, size);
+		free(cont->blob);
+		if (size_out) {
+			*size_out = size;
+		}
+		break;
+	}
+	}
+	free(cont->tables);
+	free(cont);
+	return ret;
+}
+
+sfnt_table *sfnt_subfamily_by_names(sfnt_container *sfnt, const char **names)
+{
+	for (int i = 0; i < sfnt->num_fonts; i++)
+	{
+		for (const char **name = names; *name; name++)
+		{
+			char *font_subfam = sfnt_name(sfnt->tables + i, SFNT_SUBFAMILY);
+			if (font_subfam && !strcasecmp(*name, font_subfam)) {
+				free(font_subfam);
+				return sfnt->tables + i;
+			}
+			free(font_subfam);
+		}
+	}
+	return NULL;
+}
+
+void sfnt_free(sfnt_container *sfnt)
+{
+	free(sfnt->tables);
+	free(sfnt->blob);
+	free(sfnt);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nuklear_ui/sfnt.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,44 @@
+#ifndef SFNT_H_
+#define SFNT_H_
+
+#include <stdint.h>
+enum {
+	CONTAINER_TTF,
+	CONTAINER_TTC,
+	CONTAINER_DFONT
+};
+
+enum {
+	SFNT_COPYRIGHT,
+	SFNT_FAMILY,
+	SFNT_SUBFAMILY,
+	SFNT_SUBFAMILY_UNIQUE,
+	SFNT_FULL_NAME,
+	SFNT_VERSION,
+	SFNT_POSTSCRIPT,
+	//TODO: add the rest of the name IDs
+};
+
+typedef struct sfnt_container sfnt_container;
+typedef struct {
+	uint8_t        *data;
+	sfnt_container *container;
+	uint32_t       offset;
+	uint16_t       num_entries;
+} sfnt_table;
+
+struct sfnt_container {
+	uint8_t    *blob;
+	sfnt_table *tables;
+	uint32_t   size;
+	uint8_t    num_fonts;
+	uint8_t    container_type;
+};
+
+sfnt_container *load_sfnt(uint8_t *buffer, uint32_t size);
+char *sfnt_name(sfnt_table *sfnt, uint16_t name_type);
+uint8_t *sfnt_flatten(sfnt_table *sfnt, uint32_t *size_out);
+sfnt_table *sfnt_subfamily_by_names(sfnt_container *sfnt, const char **names);
+void sfnt_free(sfnt_container *sfnt);
+
+#endif // SFNT_H_
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/paths.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,143 @@
+#include <string.h>
+#include <stdlib.h>
+#include "blastem.h"
+#include "util.h"
+
+static char **current_path;
+
+static void persist_path(void)
+{
+	char const *parts[] = {get_userdata_dir(), PATH_SEP, "sticky_path"};
+	char *pathfname = alloc_concat_m(3, parts);
+	FILE *f = fopen(pathfname, "wb");
+	if (f) {
+		if (fwrite(*current_path, 1, strlen(*current_path), f) != strlen(*current_path)) {
+			warning("Failed to save menu path");
+		}
+		fclose(f);
+	} else {
+		warning("Failed to save menu path: Could not open %s for writing\n", pathfname);
+		
+	}
+	free(pathfname);
+}
+
+#ifdef __ANDROID__
+#include <SDL.h>
+#include <jni.h>
+static char *get_external_storage_path()
+{
+	static char *ret;
+	if (ret) {
+		return ret;
+	}
+	JNIEnv *env = SDL_AndroidGetJNIEnv();
+	if ((*env)->PushLocalFrame(env, 8) < 0) {
+		return NULL;
+	}
+
+	jclass Environment = (*env)->FindClass(env, "android/os/Environment");
+	jmethodID getExternalStorageDirectory =
+		(*env)->GetStaticMethodID(env, Environment, "getExternalStorageDirectory", "()Ljava/io/File;");
+	jobject file = (*env)->CallStaticObjectMethod(env, Environment, getExternalStorageDirectory);
+	if (!file) {
+		goto cleanup;
+	}
+
+	jmethodID getAbsolutePath = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, file),
+		"getAbsolutePath", "()Ljava/lang/String;");
+	jstring path = (*env)->CallObjectMethod(env, file, getAbsolutePath);
+
+	char const *tmp = (*env)->GetStringUTFChars(env, path, NULL);
+	ret = strdup(tmp);
+	(*env)->ReleaseStringUTFChars(env, path, tmp);
+
+cleanup:
+	(*env)->PopLocalFrame(env, NULL);
+	return ret;
+}
+#endif
+
+void get_initial_browse_path(char **dst)
+{
+	*dst = NULL;
+	char *remember_path = tern_find_path(config, "ui\0remember_path\0", TVAL_PTR).ptrval;
+	if (!remember_path || !strcmp("on", remember_path)) {
+		char const *parts[] = {get_userdata_dir(), PATH_SEP, "sticky_path"};
+		char *pathfname = alloc_concat_m(3, parts);
+		FILE *f = fopen(pathfname, "rb");
+		if (f) {
+			long pathsize = file_size(f);
+			if (pathsize > 0) {
+				*dst = malloc(pathsize + 1);
+				if (fread(*dst, 1, pathsize, f) != pathsize) {
+					warning("Error restoring saved file browser path");
+					free(*dst);
+					*dst = NULL;
+				} else {
+					(*dst)[pathsize] = 0;
+				}
+			}
+			fclose(f);
+		}
+		free(pathfname);
+		if (!current_path) {
+			atexit(persist_path);
+			current_path = dst;
+		}
+	}
+	if (!*dst) {
+		*dst = tern_find_path(config, "ui\0initial_path\0", TVAL_PTR).ptrval;
+	}
+	if (!*dst){
+#ifdef __ANDROID__
+		*dst = get_external_storage_path();
+#else
+		*dst = "$HOME";
+#endif
+	}
+	tern_node *vars = tern_insert_ptr(NULL, "HOME", get_home_dir());
+	vars = tern_insert_ptr(vars, "EXEDIR", get_exe_dir());
+	*dst = replace_vars(*dst, vars, 1);
+	tern_free(vars);
+}
+
+char *path_append(const char *base, const char *suffix)
+{
+	if (!strcmp(suffix, "..")) {
+#ifdef _WIN32
+		//handle transition from root of a drive to virtual root
+		if (base[1] == ':' && !base[2]) {
+			return strdup(PATH_SEP);
+		}
+#endif
+		size_t len = strlen(base);
+		while (len > 0) {
+			--len;
+			if (is_path_sep(base[len])) {
+				if (!len) {
+					//special handling for /
+					len++;
+				}
+				char *ret = malloc(len+1);
+				memcpy(ret, base, len);
+				ret[len] = 0;
+				return ret;
+			}
+		}
+		return strdup(PATH_SEP);
+	} else {
+#ifdef _WIN32
+		if (base[0] == PATH_SEP[0] && !base[1]) {
+			//handle transition from virtual root to root of a drive
+			return strdup(suffix);
+		}
+#endif
+		if (is_path_sep(base[strlen(base) - 1])) {
+			return alloc_concat(base, suffix);
+		} else {
+			char const *pieces[] = {base, PATH_SEP, suffix};
+			return alloc_concat_m(3, pieces);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/paths.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,7 @@
+#ifndef PATHS_H_
+#define PATHS_H_
+
+void get_initial_browse_path(char **dst);
+char *path_append(const char *base, const char *suffix);
+
+#endif //PATHS_H_
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/png.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,447 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "zlib/zlib.h"
+
+static const char png_magic[] = {0x89, 'P', 'N', 'G', '\r', '\n', 0x1A, '\n'};
+static const char ihdr[] = {'I', 'H', 'D', 'R'};
+static const char plte[] = {'P', 'L', 'T', 'E'};
+static const char idat[] = {'I', 'D', 'A', 'T'};
+static const char iend[] = {'I', 'E', 'N', 'D'};
+
+enum {
+	COLOR_GRAY,
+	COLOR_TRUE = 2,
+	COLOR_INDEXED,
+	COLOR_GRAY_ALPHA,
+	COLOR_TRUE_ALPHA=6
+};
+
+static void write_chunk(FILE *f, const char*id, uint8_t *buffer, uint32_t size)
+{
+	uint8_t tmp[4] = {size >> 24, size >> 16, size >> 8, size};
+	uint8_t warn = 0;
+	warn = warn || (sizeof(tmp) != fwrite(tmp, 1, sizeof(tmp), f));
+	warn = warn || (4 != fwrite(id, 1, 4, f));
+	if (size) {
+		warn = warn || (size != fwrite(buffer, 1, size, f));
+	}
+	
+	uint32_t crc = crc32(0, NULL, 0);
+	crc = crc32(crc, id, 4);
+	if (size) {
+		crc = crc32(crc, buffer, size);
+	}
+	tmp[0] = crc >> 24;
+	tmp[1] = crc >> 16;
+	tmp[2] = crc >> 8;
+	tmp[3] = crc;
+	warn = warn || (sizeof(tmp) != fwrite(tmp, 1, sizeof(tmp), f));
+	if (warn) {
+		fprintf(stderr, "Failure during write of %c%c%c%c chunk\n", id[0], id[1], id[2], id[3]);
+	}
+}
+
+static void write_header(FILE *f, uint32_t width, uint32_t height, uint8_t color_type)
+{
+	uint8_t chunk[13] = {
+		width >> 24, width >> 16, width >> 8, width,
+		height >> 24, height >> 16, height >> 8, height,
+		8, color_type, 0, 0, 0
+	};
+	if (sizeof(png_magic) != fwrite(png_magic, 1, sizeof(png_magic), f)) {
+		fputs("Error writing PNG magic\n", stderr);
+	}
+	write_chunk(f, ihdr, chunk, sizeof(chunk));
+}
+
+void save_png24(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch)
+{
+	uint32_t idat_size = (1 + width*3) * height;
+	uint8_t *idat_buffer = malloc(idat_size);
+	uint32_t *pixel = buffer;
+	uint8_t *cur = idat_buffer;
+	for (uint32_t y = 0; y < height; y++)
+	{
+		//save filter type
+		*(cur++) = 0;
+		uint32_t *start = pixel;
+		for (uint32_t x = 0; x < width; x++, pixel++)
+		{
+			uint32_t value = *pixel;
+			*(cur++) = value >> 16;
+			*(cur++) = value >> 8;
+			*(cur++) = value;
+		}
+		pixel = start + pitch / sizeof(uint32_t);
+	}
+	write_header(f, width, height, COLOR_TRUE);
+	uLongf compress_buffer_size = idat_size + 5 * (idat_size/16383 + 1) + 3;
+	uint8_t *compressed = malloc(compress_buffer_size);
+	compress(compressed, &compress_buffer_size, idat_buffer, idat_size);
+	free(idat_buffer);
+	write_chunk(f, idat, compressed, compress_buffer_size);
+	write_chunk(f, iend, NULL, 0);
+	free(compressed);
+}
+
+void save_png(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch)
+{
+	uint32_t palette[256];
+	uint8_t pal_buffer[256*3];
+	uint32_t num_pal = 0;
+	uint32_t index_size = (1 + width) * height;
+	uint8_t *index_buffer = malloc(index_size);
+	uint8_t *cur = index_buffer;
+	uint32_t *pixel = buffer;
+	for (uint32_t y = 0; y < height; y++)
+	{
+		//save filter type
+		*(cur++) = 0;
+		uint32_t *start = pixel;
+		for (uint32_t x = 0; x < width; x++, pixel++, cur++)
+		{
+			uint32_t value = (*pixel) & 0xFFFFFF;
+			uint32_t i;
+			for (i = 0; i < num_pal; i++)
+			{
+				if (palette[i] == value) {
+					break;
+				}
+			}
+			if (i == num_pal) {
+				if (num_pal == 256) {
+					free(index_buffer);
+					save_png24(f, buffer, width, height, pitch);
+					return;
+				}
+				palette[i] = value;
+				num_pal++;
+			}
+			*cur = i;
+		}
+		pixel = start + pitch / sizeof(uint32_t);
+	}
+	write_header(f, width, height, COLOR_INDEXED);
+	cur = pal_buffer;
+	for (uint32_t i = 0; i < num_pal; i++)
+	{
+		*(cur++) = palette[i] >> 16;
+		*(cur++) = palette[i] >> 8;
+		*(cur++) = palette[i];
+	}
+	write_chunk(f, plte, pal_buffer, num_pal * 3);
+	uLongf compress_buffer_size = index_size + 5 * (index_size/16383 + 1) + 3;
+	uint8_t *compressed = malloc(compress_buffer_size);
+	compress(compressed, &compress_buffer_size, index_buffer, index_size);
+	free(index_buffer);
+	write_chunk(f, idat, compressed, compress_buffer_size);
+	write_chunk(f, iend, NULL, 0);
+	free(compressed);
+}
+
+typedef uint8_t (*filter_fun)(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x);
+typedef uint32_t (*pixel_fun)(uint8_t **cur, uint8_t **last, uint8_t bpp, uint32_t x, filter_fun);
+
+static uint8_t filter_none(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x)
+{
+	return *cur;
+}
+
+static uint8_t filter_sub(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x)
+{
+	if (x) {
+		return *cur + *(cur - bpp);
+	} else {
+		return *cur;
+	}
+}
+
+static uint8_t filter_up(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x)
+{
+	if (last) {
+		return *cur + *last;
+	} else {
+		return *cur;
+	}
+}
+
+static uint8_t filter_avg(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x)
+{
+	uint8_t prev = x ? *(cur - bpp) : 0;
+	uint8_t prior = last ? *last : 0;
+	return *cur + ((prev + prior) >> 1);
+}
+
+static uint8_t paeth(uint8_t a, uint8_t b, uint8_t c)
+{
+	int32_t p = a + b - c;
+	int32_t pa = abs(p - a);
+	int32_t pb = abs(p - b);
+	int32_t pc = abs(p - c);
+	if (pa <= pb && pa <= pc) {
+		return a;
+	}
+	if (pb <= pc) {
+		return b;
+	}
+	return c;
+}
+
+static uint8_t filter_paeth(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x)
+{
+	uint8_t prev, prev_prior;
+	if (x) {
+		prev = *(cur - bpp);
+		prev_prior = *(last - bpp);
+	} else {
+		prev = prev_prior = 0;
+	}
+	uint8_t prior = last ? *last : 0;
+	return *cur + paeth(prev, prior, prev_prior);
+}
+
+static uint32_t pixel_gray(uint8_t **cur, uint8_t **last, uint8_t bpp, uint32_t x, filter_fun filter)
+{
+	uint8_t value = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	return 0xFF000000 | value << 16 | value << 8 | value;
+}
+
+static uint32_t pixel_true(uint8_t **cur, uint8_t **last, uint8_t bpp, uint32_t x, filter_fun filter)
+{
+	uint8_t red = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	uint8_t green = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	uint8_t blue = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	return 0xFF000000 | red << 16 | green << 8 | blue;
+}
+
+static uint32_t pixel_gray_alpha(uint8_t **cur, uint8_t **last, uint8_t bpp, uint32_t x, filter_fun filter)
+{
+	uint8_t value = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	uint8_t alpha = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	return alpha << 24 | value << 16 | value << 8 | value;
+}
+
+static uint32_t pixel_true_alpha(uint8_t **cur, uint8_t **last, uint8_t bpp, uint32_t x, filter_fun filter)
+{
+	uint8_t red = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	uint8_t green = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	uint8_t blue = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	uint8_t alpha = filter(*cur, *last, bpp, x);
+	(*cur)++;
+	if (*last) {
+		(*last)++;
+	}
+	return alpha << 24 | red << 16 | green << 8 | blue;
+}
+
+static filter_fun filters[] = {filter_none, filter_sub, filter_up, filter_avg, filter_paeth};
+
+#define MIN_CHUNK_SIZE 12
+#define MIN_IHDR_SIZE 0xD
+#define MAX_SUPPORTED_DIM 32767 //chosen to avoid possibility of overflow when calculating uncompressed size
+uint32_t *load_png(uint8_t *buffer, uint32_t buf_size, uint32_t *width, uint32_t *height)
+{
+	if (buf_size < sizeof(png_magic) || memcmp(buffer, png_magic, sizeof(png_magic))) {
+		return NULL;
+	}
+	uint32_t cur = sizeof(png_magic);
+	uint8_t has_header = 0;
+	uint8_t bits, color_type, comp_type, filter_type, interlace;
+	uint8_t *idat_buf = NULL;
+	uint8_t idat_needs_free = 0;
+	uint32_t idat_size;
+	uint32_t *out = NULL;
+	uint32_t *palette = NULL;
+	while(cur + MIN_CHUNK_SIZE <= buf_size)
+	{
+		uint32_t chunk_size = buffer[cur++] << 24;
+		chunk_size |=  buffer[cur++] << 16;
+		chunk_size |=  buffer[cur++] << 8;
+		chunk_size |=  buffer[cur++];
+		if (!memcmp(ihdr, buffer + cur, sizeof(ihdr))) {
+			if (chunk_size < MIN_IHDR_SIZE || cur + MIN_IHDR_SIZE > buf_size) {
+				return NULL;
+			}
+			cur += sizeof(ihdr);
+			*width = buffer[cur++] << 24;
+			*width |=  buffer[cur++] << 16;
+			*width |=  buffer[cur++] << 8;
+			*width |=  buffer[cur++];
+			*height = buffer[cur++] << 24;
+			*height |=  buffer[cur++] << 16;
+			*height |=  buffer[cur++] << 8;
+			*height |=  buffer[cur++];
+			if (*width > MAX_SUPPORTED_DIM || *height > MAX_SUPPORTED_DIM) {
+				return NULL;
+			}
+			bits = buffer[cur++];
+			if (bits != 8) {
+				//only support 8-bits per element for now
+				return NULL;
+			}
+			color_type = buffer[cur++];
+			if (color_type > COLOR_TRUE_ALPHA || color_type == 1 || color_type == 5) {
+				//reject invalid color type
+				return NULL;
+			}
+			comp_type = buffer[cur++];
+			if (comp_type) {
+				//only compression type 0 is defined by the spec
+				return NULL;
+			}
+			filter_type = buffer[cur++];
+			interlace = buffer[cur++];
+			if (interlace) {
+				//interlacing not supported for now
+				return NULL;
+			}
+			cur += chunk_size - MIN_IHDR_SIZE;
+			has_header = 1;
+		} else {
+			if (!has_header) {
+				//IHDR is required to be the first chunk, fail if it isn't
+				break;
+			}
+			if (!memcmp(plte, buffer + cur, sizeof(plte))) {
+				//TODO: implement paletted images
+			} else if (!memcmp(idat, buffer + cur, sizeof(idat))) {
+				cur += sizeof(idat);
+				if (idat_buf) {
+					if (idat_needs_free) {
+						idat_buf = realloc(idat_buf, idat_size + chunk_size);
+					} else {
+						uint8_t *tmp = idat_buf;
+						idat_buf = malloc(idat_size + chunk_size);
+						memcpy(idat_buf, tmp, idat_size);
+					}
+					memcpy(idat_buf + idat_size, buffer + cur, chunk_size);
+					idat_size += chunk_size;
+				} else {
+					idat_buf = buffer + cur;
+					idat_size = chunk_size;
+				}
+				cur += chunk_size;
+			} else if (!memcmp(iend, buffer + cur, sizeof(iend))) {
+				if (!idat_buf) {
+					break;
+				}
+				if (!palette && color_type == COLOR_INDEXED) {
+					//indexed color, but no PLTE chunk found
+					return NULL;
+				}
+				uLongf uncompressed_size = *width * *height;
+				uint8_t bpp;
+				pixel_fun pixel;
+				switch (color_type)
+				{
+				case COLOR_GRAY:
+					uncompressed_size *= bits / 8;
+					bpp = bits/8;
+					pixel = pixel_gray;
+					break;
+				case COLOR_TRUE:
+					uncompressed_size *= 3 * bits / 8;
+					bpp = 3 * bits/8;
+					pixel = pixel_true;
+					break;
+				case COLOR_INDEXED: {
+					uint32_t pixels_per_byte = 8 / bits;
+					uncompressed_size = (*width / pixels_per_byte) * *height;
+					if (*width % pixels_per_byte) {
+						uncompressed_size += *height;
+					}
+					bpp = 1;
+					break;
+				}
+				case COLOR_GRAY_ALPHA:
+					uncompressed_size *= bits / 4;
+					bpp = bits / 4;
+					pixel = pixel_gray_alpha;
+					break;
+				case COLOR_TRUE_ALPHA:
+					uncompressed_size *= bits / 2;
+					bpp = bits / 2;
+					pixel = pixel_true_alpha;
+					break;
+				}
+				//add filter type byte
+				uncompressed_size += *height;
+				uint8_t *decomp_buffer = malloc(uncompressed_size);
+				if (Z_OK != uncompress(decomp_buffer, &uncompressed_size, idat_buf, idat_size)) {
+					free(decomp_buffer);
+					break;
+				}
+				out = calloc(*width * *height, sizeof(uint32_t));
+				uint32_t *cur_pixel = out;
+				uint8_t *cur_byte = decomp_buffer;
+				uint8_t *last_line = NULL;
+				for (uint32_t y = 0; y < *height; y++)
+				{
+					uint8_t filter_type = *(cur_byte++);
+					if (filter_type >= sizeof(filters)/sizeof(*filters)) {
+						free(out);
+						out = NULL;
+						free(decomp_buffer);
+						break;
+					}
+					filter_fun filter = filters[filter_type];
+					uint8_t *line_start = cur_byte;
+					for (uint32_t x = 0; x < *width; x++)
+					{
+						*(cur_pixel++) = pixel(&cur_byte, &last_line, bpp, x, filter);
+					}
+					last_line = line_start;
+				}
+				free(decomp_buffer);
+			} else {
+				//skip uncrecognized chunks
+				cur += 4 + chunk_size;
+			}
+		}
+		//skip CRC for now
+		cur += sizeof(uint32_t);
+	}
+	if (idat_needs_free) {
+		free(idat_buf);
+	}
+	free(palette);
+	return out;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/png.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,8 @@
+#ifndef PNG_H_
+#define PNG_H_
+
+void save_png24(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch);
+void save_png(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch);
+uint32_t *load_png(uint8_t *buffer, uint32_t buf_size, uint32_t *width, uint32_t *height);
+
+#endif //PNG_H_
--- a/psg.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/psg.c	Tue Dec 25 11:12:26 2018 -0800
@@ -10,19 +10,11 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <math.h>
-void psg_init(psg_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t samples_frame, uint32_t lowpass_cutoff)
+void psg_init(psg_context * context, uint32_t master_clock, uint32_t clock_div)
 {
 	memset(context, 0, sizeof(*context));
-	context->audio_buffer = malloc(sizeof(*context->audio_buffer) * samples_frame);
-	context->back_buffer = malloc(sizeof(*context->audio_buffer) * samples_frame);
+	context->audio = render_audio_source(master_clock, clock_div, 1);
 	context->clock_inc = clock_div;
-	context->sample_rate = sample_rate;
-	context->samples_frame = samples_frame;
-	double rc = (1.0 / (double)lowpass_cutoff) / (2.0 * M_PI);
-	double dt = 1.0 / ((double)master_clock / (double)clock_div);
-	double alpha = dt / (dt + rc);
-	context->lowpass_alpha = (int32_t)(((double)0x10000) * alpha);
-	psg_adjust_master_clock(context, master_clock);
 	for (int i = 0; i < 4; i++) {
 		context->volume[i] = 0xF;
 	}
@@ -30,19 +22,13 @@
 
 void psg_free(psg_context *context)
 {
-	free(context->audio_buffer);
-	//TODO: Figure out how to make this 100% safe
-	//audio thread could still be using this
-	free(context->back_buffer);
+	render_free_source(context->audio);
 	free(context);
 }
 
-#define BUFFER_INC_RES 0x40000000UL
-
 void psg_adjust_master_clock(psg_context * context, uint32_t master_clock)
 {
-	uint64_t old_inc = context->buffer_inc;
-	context->buffer_inc = ((BUFFER_INC_RES * (uint64_t)context->sample_rate) / (uint64_t)master_clock) * (uint64_t)context->clock_inc;
+	render_audio_adjust_clock(context->audio, master_clock, context->clock_inc);
 }
 
 void psg_write(psg_context * context, uint8_t value)
@@ -120,33 +106,19 @@
 			}
 		}
 
-		context->last_sample = context->accum;
-		context->accum = 0;
+		int16_t accum = 0;
 		
 		for (int i = 0; i < 3; i++) {
 			if (context->output_state[i]) {
-				context->accum += volume_table[context->volume[i]];
+				accum += volume_table[context->volume[i]];
 			}
 		}
 		if (context->noise_out) {
-			context->accum += volume_table[context->volume[3]];
+			accum += volume_table[context->volume[3]];
 		}
-		int32_t tmp = context->accum * context->lowpass_alpha + context->last_sample * (0x10000 - context->lowpass_alpha);
-		context->accum = tmp >> 16;
+		
+		render_put_mono_sample(context->audio, accum);
 
-		context->buffer_fraction += context->buffer_inc;
-		while (context->buffer_fraction >= BUFFER_INC_RES) {
-			context->buffer_fraction -= BUFFER_INC_RES;
-			int32_t tmp = context->last_sample * ((context->buffer_fraction << 16) / context->buffer_inc);
-			tmp += context->accum * (0x10000 - ((context->buffer_fraction << 16) / context->buffer_inc));
-			context->audio_buffer[context->buffer_pos++] = tmp >> 16;
-			
-			if (context->buffer_pos == context->samples_frame) {
-				if (!headless) {
-					render_wait_psg(context);
-				}
-			}
-		}
 		context->cycles += context->clock_inc;
 	}
 }
--- a/psg.h	Sun Dec 31 10:11:16 2017 -0800
+++ b/psg.h	Tue Dec 25 11:12:26 2018 -0800
@@ -8,23 +8,15 @@
 
 #include <stdint.h>
 #include "serialize.h"
+#include "render.h"
 
 typedef struct {
-	int16_t  *audio_buffer;
-	int16_t  *back_buffer;
-	uint64_t buffer_fraction;
-	uint64_t buffer_inc;
-	uint32_t buffer_pos;
+	audio_source *audio;
 	uint32_t clock_inc;
 	uint32_t cycles;
-	uint32_t sample_rate;
-	uint32_t samples_frame;
-	int32_t lowpass_alpha;
 	uint16_t lsfr;
 	uint16_t counter_load[4];
 	uint16_t counters[4];
-	int16_t  accum;
-	int16_t  last_sample;
 	uint8_t  volume[4];
 	uint8_t  output_state[4];
 	uint8_t  noise_out;
@@ -34,7 +26,7 @@
 } psg_context;
 
 
-void psg_init(psg_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t samples_frame, uint32_t lowpass_cutoff);
+void psg_init(psg_context * context, uint32_t master_clock, uint32_t clock_div);
 void psg_free(psg_context *context);
 void psg_adjust_master_clock(psg_context * context, uint32_t master_clock);
 void psg_write(psg_context * context, uint8_t value);
--- a/render.h	Sun Dec 31 10:11:16 2017 -0800
+++ b/render.h	Tue Dec 25 11:12:26 2018 -0800
@@ -40,6 +40,22 @@
 #define RENDERKEY_PLAY     SDLK_AUDIOPLAY
 #define RENDERKEY_SEARCH   SDLK_AC_SEARCH
 #define RENDERKEY_BACK     SDLK_AC_BACK
+#define RENDERKEY_NP0      SDLK_KP_0
+#define RENDERKEY_NP1      SDLK_KP_1
+#define RENDERKEY_NP2      SDLK_KP_2
+#define RENDERKEY_NP3      SDLK_KP_3
+#define RENDERKEY_NP4      SDLK_KP_4
+#define RENDERKEY_NP5      SDLK_KP_5
+#define RENDERKEY_NP6      SDLK_KP_6
+#define RENDERKEY_NP7      SDLK_KP_7
+#define RENDERKEY_NP8      SDLK_KP_8
+#define RENDERKEY_NP9      SDLK_KP_9
+#define RENDERKEY_NP_DIV   SDLK_KP_DIVIDE
+#define RENDERKEY_NP_MUL   SDLK_KP_MULTIPLY
+#define RENDERKEY_NP_MIN   SDLK_KP_MINUS
+#define RENDERKEY_NP_PLUS  SDLK_KP_PLUS
+#define RENDERKEY_NP_ENTER SDLK_KP_ENTER
+#define RENDERKEY_NP_STOP  SDLK_KP_PERIOD
 #define RENDER_DPAD_UP     SDL_HAT_UP
 #define RENDER_DPAD_DOWN   SDL_HAT_DOWN
 #define RENDER_DPAD_LEFT   SDL_HAT_LEFT
@@ -52,10 +68,9 @@
 
 #define FRAMEBUFFER_ODD 0
 #define FRAMEBUFFER_EVEN 1
+#define FRAMEBUFFER_USER_START 2
 
 #include "vdp.h"
-#include "psg.h"
-#include "ym2612.h"
 
 typedef enum {
 	VID_NTSC,
@@ -69,21 +84,22 @@
 #define RENDER_NOT_MAPPED -2
 #define RENDER_NOT_PLUGGED_IN -3
 
+typedef struct audio_source audio_source;
 typedef void (*drop_handler)(const char *filename);
 
 uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b);
 void render_save_screenshot(char *path);
+uint8_t render_create_window(char *caption, uint32_t width, uint32_t height);
+void render_destroy_window(uint8_t which);
 uint32_t *render_get_framebuffer(uint8_t which, int *pitch);
 void render_framebuffer_updated(uint8_t which, int width);
+//returns the framebuffer index associated with the Window that has focus
+uint8_t render_get_active_framebuffer(void);
 void render_init(int width, int height, char * title, uint8_t fullscreen);
 void render_set_video_standard(vid_std std);
 void render_toggle_fullscreen();
 void render_update_caption(char *title);
 void render_wait_quit(vdp_context * context);
-void render_wait_psg(psg_context * context);
-void render_wait_ym(ym2612_context * context);
-void render_disable_ym();
-void render_enable_ym();
 uint32_t render_audio_buffer();
 uint32_t render_sample_rate();
 void process_events();
@@ -96,6 +112,7 @@
 int32_t render_dpad_part(int32_t input);
 int32_t render_axis_part(int32_t input);
 uint8_t render_direction_part(int32_t input);
+char* render_joystick_type_id(int index);
 void render_errorbox(char *title, char *message);
 void render_warnbox(char *title, char *message);
 void render_infobox(char *title, char *message);
@@ -103,6 +120,17 @@
 uint32_t render_emulated_height();
 uint32_t render_overscan_top();
 uint32_t render_overscan_left();
+uint32_t render_elapsed_ms(void);
+void render_sleep_ms(uint32_t delay);
+uint8_t render_has_gl(void);
+audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels);
+void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider);
+void render_put_mono_sample(audio_source *src, int16_t value);
+void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right);
+void render_pause_source(audio_source *src);
+void render_resume_source(audio_source *src);
+void render_free_source(audio_source *src);
+void render_config_updated(void);
 
 #endif //RENDER_H_
 
--- a/render_sdl.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/render_sdl.c	Tue Dec 25 11:12:26 2018 -0800
@@ -8,11 +8,15 @@
 #include <string.h>
 #include <math.h>
 #include "render.h"
+#include "render_sdl.h"
 #include "blastem.h"
 #include "genesis.h"
-#include "io.h"
+#include "bindings.h"
 #include "util.h"
 #include "ppm.h"
+#include "png.h"
+#include "config.h"
+#include "controller_info.h"
 
 #ifndef DISABLE_OPENGL
 #include <GL/glew.h>
@@ -21,7 +25,9 @@
 #define MAX_EVENT_POLL_PER_FRAME 2
 
 static SDL_Window *main_window;
+static SDL_Window **extra_windows;
 static SDL_Renderer *main_renderer;
+static SDL_Renderer **extra_renderers;
 static SDL_Texture  **sdl_textures;
 static uint8_t num_textures;
 static SDL_Rect      main_clip;
@@ -34,72 +40,193 @@
 
 static uint32_t last_frame = 0;
 
-static int16_t * current_psg = NULL;
-static int16_t * current_ym = NULL;
-
 static uint32_t buffer_samples, sample_rate;
 static uint32_t missing_count;
 
 static SDL_mutex * audio_mutex;
 static SDL_cond * audio_ready;
-static SDL_cond * psg_cond;
-static SDL_cond * ym_cond;
 static uint8_t quitting = 0;
-static uint8_t ym_enabled = 1;
+
+struct audio_source {
+	SDL_cond *cond;
+	int16_t  *front;
+	int16_t  *back;
+	double   dt;
+	uint64_t buffer_fraction;
+	uint64_t buffer_inc;
+	uint32_t buffer_pos;
+	uint32_t read_start;
+	uint32_t read_end;
+	uint32_t lowpass_alpha;
+	uint32_t mask;
+	int16_t  last_left;
+	int16_t  last_right;
+	uint8_t  num_channels;
+	uint8_t  front_populated;
+};
+
+static audio_source *audio_sources[8];
+static audio_source *inactive_audio_sources[8];
+static uint8_t num_audio_sources;
+static uint8_t num_inactive_audio_sources;
+static uint8_t sync_to_audio;
+static uint32_t min_buffered;
+
+typedef int32_t (*mix_func)(audio_source *audio, void *vstream, int len);
+
+static int32_t mix_s16(audio_source *audio, void *vstream, int len)
+{
+	int samples = len/(sizeof(int16_t)*2);
+	int16_t *stream = vstream;
+	int16_t *end = stream + 2*samples;
+	int16_t *src = audio->front;
+	uint32_t i = audio->read_start;
+	uint32_t i_end = audio->read_end;
+	int16_t *cur = stream;
+	if (audio->num_channels == 1) {
+		while (cur < end && i != i_end)
+		{
+			*(cur++) += src[i];
+			*(cur++) += src[i++];
+			i &= audio->mask;
+		}
+	} else {
+		while (cur < end && i != i_end)
+		{
+			*(cur++) += src[i++];
+			*(cur++) += src[i++];
+			i &= audio->mask;
+		}
+	}
+	
+	if (cur != end) {
+		printf("Underflow of %d samples, read_start: %d, read_end: %d, mask: %X\n", (int)(end-cur)/2, audio->read_start, audio->read_end, audio->mask);
+	}
+	if (!sync_to_audio) {
+		audio->read_start = i;
+	}
+	if (cur != end) {
+		//printf("Underflow of %d samples, read_start: %d, read_end: %d, mask: %X\n", (int)(end-cur)/2, audio->read_start, audio->read_end, audio->mask);
+		return (cur-end)/2;
+	} else {
+		return ((i_end - i) & audio->mask) / audio->num_channels;
+	}
+}
+
+static int32_t mix_f32(audio_source *audio, void *vstream, int len)
+{
+	int samples = len/(sizeof(float)*2);
+	float *stream = vstream;
+	float *end = stream + 2*samples;
+	int16_t *src = audio->front;
+	uint32_t i = audio->read_start;
+	uint32_t i_end = audio->read_end;
+	float *cur = stream;
+	if (audio->num_channels == 1) {
+		while (cur < end && i != i_end)
+		{
+			*(cur++) += ((float)src[i]) / 0x7FFF;
+			*(cur++) += ((float)src[i++]) / 0x7FFF;
+			i &= audio->mask;
+		}
+	} else {
+		while(cur < end && i != i_end)
+		{
+			*(cur++) += ((float)src[i++]) / 0x7FFF;
+			*(cur++) += ((float)src[i++]) / 0x7FFF;
+			i &= audio->mask;
+		}
+	}
+	if (!sync_to_audio) {
+		audio->read_start = i;
+	}
+	if (cur != end) {
+		printf("Underflow of %d samples, read_start: %d, read_end: %d, mask: %X\n", (int)(end-cur)/2, audio->read_start, audio->read_end, audio->mask);
+		return (cur-end)/2;
+	} else {
+		return ((i_end - i) & audio->mask) / audio->num_channels;
+	}
+}
+
+static int32_t mix_null(audio_source *audio, void *vstream, int len)
+{
+	return 0;
+}
+
+static mix_func mix;
 
 static void audio_callback(void * userdata, uint8_t *byte_stream, int len)
 {
-	//puts("audio_callback");
-	int16_t * stream = (int16_t *)byte_stream;
-	int samples = len/(sizeof(int16_t)*2);
-	int16_t * psg_buf, * ym_buf;
-	uint8_t local_quit;
+	uint8_t num_populated;
+	memset(byte_stream, 0, len);
 	SDL_LockMutex(audio_mutex);
-		psg_buf = NULL;
-		ym_buf = NULL;
 		do {
-			if (!psg_buf) {
-				psg_buf = current_psg;
-				current_psg = NULL;
-				SDL_CondSignal(psg_cond);
+			num_populated = 0;
+			for (uint8_t i = 0; i < num_audio_sources; i++)
+			{
+				if (audio_sources[i]->front_populated) {
+					num_populated++;
+				}
 			}
-			if (ym_enabled && !ym_buf) {
-				ym_buf = current_ym;
-				current_ym = NULL;
-				SDL_CondSignal(ym_cond);
-			}
-			if (!quitting && (!psg_buf || (ym_enabled && !ym_buf))) {
+			if (!quitting && num_populated < num_audio_sources) {
+				fflush(stdout);
 				SDL_CondWait(audio_ready, audio_mutex);
 			}
-		} while(!quitting && (!psg_buf || (ym_enabled && !ym_buf)));
-
-		local_quit = quitting;
-	SDL_UnlockMutex(audio_mutex);
-	if (!local_quit) {
-		if (ym_enabled) {
-			for (int i = 0; i < samples; i++)
+		} while(!quitting && num_populated < num_audio_sources);
+		if (!quitting) {
+			for (uint8_t i = 0; i < num_audio_sources; i++)
 			{
-				*(stream++) = psg_buf[i] + *(ym_buf++);
-				*(stream++) = psg_buf[i] + *(ym_buf++);
-			}
-		} else {
-			for (int i = 0; i < samples; i++)
-			{
-				*(stream++) = psg_buf[i];
-				*(stream++) = psg_buf[i];
+				mix(audio_sources[i], byte_stream, len);
+				audio_sources[i]->front_populated = 0;
+				SDL_CondSignal(audio_sources[i]->cond);
 			}
 		}
+	SDL_UnlockMutex(audio_mutex);
+}
+
+#define NO_LAST_BUFFERED -2000000000
+static int32_t last_buffered = NO_LAST_BUFFERED;
+static float average_change;
+#define BUFFER_FRAMES_THRESHOLD 6
+#define BASE_MAX_ADJUST 0.0125
+static float max_adjust;
+static int32_t cur_min_buffered;
+static uint32_t min_remaining_buffer;
+static void audio_callback_drc(void *userData, uint8_t *byte_stream, int len)
+{
+	memset(byte_stream, 0, len);
+	if (cur_min_buffered < 0) {
+		//underflow last frame, but main thread hasn't gotten a chance to call SDL_PauseAudio yet
+		return;
+	}
+	cur_min_buffered = 0x7FFFFFFF;
+	min_remaining_buffer = 0xFFFFFFFF;
+	for (uint8_t i = 0; i < num_audio_sources; i++)
+	{
+		
+		int32_t buffered = mix(audio_sources[i], byte_stream, len);
+		cur_min_buffered = buffered < cur_min_buffered ? buffered : cur_min_buffered;
+		uint32_t remaining = (audio_sources[i]->mask + 1)/audio_sources[i]->num_channels - buffered;
+		min_remaining_buffer = remaining < min_remaining_buffer ? remaining : min_remaining_buffer;
 	}
 }
 
-void render_disable_ym()
+static void lock_audio()
 {
-	ym_enabled = 0;
+	if (sync_to_audio) {
+		SDL_LockMutex(audio_mutex);
+	} else {
+		SDL_LockAudio();
+	}
 }
 
-void render_enable_ym()
+static void unlock_audio()
 {
-	ym_enabled = 1;
+	if (sync_to_audio) {
+		SDL_UnlockMutex(audio_mutex);
+	} else {
+		SDL_UnlockAudio();
+	}
 }
 
 static void render_close_audio()
@@ -111,6 +238,184 @@
 	SDL_CloseAudio();
 }
 
+#define BUFFER_INC_RES 0x40000000UL
+
+void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider)
+{
+	src->buffer_inc = ((BUFFER_INC_RES * (uint64_t)sample_rate) / master_clock) * sample_divider;
+}
+
+audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels)
+{
+	audio_source *ret = NULL;
+	uint32_t alloc_size = sync_to_audio ? channels * buffer_samples : nearest_pow2(min_buffered * 4 * channels);
+	lock_audio();
+		if (num_audio_sources < 8) {
+			ret = malloc(sizeof(audio_source));
+			ret->back = malloc(alloc_size * sizeof(int16_t));
+			ret->front = sync_to_audio ? malloc(alloc_size * sizeof(int16_t)) : ret->back;
+			ret->front_populated = 0;
+			ret->cond = SDL_CreateCond();
+			ret->num_channels = channels;
+			audio_sources[num_audio_sources++] = ret;
+		}
+	unlock_audio();
+	if (!ret) {
+		fatal_error("Too many audio sources!");
+	} else {
+		render_audio_adjust_clock(ret, master_clock, sample_divider);
+		double lowpass_cutoff = get_lowpass_cutoff(config);
+		double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI);
+		ret->dt = 1.0 / ((double)master_clock / (double)(sample_divider));
+		double alpha = ret->dt / (ret->dt + rc);
+		ret->lowpass_alpha = (int32_t)(((double)0x10000) * alpha);
+		ret->buffer_pos = 0;
+		ret->buffer_fraction = 0;
+		ret->last_left = ret->last_right = 0;
+		ret->read_start = 0;
+		ret->read_end = sync_to_audio ? buffer_samples * channels : 0;
+		ret->mask = sync_to_audio ? 0xFFFFFFFF : alloc_size-1;
+	}
+	if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) {
+		SDL_PauseAudio(0);
+	}
+	return ret;
+}
+
+void render_pause_source(audio_source *src)
+{
+	uint8_t need_pause = 0;
+	lock_audio();
+		for (uint8_t i = 0; i < num_audio_sources; i++)
+		{
+			if (audio_sources[i] == src) {
+				audio_sources[i] = audio_sources[--num_audio_sources];
+				if (sync_to_audio) {
+					SDL_CondSignal(audio_ready);
+				}
+				break;
+			}
+		}
+		if (!num_audio_sources) {
+			need_pause = 1;
+		}
+	unlock_audio();
+	if (need_pause) {
+		SDL_PauseAudio(1);
+	}
+	inactive_audio_sources[num_inactive_audio_sources++] = src;
+}
+
+void render_resume_source(audio_source *src)
+{
+	lock_audio();
+		if (num_audio_sources < 8) {
+			audio_sources[num_audio_sources++] = src;
+		}
+	unlock_audio();
+	for (uint8_t i = 0; i < num_inactive_audio_sources; i++)
+	{
+		if (inactive_audio_sources[i] == src) {
+			inactive_audio_sources[i] = inactive_audio_sources[--num_inactive_audio_sources];
+		}
+	}
+	if (sync_to_audio) {
+		SDL_PauseAudio(0);
+	}
+}
+
+void render_free_source(audio_source *src)
+{
+	render_pause_source(src);
+	
+	free(src->front);
+	if (sync_to_audio) {
+		free(src->back);
+		SDL_DestroyCond(src->cond);
+	}
+	free(src);
+}
+static uint32_t sync_samples;
+static void do_audio_ready(audio_source *src)
+{
+	if (sync_to_audio) {
+		SDL_LockMutex(audio_mutex);
+			while (src->front_populated) {
+				SDL_CondWait(src->cond, audio_mutex);
+			}
+			int16_t *tmp = src->front;
+			src->front = src->back;
+			src->back = tmp;
+			src->front_populated = 1;
+			src->buffer_pos = 0;
+			SDL_CondSignal(audio_ready);
+		SDL_UnlockMutex(audio_mutex);
+	} else {
+		uint32_t num_buffered;
+		SDL_LockAudio();
+			src->read_end = src->buffer_pos;
+			num_buffered = ((src->read_end - src->read_start) & src->mask) / src->num_channels;
+		SDL_UnlockAudio();
+		if (num_buffered >= min_buffered && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) {
+			SDL_PauseAudio(0);
+		}
+	}
+}
+
+static int16_t lowpass_sample(audio_source *src, int16_t last, int16_t current)
+{
+	int32_t tmp = current * src->lowpass_alpha + last * (0x10000 - src->lowpass_alpha);
+	current = tmp >> 16;
+	return current;
+}
+
+static void interp_sample(audio_source *src, int16_t last, int16_t current)
+{
+	int64_t tmp = last * ((src->buffer_fraction << 16) / src->buffer_inc);
+	tmp += current * (0x10000 - ((src->buffer_fraction << 16) / src->buffer_inc));
+	src->back[src->buffer_pos++] = tmp >> 16;
+}
+
+void render_put_mono_sample(audio_source *src, int16_t value)
+{
+	value = lowpass_sample(src, src->last_left, value);
+	src->buffer_fraction += src->buffer_inc;
+	uint32_t base = sync_to_audio ? 0 : src->read_end;
+	while (src->buffer_fraction > BUFFER_INC_RES)
+	{
+		src->buffer_fraction -= BUFFER_INC_RES;
+		interp_sample(src, src->last_left, value);
+		
+		if (((src->buffer_pos - base) & src->mask) >= sync_samples) {
+			do_audio_ready(src);
+		}
+		src->buffer_pos &= src->mask;
+	}
+	src->last_left = value;
+}
+
+void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right)
+{
+	left = lowpass_sample(src, src->last_left, left);
+	right = lowpass_sample(src, src->last_right, right);
+	src->buffer_fraction += src->buffer_inc;
+	uint32_t base = sync_to_audio ? 0 : src->read_end;
+	while (src->buffer_fraction > BUFFER_INC_RES)
+	{
+		src->buffer_fraction -= BUFFER_INC_RES;
+		
+		interp_sample(src, src->last_left, left);
+		interp_sample(src, src->last_right, right);
+		
+		if (((src->buffer_pos - base) & src->mask)/2 >= sync_samples) {
+			do_audio_ready(src);
+		}
+		src->buffer_pos &= src->mask;
+	}
+	src->last_left = left;
+	src->last_right = right;
+}
+
 static SDL_Joystick * joysticks[MAX_JOYSTICKS];
 static int joystick_sdl_index[MAX_JOYSTICKS];
 
@@ -239,12 +544,20 @@
 	un_height = glGetUniformLocation(program, "height");
 	at_pos = glGetAttribLocation(program, "pos");
 }
+
+static void gl_teardown()
+{
+	glDeleteProgram(program);
+	glDeleteShader(vshader);
+	glDeleteShader(fshader);
+	glDeleteBuffers(2, buffers);
+	glDeleteTextures(3, textures);
+}
 #endif
 
+static uint8_t texture_init;
 static void render_alloc_surfaces()
 {
-	static uint8_t texture_init;
-
 	if (texture_init) {
 		return;
 	}
@@ -267,18 +580,32 @@
 #endif
 }
 
+static void free_surfaces(void)
+{
+	for (int i = 0; i < num_textures; i++)
+	{
+		if (sdl_textures[i]) {
+			SDL_DestroyTexture(sdl_textures[i]);
+		}
+	}
+	free(sdl_textures);
+	sdl_textures = NULL;
+	texture_init = 0;
+}
+
 static char * caption = NULL;
 static char * fps_caption = NULL;
 
 static void render_quit()
 {
 	render_close_audio();
-	for (int i = 0; i < num_textures; i++)
-	{
-		if (sdl_textures[i]) {
-			SDL_DestroyTexture(sdl_textures[i]);
-		}
+	free_surfaces();
+#ifndef DISABLE_OPENGL
+	if (render_gl) {
+		gl_teardown();
+		SDL_GL_DeleteContext(main_context);
 	}
+#endif
 }
 
 static float config_aspect()
@@ -338,520 +665,11 @@
 	}
 }
 
-static uint32_t overscan_top[NUM_VID_STD] = {2, 21};
-static uint32_t overscan_bot[NUM_VID_STD] = {1, 17};
-static uint32_t overscan_left[NUM_VID_STD] = {13, 13};
-static uint32_t overscan_right[NUM_VID_STD] = {14, 14};
-static vid_std video_standard = VID_NTSC;
-static char *vid_std_names[NUM_VID_STD] = {"ntsc", "pal"};
-void render_init(int width, int height, char * title, uint8_t fullscreen)
-{
-	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
-		fatal_error("Unable to init SDL: %s\n", SDL_GetError());
-	}
-	atexit(SDL_Quit);
-	if (height <= 0) {
-		float aspect = config_aspect() > 0.0f ? config_aspect() : 4.0f/3.0f;
-		height = ((float)width / aspect) + 0.5f;
-	}
-	printf("width: %d, height: %d\n", width, height);
-	windowed_width = width;
-	windowed_height = height;
-	
-	uint32_t flags = SDL_WINDOW_RESIZABLE;
-
-	if (fullscreen) {
-		flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
-		SDL_DisplayMode mode;
-		//TODO: Multiple monitor support
-		SDL_GetCurrentDisplayMode(0, &mode);
-		//the SDL2 migration guide suggests setting width and height to 0 when using SDL_WINDOW_FULLSCREEN_DESKTOP
-		//but that doesn't seem to work right when using OpenGL, at least on Linux anyway
-		width = mode.w;
-		height = mode.h;
-	}
-	main_width = width;
-	main_height = height;
-	is_fullscreen = fullscreen;
-
-	render_gl = 0;
-	tern_val def = {.ptrval = "off"};
-	char *vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval;
-	
-	tern_node *video = tern_find_node(config, "video");
-	if (video)
-	{
-		for (int i = 0; i < NUM_VID_STD; i++)
-		{
-			tern_node *std_settings = tern_find_node(video, vid_std_names[i]);
-			if (std_settings) {
-				char *val = tern_find_path_default(std_settings, "overscan\0top\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
-				if (val) {
-					overscan_top[i] = atoi(val);
-				}
-				val = tern_find_path_default(std_settings, "overscan\0bottom\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
-				if (val) {
-					overscan_bot[i] = atoi(val);
-				}
-				val = tern_find_path_default(std_settings, "overscan\0left\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
-				if (val) {
-					overscan_left[i] = atoi(val);
-				}
-				val = tern_find_path_default(std_settings, "overscan\0right\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
-				if (val) {
-					overscan_right[i] = atoi(val);
-				}
-			}
-		}
-	}
-
-#ifndef DISABLE_OPENGL
-	char *gl_enabled_str = tern_find_path_default(config, "video\0gl\0", def, TVAL_PTR).ptrval;
-	uint8_t gl_enabled = strcmp(gl_enabled_str, "off") != 0;
-	if (gl_enabled)
-	{
-		flags |= SDL_WINDOW_OPENGL;
-		SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
-		SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
-		SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
-		SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
-		SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
-	}
-#endif
-	main_window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags);
-	if (!main_window) {
-		fatal_error("Unable to create SDL window: %s\n", SDL_GetError());
-	}
-#ifndef DISABLE_OPENGL
-	if (gl_enabled)
-	{
-		main_context = SDL_GL_CreateContext(main_window);
-		GLenum res = glewInit();
-		if (res != GLEW_OK) {
-			warning("Initialization of GLEW failed with code %d\n", res);
-		}
-
-		if (res == GLEW_OK && GLEW_VERSION_2_0) {
-			render_gl = 1;
-			if (!strcmp("tear", vsync)) {
-				if (SDL_GL_SetSwapInterval(-1) < 0) {
-					warning("late tear is not available (%s), using normal vsync\n", SDL_GetError());
-					vsync = "on";
-				} else {
-					vsync = NULL;
-				}
-			}
-			if (vsync) {
-				if (SDL_GL_SetSwapInterval(!strcmp("on", vsync)) < 0) {
-					warning("Failed to set vsync to %s: %s\n", vsync, SDL_GetError());
-				}
-			}
-		} else {
-			warning("OpenGL 2.0 is unavailable, falling back to SDL2 renderer\n");
-		}
-	}
-	if (!render_gl) {
-#endif
-		flags = SDL_RENDERER_ACCELERATED;
-		if (!strcmp("on", vsync) || !strcmp("tear", vsync)) {
-			flags |= SDL_RENDERER_PRESENTVSYNC;
-		}
-		main_renderer = SDL_CreateRenderer(main_window, -1, flags);
-
-		if (!main_renderer) {
-			fatal_error("unable to create SDL renderer: %s\n", SDL_GetError());
-		}
-		main_clip.x = main_clip.y = 0;
-		main_clip.w = width;
-		main_clip.h = height;
-#ifndef DISABLE_OPENGL
-	}
-#endif
-
-	SDL_GetWindowSize(main_window, &main_width, &main_height);
-	printf("Window created with size: %d x %d\n", main_width, main_height);
-	update_aspect();
-	render_alloc_surfaces();
-	def.ptrval = "off";
-	scanlines = !strcmp(tern_find_path_default(config, "video\0scanlines\0", def, TVAL_PTR).ptrval, "on");
-
-	caption = title;
-
-	audio_mutex = SDL_CreateMutex();
-	psg_cond = SDL_CreateCond();
-	ym_cond = SDL_CreateCond();
-	audio_ready = SDL_CreateCond();
-
-	SDL_AudioSpec desired, actual;
-    char * rate_str = tern_find_path(config, "audio\0rate\0", TVAL_PTR).ptrval;
-   	int rate = rate_str ? atoi(rate_str) : 0;
-   	if (!rate) {
-   		rate = 48000;
-   	}
-    desired.freq = rate;
-	desired.format = AUDIO_S16SYS;
-	desired.channels = 2;
-    char * samples_str = tern_find_path(config, "audio\0buffer\0", TVAL_PTR).ptrval;
-   	int samples = samples_str ? atoi(samples_str) : 0;
-   	if (!samples) {
-   		samples = 512;
-   	}
-    printf("config says: %d\n", samples);
-    desired.samples = samples*2;
-	desired.callback = audio_callback;
-	desired.userdata = NULL;
-
-	if (SDL_OpenAudio(&desired, &actual) < 0) {
-		fatal_error("Unable to open SDL audio: %s\n", SDL_GetError());
-	}
-	buffer_samples = actual.samples;
-	sample_rate = actual.freq;
-	printf("Initialized audio at frequency %d with a %d sample buffer\n", actual.freq, actual.samples);
-	SDL_PauseAudio(0);
-	
-	uint32_t db_size;
-	char *db_data = read_bundled_file("gamecontrollerdb.txt", &db_size);
-	if (db_data) {
-		int added = SDL_GameControllerAddMappingsFromRW(SDL_RWFromMem(db_data, db_size), 1);
-		free(db_data);
-		printf("Added %d game controller mappings from gamecontrollerdb.txt\n", added);
-	}
-	
-	SDL_JoystickEventState(SDL_ENABLE);
-
-	atexit(render_quit);
-}
-
-void render_set_video_standard(vid_std std)
-{
-	video_standard = std;
-}
-
-void render_update_caption(char *title)
-{
-	caption = title;
-	free(fps_caption);
-	fps_caption = NULL;
-}
-
-static char *screenshot_path;
-void render_save_screenshot(char *path)
-{
-	if (screenshot_path) {
-		free(screenshot_path);
-	}
-	screenshot_path = path;
-}
-
-uint32_t *locked_pixels;
-uint32_t locked_pitch;
-uint32_t *render_get_framebuffer(uint8_t which, int *pitch)
-{
-#ifndef DISABLE_OPENGL
-	if (render_gl && which <= FRAMEBUFFER_EVEN) {
-		*pitch = LINEBUF_SIZE * sizeof(uint32_t);
-		return texture_buf;
-	} else {
-#endif
-		if (which >= num_textures) {
-			warning("Request for invalid framebuffer number %d\n", which);
-			return NULL;
-		}
-		void *pixels;
-		if (SDL_LockTexture(sdl_textures[which], NULL, &pixels, pitch) < 0) {
-			warning("Failed to lock texture: %s\n", SDL_GetError());
-			return NULL;
-		}
-		static uint8_t last;
-		if (which <= FRAMEBUFFER_EVEN) {
-			locked_pixels = pixels;
-			if (which == FRAMEBUFFER_EVEN) {
-				pixels += *pitch;
-			}
-			locked_pitch = *pitch;
-			if (which != last) {
-				*pitch *= 2;
-			}
-			last = which;
-		}
-		return pixels;
-#ifndef DISABLE_OPENGL
-	}
-#endif
-}
-
-uint8_t events_processed;
-#ifdef __ANDROID__
-#define FPS_INTERVAL 10000
-#else
-#define FPS_INTERVAL 1000
-#endif
-
-static uint32_t last_width;
-void render_framebuffer_updated(uint8_t which, int width)
+static ui_render_fun on_context_destroyed, on_context_created;
+void render_set_gl_context_handlers(ui_render_fun destroy, ui_render_fun create)
 {
-	static uint8_t last;
-	last_width = width;
-	uint32_t height = which <= FRAMEBUFFER_EVEN 
-		? (video_standard == VID_NTSC ? 243 : 294) - (overscan_top[video_standard] + overscan_bot[video_standard])
-		: 240;
-	FILE *screenshot_file = NULL;
-	uint32_t shot_height, shot_width;
-	if (screenshot_path && which == FRAMEBUFFER_ODD) {
-		screenshot_file = fopen(screenshot_path, "wb");
-		if (screenshot_file) {
-			info_message("Saving screenshot to %s\n", screenshot_path);
-		} else {
-			warning("Failed to open screenshot file %s for writing\n", screenshot_path);
-		}
-		free(screenshot_path);
-		screenshot_path = NULL;
-		shot_height = video_standard == VID_NTSC ? 243 : 294;
-		shot_width = width;
-	}
-	width -= overscan_left[video_standard] + overscan_right[video_standard];
-#ifndef DISABLE_OPENGL
-	if (render_gl && which <= FRAMEBUFFER_EVEN) {
-		glBindTexture(GL_TEXTURE_2D, textures[which]);
-		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, LINEBUF_SIZE, height, GL_BGRA, GL_UNSIGNED_BYTE, texture_buf + overscan_left[video_standard] + LINEBUF_SIZE * overscan_top[video_standard]);
-
-		glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-		glClear(GL_COLOR_BUFFER_BIT);
-
-		glUseProgram(program);
-		glActiveTexture(GL_TEXTURE0);
-		glBindTexture(GL_TEXTURE_2D, textures[0]);
-		glUniform1i(un_textures[0], 0);
-
-		glActiveTexture(GL_TEXTURE1);
-		glBindTexture(GL_TEXTURE_2D, textures[last != which ? 1 : scanlines ? 2 : 0]);
-		glUniform1i(un_textures[1], 1);
-
-		glUniform1f(un_width, width);
-		glUniform1f(un_height, height);
-
-		glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
-		glVertexAttribPointer(at_pos, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat[2]), (void *)0);
-		glEnableVertexAttribArray(at_pos);
-
-		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
-		glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, (void *)0);
-
-		glDisableVertexAttribArray(at_pos);
-
-		SDL_GL_SwapWindow(main_window);
-		
-		if (screenshot_file) {
-			//properly supporting interlaced modes here is non-trivial, so only save the odd field for now
-			save_ppm(screenshot_file, texture_buf, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t));
-		}
-	} else {
-#endif
-		if (which <= FRAMEBUFFER_EVEN && last != which) {
-			uint8_t *cur_dst = (uint8_t *)locked_pixels;
-			uint8_t *cur_saved = (uint8_t *)texture_buf;
-			uint32_t dst_off = which == FRAMEBUFFER_EVEN ? 0 : locked_pitch;
-			uint32_t src_off = which == FRAMEBUFFER_EVEN ? locked_pitch : 0;
-			for (int i = 0; i < height; ++i)
-			{
-				//copy saved line from other field
-				memcpy(cur_dst + dst_off, cur_saved, locked_pitch);
-				//save line from this field to buffer for next frame
-				memcpy(cur_saved, cur_dst + src_off, locked_pitch);
-				cur_dst += locked_pitch * 2;
-				cur_saved += locked_pitch;
-			}
-			height = 480;
-		}
-		if (screenshot_file) {
-			uint32_t shot_pitch = locked_pitch;
-			if (which == FRAMEBUFFER_EVEN) {
-				shot_height *= 2;
-			} else {
-				shot_pitch *= 2;
-			}
-			save_ppm(screenshot_file, locked_pixels, shot_width, shot_height, shot_pitch);
-		}
-		SDL_UnlockTexture(sdl_textures[which]);
-		SDL_Rect src_clip = {
-			.x = overscan_left[video_standard],
-			.y = overscan_top[video_standard],
-			.w = width,
-			.h = height
-		};
-		SDL_SetRenderDrawColor(main_renderer, 0, 0, 0, 255);
-		SDL_RenderClear(main_renderer);
-		SDL_RenderCopy(main_renderer, sdl_textures[which], &src_clip, &main_clip);
-		SDL_RenderPresent(main_renderer);
-#ifndef DISABLE_OPENGL
-	}
-#endif
-	if (screenshot_file) {
-		fclose(screenshot_file);
-	}
-	if (which <= FRAMEBUFFER_EVEN) {
-		last = which;
-		static uint32_t frame_counter, start;
-		frame_counter++;
-		last_frame= SDL_GetTicks();
-		if ((last_frame - start) > FPS_INTERVAL) {
-			if (start && (last_frame-start)) {
-	#ifdef __ANDROID__
-				info_message("%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0));
-	#else
-				if (!fps_caption) {
-					fps_caption = malloc(strlen(caption) + strlen(" - 100000000.1 fps") + 1);
-				}
-				sprintf(fps_caption, "%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0));
-				SDL_SetWindowTitle(main_window, fps_caption);
-	#endif
-			}
-			start = last_frame;
-			frame_counter = 0;
-		}
-	}
-	if (!events_processed) {
-		process_events();
-	}
-	events_processed = 0;
-}
-
-uint32_t render_emulated_width()
-{
-	return last_width - overscan_left[video_standard] - overscan_right[video_standard];
-}
-
-uint32_t render_emulated_height()
-{
-	return (video_standard == VID_NTSC ? 243 : 294) - overscan_top[video_standard] - overscan_bot[video_standard];
-}
-
-uint32_t render_overscan_left()
-{
-	return overscan_left[video_standard];
-}
-
-uint32_t render_overscan_top()
-{
-	return overscan_top[video_standard];
-}
-
-void render_wait_quit(vdp_context * context)
-{
-	SDL_Event event;
-	while(SDL_WaitEvent(&event)) {
-		switch (event.type) {
-		case SDL_QUIT:
-			return;
-		}
-	}
-}
-
-static int find_joystick_index(SDL_JoystickID instanceID)
-{
-	for (int i = 0; i < MAX_JOYSTICKS; i++) {
-		if (joysticks[i] && SDL_JoystickInstanceID(joysticks[i]) == instanceID) {
-			return i;
-		}
-	}
-	return -1;
-}
-
-static int lowest_unused_joystick_index()
-{
-	for (int i = 0; i < MAX_JOYSTICKS; i++) {
-		if (!joysticks[i]) {
-			return i;
-		}
-	}
-	return -1;
-}
-
-int32_t render_translate_input_name(int32_t controller, char *name, uint8_t is_axis)
-{
-	static tern_node *button_lookup, *axis_lookup;
-	if (controller > MAX_JOYSTICKS || !joysticks[controller]) {
-		return RENDER_NOT_PLUGGED_IN;
-	}
-	
-	if (!SDL_IsGameController(joystick_sdl_index[controller])) {
-		return RENDER_NOT_MAPPED;
-	}
-	SDL_GameController *control = SDL_GameControllerOpen(joystick_sdl_index[controller]);
-	if (!control) {
-		warning("Failed to open game controller %d: %s\n", controller, SDL_GetError());
-		return RENDER_NOT_PLUGGED_IN;
-	}
-	
-	SDL_GameControllerButtonBind cbind;
-	if (is_axis) {
-		if (!axis_lookup) {
-			for (int i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++)
-			{
-				axis_lookup = tern_insert_int(axis_lookup, SDL_GameControllerGetStringForAxis(i), i);
-			}
-			//alternative Playstation-style names
-			axis_lookup = tern_insert_int(axis_lookup, "l2", SDL_CONTROLLER_AXIS_TRIGGERLEFT);
-			axis_lookup = tern_insert_int(axis_lookup, "r2", SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
-		}
-		intptr_t sdl_axis = tern_find_int(axis_lookup, name, SDL_CONTROLLER_AXIS_INVALID);
-		if (sdl_axis == SDL_CONTROLLER_AXIS_INVALID) {
-			SDL_GameControllerClose(control);
-			return RENDER_INVALID_NAME;
-		}
-		cbind = SDL_GameControllerGetBindForAxis(control, sdl_axis);
-	} else {
-		if (!button_lookup) {
-			for (int i = SDL_CONTROLLER_BUTTON_A; i < SDL_CONTROLLER_BUTTON_MAX; i++)
-			{
-				button_lookup = tern_insert_int(button_lookup, SDL_GameControllerGetStringForButton(i), i);
-			}
-			//alternative Playstation-style names
-			button_lookup = tern_insert_int(button_lookup, "cross", SDL_CONTROLLER_BUTTON_A);
-			button_lookup = tern_insert_int(button_lookup, "circle", SDL_CONTROLLER_BUTTON_B);
-			button_lookup = tern_insert_int(button_lookup, "square", SDL_CONTROLLER_BUTTON_X);
-			button_lookup = tern_insert_int(button_lookup, "triangle", SDL_CONTROLLER_BUTTON_Y);
-			button_lookup = tern_insert_int(button_lookup, "share", SDL_CONTROLLER_BUTTON_BACK);
-			button_lookup = tern_insert_int(button_lookup, "select", SDL_CONTROLLER_BUTTON_BACK);
-			button_lookup = tern_insert_int(button_lookup, "options", SDL_CONTROLLER_BUTTON_START);
-			button_lookup = tern_insert_int(button_lookup, "l1", SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
-			button_lookup = tern_insert_int(button_lookup, "r1", SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
-			button_lookup = tern_insert_int(button_lookup, "l3", SDL_CONTROLLER_BUTTON_LEFTSTICK);
-			button_lookup = tern_insert_int(button_lookup, "r3", SDL_CONTROLLER_BUTTON_RIGHTSTICK);
-		}
-		intptr_t sdl_button = tern_find_int(button_lookup, name, SDL_CONTROLLER_BUTTON_INVALID);
-		if (sdl_button == SDL_CONTROLLER_BUTTON_INVALID) {
-			SDL_GameControllerClose(control);
-			return RENDER_INVALID_NAME;
-		}
-		cbind = SDL_GameControllerGetBindForButton(control, sdl_button);
-	}
-	SDL_GameControllerClose(control);
-	switch (cbind.bindType)
-	{
-	case SDL_CONTROLLER_BINDTYPE_BUTTON:
-		return cbind.value.button;
-	case SDL_CONTROLLER_BINDTYPE_AXIS:
-		return RENDER_AXIS_BIT | cbind.value.axis;
-	case SDL_CONTROLLER_BINDTYPE_HAT:
-		return RENDER_DPAD_BIT | (cbind.value.hat.hat << 4) | cbind.value.hat.hat_mask;
-	}
-	return RENDER_NOT_MAPPED;
-}
-
-int32_t render_dpad_part(int32_t input)
-{
-	return input >> 4 & 0xFFFFFF;
-}
-
-uint8_t render_direction_part(int32_t input)
-{
-	return input & 0xF;
-}
-
-int32_t render_axis_part(int32_t input)
-{
-	return input & 0xFFFFFFF;
+	on_context_destroyed = destroy;
+	on_context_created = create;
 }
 
 static uint8_t scancode_map[SDL_NUM_SCANCODES] = {
@@ -964,8 +782,70 @@
 	drag_drop_handler = handler;
 }
 
+static event_handler custom_event_handler;
+void render_set_event_handler(event_handler handler)
+{
+	custom_event_handler = handler;
+}
+
+static int find_joystick_index(SDL_JoystickID instanceID)
+{
+	for (int i = 0; i < MAX_JOYSTICKS; i++) {
+		if (joysticks[i] && SDL_JoystickInstanceID(joysticks[i]) == instanceID) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+static int lowest_unused_joystick_index()
+{
+	for (int i = 0; i < MAX_JOYSTICKS; i++) {
+		if (!joysticks[i]) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+SDL_Joystick *render_get_joystick(int index)
+{
+	if (index >= MAX_JOYSTICKS) {
+		return NULL;
+	}
+	return joysticks[index];
+}
+
+char* render_joystick_type_id(int index)
+{
+	SDL_Joystick *stick = render_get_joystick(index);
+	if (!stick) {
+		return NULL;
+	}
+	char *guid_string = malloc(33);
+	SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(stick), guid_string, 33);
+	return guid_string;
+}
+
+SDL_GameController *render_get_controller(int index)
+{
+	if (index >= MAX_JOYSTICKS) {
+		return NULL;
+	}
+	return SDL_GameControllerOpen(joystick_sdl_index[index]);
+}
+
+static uint32_t overscan_top[NUM_VID_STD] = {2, 21};
+static uint32_t overscan_bot[NUM_VID_STD] = {1, 17};
+static uint32_t overscan_left[NUM_VID_STD] = {13, 13};
+static uint32_t overscan_right[NUM_VID_STD] = {14, 14};
+static vid_std video_standard = VID_NTSC;
+
 static int32_t handle_event(SDL_Event *event)
 {
+	if (custom_event_handler) {
+		custom_event_handler(event);
+	}
 	switch (event->type) {
 	case SDL_KEYDOWN:
 		handle_keydown(event->key.keysym.sym, scancode_map[event->key.keysym.scancode]);
@@ -980,7 +860,7 @@
 		handle_joyup(find_joystick_index(event->jbutton.which), event->jbutton.button);
 		break;
 	case SDL_JOYHATMOTION:
-		handle_joy_dpad(find_joystick_index(event->jbutton.which), event->jhat.hat, event->jhat.value);
+		handle_joy_dpad(find_joystick_index(event->jhat.which), event->jhat.hat, event->jhat.value);
 		break;
 	case SDL_JOYAXISMOTION:
 		handle_joy_axis(find_joystick_index(event->jaxis.which), event->jaxis.axis, event->jaxis.value);
@@ -1028,9 +908,16 @@
 			update_aspect();
 #ifndef DISABLE_OPENGL
 			if (render_gl) {
+				if (on_context_destroyed) {
+					on_context_destroyed();
+				}
+				gl_teardown();
 				SDL_GL_DeleteContext(main_context);
 				main_context = SDL_GL_CreateContext(main_window);
 				gl_setup();
+				if (on_context_created) {
+					on_context_created();
+				}
 			}
 #endif
 			break;
@@ -1058,6 +945,869 @@
 	}
 }
 
+static char *vid_std_names[NUM_VID_STD] = {"ntsc", "pal"};
+static int display_hz;
+static int source_hz;
+static int source_frame;
+static int source_frame_count;
+static int frame_repeat[60];
+
+static void init_audio()
+{
+	SDL_AudioSpec desired, actual;
+    char * rate_str = tern_find_path(config, "audio\0rate\0", TVAL_PTR).ptrval;
+   	int rate = rate_str ? atoi(rate_str) : 0;
+   	if (!rate) {
+   		rate = 48000;
+   	}
+    desired.freq = rate;
+	desired.format = AUDIO_S16SYS;
+	desired.channels = 2;
+    char * samples_str = tern_find_path(config, "audio\0buffer\0", TVAL_PTR).ptrval;
+   	int samples = samples_str ? atoi(samples_str) : 0;
+   	if (!samples) {
+   		samples = 512;
+   	}
+    printf("config says: %d\n", samples);
+    desired.samples = samples*2;
+	desired.callback = sync_to_audio ? audio_callback : audio_callback_drc;
+	desired.userdata = NULL;
+
+	if (SDL_OpenAudio(&desired, &actual) < 0) {
+		fatal_error("Unable to open SDL audio: %s\n", SDL_GetError());
+	}
+	buffer_samples = actual.samples;
+	sample_rate = actual.freq;
+	printf("Initialized audio at frequency %d with a %d sample buffer, ", actual.freq, actual.samples);
+	if (actual.format == AUDIO_S16SYS) {
+		puts("signed 16-bit int format");
+		mix = mix_s16;
+	} else if (actual.format == AUDIO_F32SYS) {
+		puts("32-bit float format");
+		mix = mix_f32;
+	} else {
+		printf("unsupported format %X\n", actual.format);
+		warning("Unsupported audio sample format: %X\n", actual.format);
+		mix = mix_null;
+	}
+}
+
+void window_setup(void)
+{
+	uint32_t flags = SDL_WINDOW_RESIZABLE;
+	if (is_fullscreen) {
+		flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
+	}
+	
+	tern_val def = {.ptrval = "video"};
+	char *sync_src = tern_find_path_default(config, "system\0sync_source\0", def, TVAL_PTR).ptrval;
+	sync_to_audio = !strcmp(sync_src, "audio");
+	
+	const char *vsync;
+	if (sync_to_audio) {
+		def.ptrval = "off";
+		vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval;
+	} else {
+		vsync = "on";
+	}
+	
+	tern_node *video = tern_find_node(config, "video");
+	if (video)
+	{
+		for (int i = 0; i < NUM_VID_STD; i++)
+		{
+			tern_node *std_settings = tern_find_node(video, vid_std_names[i]);
+			if (std_settings) {
+				char *val = tern_find_path_default(std_settings, "overscan\0top\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
+				if (val) {
+					overscan_top[i] = atoi(val);
+				}
+				val = tern_find_path_default(std_settings, "overscan\0bottom\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
+				if (val) {
+					overscan_bot[i] = atoi(val);
+				}
+				val = tern_find_path_default(std_settings, "overscan\0left\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
+				if (val) {
+					overscan_left[i] = atoi(val);
+				}
+				val = tern_find_path_default(std_settings, "overscan\0right\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
+				if (val) {
+					overscan_right[i] = atoi(val);
+				}
+			}
+		}
+	}
+	render_gl = 0;
+	
+#ifndef DISABLE_OPENGL
+	char *gl_enabled_str = tern_find_path_default(config, "video\0gl\0", def, TVAL_PTR).ptrval;
+	uint8_t gl_enabled = strcmp(gl_enabled_str, "off") != 0;
+	if (gl_enabled)
+	{
+		flags |= SDL_WINDOW_OPENGL;
+		SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
+		SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
+		SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
+		SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
+		SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+	}
+#endif
+	main_window = SDL_CreateWindow(caption, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, main_width, main_height, flags);
+	if (!main_window) {
+		fatal_error("Unable to create SDL window: %s\n", SDL_GetError());
+	}
+#ifndef DISABLE_OPENGL
+	if (gl_enabled)
+	{
+		main_context = SDL_GL_CreateContext(main_window);
+		GLenum res = glewInit();
+		if (res != GLEW_OK) {
+			warning("Initialization of GLEW failed with code %d\n", res);
+		}
+
+		if (res == GLEW_OK && GLEW_VERSION_2_0) {
+			render_gl = 1;
+			if (!strcmp("tear", vsync)) {
+				if (SDL_GL_SetSwapInterval(-1) < 0) {
+					warning("late tear is not available (%s), using normal vsync\n", SDL_GetError());
+					vsync = "on";
+				} else {
+					vsync = NULL;
+				}
+			}
+			if (vsync) {
+				if (SDL_GL_SetSwapInterval(!strcmp("on", vsync)) < 0) {
+					warning("Failed to set vsync to %s: %s\n", vsync, SDL_GetError());
+				}
+			}
+		} else {
+			warning("OpenGL 2.0 is unavailable, falling back to SDL2 renderer\n");
+		}
+	}
+	if (!render_gl) {
+#endif
+		flags = SDL_RENDERER_ACCELERATED;
+		if (!strcmp("on", vsync) || !strcmp("tear", vsync)) {
+			flags |= SDL_RENDERER_PRESENTVSYNC;
+		}
+		main_renderer = SDL_CreateRenderer(main_window, -1, flags);
+
+		if (!main_renderer) {
+			fatal_error("unable to create SDL renderer: %s\n", SDL_GetError());
+		}
+		main_clip.x = main_clip.y = 0;
+		main_clip.w = main_width;
+		main_clip.h = main_height;
+#ifndef DISABLE_OPENGL
+	}
+#endif
+
+	SDL_GetWindowSize(main_window, &main_width, &main_height);
+	printf("Window created with size: %d x %d\n", main_width, main_height);
+	update_aspect();
+	render_alloc_surfaces();
+	def.ptrval = "off";
+	scanlines = !strcmp(tern_find_path_default(config, "video\0scanlines\0", def, TVAL_PTR).ptrval, "on");
+}
+
+void render_init(int width, int height, char * title, uint8_t fullscreen)
+{
+	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
+		fatal_error("Unable to init SDL: %s\n", SDL_GetError());
+	}
+	atexit(SDL_Quit);
+	if (height <= 0) {
+		float aspect = config_aspect() > 0.0f ? config_aspect() : 4.0f/3.0f;
+		height = ((float)width / aspect) + 0.5f;
+	}
+	printf("width: %d, height: %d\n", width, height);
+	windowed_width = width;
+	windowed_height = height;
+	
+	SDL_DisplayMode mode;
+	//TODO: Explicit multiple monitor support
+	SDL_GetCurrentDisplayMode(0, &mode);
+	display_hz = mode.refresh_rate;
+
+	if (fullscreen) {
+		//the SDL2 migration guide suggests setting width and height to 0 when using SDL_WINDOW_FULLSCREEN_DESKTOP
+		//but that doesn't seem to work right when using OpenGL, at least on Linux anyway
+		width = mode.w;
+		height = mode.h;
+	}
+	main_width = width;
+	main_height = height;
+	is_fullscreen = fullscreen;
+	
+	caption = title;
+	
+	window_setup();
+
+	audio_mutex = SDL_CreateMutex();
+	audio_ready = SDL_CreateCond();
+	
+	init_audio();
+	
+	uint32_t db_size;
+	char *db_data = read_bundled_file("gamecontrollerdb.txt", &db_size);
+	if (db_data) {
+		int added = SDL_GameControllerAddMappingsFromRW(SDL_RWFromMem(db_data, db_size), 1);
+		free(db_data);
+		printf("Added %d game controller mappings from gamecontrollerdb.txt\n", added);
+	}
+	
+	controller_add_mappings();
+	
+	SDL_JoystickEventState(SDL_ENABLE);
+	
+	render_set_video_standard(VID_NTSC);
+
+	atexit(render_quit);
+}
+#include<unistd.h>
+static int in_toggle;
+static void update_source(audio_source *src, double rc, uint8_t sync_changed)
+{
+	double alpha = src->dt / (src->dt + rc);
+	int32_t lowpass_alpha = (int32_t)(((double)0x10000) * alpha);
+	src->lowpass_alpha = lowpass_alpha;
+	if (sync_changed) {
+		uint32_t alloc_size = sync_to_audio ? src->num_channels * buffer_samples : nearest_pow2(min_buffered * 4 * src->num_channels);
+		src->back = realloc(src->back, alloc_size * sizeof(int16_t));
+		if (sync_to_audio) {
+			src->front = malloc(alloc_size * sizeof(int16_t));
+		} else {
+			free(src->front);
+			src->front = src->back;
+		}
+		src->mask = sync_to_audio ? 0xFFFFFFFF : alloc_size-1;
+		src->read_start = 0;
+		src->read_end = sync_to_audio ? buffer_samples * src->num_channels : 0;
+		src->buffer_pos = 0;
+	}
+}
+
+void render_config_updated(void)
+{
+	uint8_t old_sync_to_audio = sync_to_audio;
+	
+	free_surfaces();
+#ifndef DISABLE_OPENGL
+	if (render_gl) {
+		if (on_context_destroyed) {
+			on_context_destroyed();
+		}
+		gl_teardown();
+		SDL_GL_DeleteContext(main_context);
+	} else {
+#endif
+		SDL_DestroyRenderer(main_renderer);
+#ifndef DISABLE_OPENGL
+	}
+#endif
+	in_toggle = 1;
+	SDL_DestroyWindow(main_window);
+	drain_events();
+	
+	char *config_width = tern_find_path(config, "video\0width\0", TVAL_PTR).ptrval;
+	if (config_width) {
+		windowed_width = atoi(config_width);
+	}
+	char *config_height = tern_find_path(config, "video\0height\0", TVAL_PTR).ptrval;
+	if (config_height) {
+		windowed_height = atoi(config_height);
+	} else {
+		float aspect = config_aspect() > 0.0f ? config_aspect() : 4.0f/3.0f;
+		windowed_height = ((float)windowed_width / aspect) + 0.5f;
+	}
+	char *config_fullscreen = tern_find_path(config, "video\0fullscreen\0", TVAL_PTR).ptrval;
+	is_fullscreen = config_fullscreen && !strcmp("on", config_fullscreen);
+	if (is_fullscreen) {
+		SDL_DisplayMode mode;
+		//TODO: Multiple monitor support
+		SDL_GetCurrentDisplayMode(0, &mode);
+		main_width = mode.w;
+		main_height = mode.h;
+	} else {
+		main_width = windowed_width;
+		main_height = windowed_height;
+	}
+	
+	window_setup();
+	update_aspect();
+#ifndef DISABLE_OPENGL
+	//need to check render_gl again after window_setup as render option could have changed
+	if (render_gl && on_context_created) {
+		on_context_created();
+	}
+#endif
+
+	uint8_t was_paused = SDL_GetAudioStatus() == SDL_AUDIO_PAUSED;
+	render_close_audio();
+	quitting = 0;
+	init_audio();
+	render_set_video_standard(video_standard);
+	
+	double lowpass_cutoff = get_lowpass_cutoff(config);
+	double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI);
+	lock_audio();
+		for (uint8_t i = 0; i < num_audio_sources; i++)
+		{
+			update_source(audio_sources[i], rc, old_sync_to_audio != sync_to_audio);
+		}
+	unlock_audio();
+	for (uint8_t i = 0; i < num_inactive_audio_sources; i++)
+	{
+		update_source(inactive_audio_sources[i], rc, old_sync_to_audio != sync_to_audio);
+	}
+	drain_events();
+	in_toggle = 0;
+	if (!was_paused) {
+		SDL_PauseAudio(0);
+	}
+}
+
+SDL_Window *render_get_window(void)
+{
+	return main_window;
+}
+
+void render_set_video_standard(vid_std std)
+{
+	video_standard = std;
+	source_hz = std == VID_PAL ? 50 : 60;
+	uint32_t max_repeat = 0;
+	if (abs(source_hz - display_hz) < 2) {
+		memset(frame_repeat, 0, sizeof(int)*display_hz);
+	} else {
+		int inc = display_hz * 100000 / source_hz;
+		int accum = 0;
+		int dst_frames = 0;
+		for (int src_frame = 0; src_frame < source_hz; src_frame++)
+		{
+			frame_repeat[src_frame] = -1;
+			accum += inc;
+			while (accum > 100000)
+			{
+				accum -= 100000;
+				frame_repeat[src_frame]++;
+				max_repeat = frame_repeat[src_frame] > max_repeat ? frame_repeat[src_frame] : max_repeat;
+				dst_frames++;
+			}
+		}
+		if (dst_frames != display_hz) {
+			frame_repeat[source_hz-1] += display_hz - dst_frames;
+		}
+	}
+	source_frame = 0;
+	source_frame_count = frame_repeat[0];
+	//sync samples with audio thread approximately every 8 lines
+	sync_samples = sync_to_audio ? buffer_samples : 8 * sample_rate / (source_hz * (VID_PAL ? 313 : 262));
+	max_repeat++;
+	min_buffered = (((float)max_repeat * (float)sample_rate/(float)source_hz)/* / (float)buffer_samples*/);// + 0.9999;
+	//min_buffered *= buffer_samples;
+	printf("Min samples buffered before audio start: %d\n", min_buffered);
+	max_adjust = BASE_MAX_ADJUST / source_hz;
+}
+
+void render_update_caption(char *title)
+{
+	caption = title;
+	free(fps_caption);
+	fps_caption = NULL;
+}
+
+static char *screenshot_path;
+void render_save_screenshot(char *path)
+{
+	if (screenshot_path) {
+		free(screenshot_path);
+	}
+	screenshot_path = path;
+}
+
+uint8_t render_create_window(char *caption, uint32_t width, uint32_t height)
+{
+	uint8_t win_idx = 0xFF;
+	for (int i = 0; i < num_textures - FRAMEBUFFER_USER_START; i++)
+	{
+		if (!extra_windows[i]) {
+			win_idx = i;
+			break;
+		}
+	}
+	
+	if (win_idx == 0xFF) {
+		num_textures++;
+		sdl_textures = realloc(sdl_textures, num_textures * sizeof(*sdl_textures));
+		extra_windows = realloc(extra_windows, (num_textures - FRAMEBUFFER_USER_START) * sizeof(*extra_windows));
+		extra_renderers = realloc(extra_renderers, (num_textures - FRAMEBUFFER_USER_START) * sizeof(*extra_renderers));
+		win_idx = num_textures - FRAMEBUFFER_USER_START - 1;
+	}
+	extra_windows[win_idx] = SDL_CreateWindow(caption, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, 0);
+	if (!extra_windows[win_idx]) {
+		goto fail_window;
+	}
+	extra_renderers[win_idx] = SDL_CreateRenderer(extra_windows[win_idx], -1, SDL_RENDERER_ACCELERATED);
+	if (!extra_renderers[win_idx]) {
+		goto fail_renderer;
+	}
+	uint8_t texture_idx = win_idx + FRAMEBUFFER_USER_START;
+	sdl_textures[texture_idx] = SDL_CreateTexture(extra_renderers[win_idx], SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
+	if (!sdl_textures[texture_idx]) {
+		goto fail_texture;
+	}
+	return texture_idx;
+	
+fail_texture:
+	SDL_DestroyRenderer(extra_renderers[win_idx]);
+fail_renderer:
+	SDL_DestroyWindow(extra_windows[win_idx]);
+fail_window:
+	num_textures--;
+	return 0;
+}
+
+void render_destroy_window(uint8_t which)
+{
+	uint8_t win_idx = which - FRAMEBUFFER_USER_START;
+	//Destroying the renderers also frees the textures
+	SDL_DestroyRenderer(extra_renderers[win_idx]);
+	SDL_DestroyWindow(extra_windows[win_idx]);
+	
+	extra_renderers[win_idx] = NULL;
+	extra_windows[win_idx] = NULL;
+}
+
+uint32_t *locked_pixels;
+uint32_t locked_pitch;
+uint32_t *render_get_framebuffer(uint8_t which, int *pitch)
+{
+#ifndef DISABLE_OPENGL
+	if (render_gl && which <= FRAMEBUFFER_EVEN) {
+		*pitch = LINEBUF_SIZE * sizeof(uint32_t);
+		return texture_buf;
+	} else {
+#endif
+		if (which >= num_textures) {
+			warning("Request for invalid framebuffer number %d\n", which);
+			return NULL;
+		}
+		void *pixels;
+		if (SDL_LockTexture(sdl_textures[which], NULL, &pixels, pitch) < 0) {
+			warning("Failed to lock texture: %s\n", SDL_GetError());
+			return NULL;
+		}
+		static uint8_t last;
+		if (which <= FRAMEBUFFER_EVEN) {
+			locked_pixels = pixels;
+			if (which == FRAMEBUFFER_EVEN) {
+				pixels += *pitch;
+			}
+			locked_pitch = *pitch;
+			if (which != last) {
+				*pitch *= 2;
+			}
+			last = which;
+		}
+		return pixels;
+#ifndef DISABLE_OPENGL
+	}
+#endif
+}
+
+uint8_t events_processed;
+#ifdef __ANDROID__
+#define FPS_INTERVAL 10000
+#else
+#define FPS_INTERVAL 1000
+#endif
+
+static uint32_t last_width, last_height;
+static uint8_t interlaced;
+void render_framebuffer_updated(uint8_t which, int width)
+{
+	static uint8_t last;
+	if (!sync_to_audio && which <= FRAMEBUFFER_EVEN && source_frame_count < 0) {
+		source_frame++;
+		if (source_frame >= source_hz) {
+			source_frame = 0;
+		}
+		source_frame_count = frame_repeat[source_frame];
+		//TODO: Figure out what to do about SDL Render API texture locking
+		return;
+	}
+	
+	last_width = width;
+	uint32_t height = which <= FRAMEBUFFER_EVEN 
+		? (video_standard == VID_NTSC ? 243 : 294) - (overscan_top[video_standard] + overscan_bot[video_standard])
+		: 240;
+	FILE *screenshot_file = NULL;
+	uint32_t shot_height, shot_width;
+	char *ext;
+	if (screenshot_path && which == FRAMEBUFFER_ODD) {
+		screenshot_file = fopen(screenshot_path, "wb");
+		if (screenshot_file) {
+#ifndef DISABLE_ZLIB
+			ext = path_extension(screenshot_path);
+#endif
+			info_message("Saving screenshot to %s\n", screenshot_path);
+		} else {
+			warning("Failed to open screenshot file %s for writing\n", screenshot_path);
+		}
+		free(screenshot_path);
+		screenshot_path = NULL;
+		shot_height = video_standard == VID_NTSC ? 243 : 294;
+		shot_width = width;
+	}
+	interlaced = last != which;
+	width -= overscan_left[video_standard] + overscan_right[video_standard];
+#ifndef DISABLE_OPENGL
+	if (render_gl && which <= FRAMEBUFFER_EVEN) {
+		SDL_GL_MakeCurrent(main_window, main_context);
+		glBindTexture(GL_TEXTURE_2D, textures[which]);
+		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, LINEBUF_SIZE, height, GL_BGRA, GL_UNSIGNED_BYTE, texture_buf + overscan_left[video_standard] + LINEBUF_SIZE * overscan_top[video_standard]);
+		
+		if (screenshot_file) {
+			//properly supporting interlaced modes here is non-trivial, so only save the odd field for now
+#ifndef DISABLE_ZLIB
+			if (!strcasecmp(ext, "png")) {
+				free(ext);
+				save_png(screenshot_file, texture_buf, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t));
+			} else {
+				free(ext);
+#endif
+				save_ppm(screenshot_file, texture_buf, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t));
+#ifndef DISABLE_ZLIB
+			}
+#endif
+		}
+	} else {
+#endif
+		if (which <= FRAMEBUFFER_EVEN && last != which) {
+			uint8_t *cur_dst = (uint8_t *)locked_pixels;
+			uint8_t *cur_saved = (uint8_t *)texture_buf;
+			uint32_t dst_off = which == FRAMEBUFFER_EVEN ? 0 : locked_pitch;
+			uint32_t src_off = which == FRAMEBUFFER_EVEN ? locked_pitch : 0;
+			for (int i = 0; i < height; ++i)
+			{
+				//copy saved line from other field
+				memcpy(cur_dst + dst_off, cur_saved, locked_pitch);
+				//save line from this field to buffer for next frame
+				memcpy(cur_saved, cur_dst + src_off, locked_pitch);
+				cur_dst += locked_pitch * 2;
+				cur_saved += locked_pitch;
+			}
+			height = 480;
+		}
+		if (screenshot_file) {
+			uint32_t shot_pitch = locked_pitch;
+			if (which == FRAMEBUFFER_EVEN) {
+				shot_height *= 2;
+			} else {
+				shot_pitch *= 2;
+			}
+#ifndef DISABLE_ZLIB
+			if (!strcasecmp(ext, "png")) {
+				free(ext);
+				save_png(screenshot_file, locked_pixels, shot_width, shot_height, shot_pitch);
+			} else {
+				free(ext);
+#endif
+				save_ppm(screenshot_file, locked_pixels, shot_width, shot_height, shot_pitch);
+#ifndef DISABLE_ZLIB
+			}
+#endif
+		}
+		SDL_UnlockTexture(sdl_textures[which]);
+#ifndef DISABLE_OPENGL
+	}
+#endif
+	last_height = height;
+	if (which <= FRAMEBUFFER_EVEN) {
+		render_update_display();
+	} else {
+		SDL_RenderCopy(extra_renderers[which - FRAMEBUFFER_USER_START], sdl_textures[which], NULL, NULL);
+		SDL_RenderPresent(extra_renderers[which - FRAMEBUFFER_USER_START]);
+	}
+	if (screenshot_file) {
+		fclose(screenshot_file);
+	}
+	if (which <= FRAMEBUFFER_EVEN) {
+		last = which;
+		static uint32_t frame_counter, start;
+		frame_counter++;
+		last_frame= SDL_GetTicks();
+		if ((last_frame - start) > FPS_INTERVAL) {
+			if (start && (last_frame-start)) {
+	#ifdef __ANDROID__
+				info_message("%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0));
+	#else
+				if (!fps_caption) {
+					fps_caption = malloc(strlen(caption) + strlen(" - 100000000.1 fps") + 1);
+				}
+				sprintf(fps_caption, "%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0));
+				SDL_SetWindowTitle(main_window, fps_caption);
+	#endif
+			}
+			start = last_frame;
+			frame_counter = 0;
+		}
+	}
+	if (!sync_to_audio) {
+		int32_t local_cur_min, local_min_remaining;
+		SDL_LockAudio();
+			if (last_buffered > NO_LAST_BUFFERED) {
+				average_change *= 0.9f;
+				average_change += (cur_min_buffered - last_buffered) * 0.1f;
+			}
+			local_cur_min = cur_min_buffered;
+			local_min_remaining = min_remaining_buffer;
+			last_buffered = cur_min_buffered;
+		SDL_UnlockAudio();
+		float frames_to_problem;
+		if (average_change < 0) {
+			frames_to_problem = (float)local_cur_min / -average_change;
+		} else {
+			frames_to_problem = (float)local_min_remaining / average_change;
+		}
+		float adjust_ratio = 0.0f;
+		if (
+			frames_to_problem < BUFFER_FRAMES_THRESHOLD
+			|| (average_change < 0 && local_cur_min < 3*min_buffered / 4)
+			|| (average_change >0 && local_cur_min > 5 * min_buffered / 4)
+			|| cur_min_buffered < 0
+		) {
+			
+			if (cur_min_buffered < 0) {
+				adjust_ratio = max_adjust;
+				SDL_PauseAudio(1);
+				last_buffered = NO_LAST_BUFFERED;
+				cur_min_buffered = 0;
+			} else {
+				adjust_ratio = -1.0 * average_change / ((float)sample_rate / (float)source_hz);
+				adjust_ratio /= 2.5 * source_hz;
+				if (fabsf(adjust_ratio) > max_adjust) {
+					adjust_ratio = adjust_ratio > 0 ? max_adjust : -max_adjust;
+				}
+			}
+		} else if (local_cur_min < min_buffered / 2) {
+			adjust_ratio = max_adjust;
+		}
+		if (adjust_ratio != 0.0f) {
+			average_change = 0;
+			for (uint8_t i = 0; i < num_audio_sources; i++)
+			{
+				audio_sources[i]->buffer_inc = ((double)audio_sources[i]->buffer_inc) + ((double)audio_sources[i]->buffer_inc) * adjust_ratio + 0.5;
+			}
+		}
+		while (source_frame_count > 0)
+		{
+			render_update_display();
+			source_frame_count--;
+		}
+		source_frame++;
+		if (source_frame >= source_hz) {
+			source_frame = 0;
+		}
+		source_frame_count = frame_repeat[source_frame];
+	}
+}
+
+static ui_render_fun render_ui;
+void render_set_ui_render_fun(ui_render_fun fun)
+{
+	render_ui = fun;
+}
+
+void render_update_display()
+{
+#ifndef DISABLE_OPENGL
+	if (render_gl) {
+		glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+		glClear(GL_COLOR_BUFFER_BIT);
+
+		glUseProgram(program);
+		glActiveTexture(GL_TEXTURE0);
+		glBindTexture(GL_TEXTURE_2D, textures[0]);
+		glUniform1i(un_textures[0], 0);
+
+		glActiveTexture(GL_TEXTURE1);
+		glBindTexture(GL_TEXTURE_2D, textures[interlaced ? 1 : scanlines ? 2 : 0]);
+		glUniform1i(un_textures[1], 1);
+
+		glUniform1f(un_width, render_emulated_width());
+		glUniform1f(un_height, last_height);
+
+		glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
+		glVertexAttribPointer(at_pos, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat[2]), (void *)0);
+		glEnableVertexAttribArray(at_pos);
+
+		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
+		glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, (void *)0);
+
+		glDisableVertexAttribArray(at_pos);
+		
+		if (render_ui) {
+			render_ui();
+		}
+
+		SDL_GL_SwapWindow(main_window);
+	} else {
+#endif
+		SDL_Rect src_clip = {
+			.x = overscan_left[video_standard],
+			.y = overscan_top[video_standard],
+			.w = render_emulated_width(),
+			.h = last_height
+		};
+		SDL_SetRenderDrawColor(main_renderer, 0, 0, 0, 255);
+		SDL_RenderClear(main_renderer);
+		SDL_RenderCopy(main_renderer, sdl_textures[FRAMEBUFFER_ODD], &src_clip, &main_clip);
+		if (render_ui) {
+			render_ui();
+		}
+		SDL_RenderPresent(main_renderer);
+#ifndef DISABLE_OPENGL
+	}
+#endif
+	if (!events_processed) {
+		process_events();
+	}
+	events_processed = 0;
+}
+
+uint32_t render_emulated_width()
+{
+	return last_width - overscan_left[video_standard] - overscan_right[video_standard];
+}
+
+uint32_t render_emulated_height()
+{
+	return (video_standard == VID_NTSC ? 243 : 294) - overscan_top[video_standard] - overscan_bot[video_standard];
+}
+
+uint32_t render_overscan_left()
+{
+	return overscan_left[video_standard];
+}
+
+uint32_t render_overscan_top()
+{
+	return overscan_top[video_standard];
+}
+
+void render_wait_quit(vdp_context * context)
+{
+	SDL_Event event;
+	while(SDL_WaitEvent(&event)) {
+		switch (event.type) {
+		case SDL_QUIT:
+			return;
+		}
+	}
+}
+
+int render_lookup_button(char *name)
+{
+	static tern_node *button_lookup;
+	if (!button_lookup) {
+		for (int i = SDL_CONTROLLER_BUTTON_A; i < SDL_CONTROLLER_BUTTON_MAX; i++)
+		{
+			button_lookup = tern_insert_int(button_lookup, SDL_GameControllerGetStringForButton(i), i);
+		}
+		//alternative Playstation-style names
+		button_lookup = tern_insert_int(button_lookup, "cross", SDL_CONTROLLER_BUTTON_A);
+		button_lookup = tern_insert_int(button_lookup, "circle", SDL_CONTROLLER_BUTTON_B);
+		button_lookup = tern_insert_int(button_lookup, "square", SDL_CONTROLLER_BUTTON_X);
+		button_lookup = tern_insert_int(button_lookup, "triangle", SDL_CONTROLLER_BUTTON_Y);
+		button_lookup = tern_insert_int(button_lookup, "share", SDL_CONTROLLER_BUTTON_BACK);
+		button_lookup = tern_insert_int(button_lookup, "select", SDL_CONTROLLER_BUTTON_BACK);
+		button_lookup = tern_insert_int(button_lookup, "options", SDL_CONTROLLER_BUTTON_START);
+		button_lookup = tern_insert_int(button_lookup, "l1", SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
+		button_lookup = tern_insert_int(button_lookup, "r1", SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
+		button_lookup = tern_insert_int(button_lookup, "l3", SDL_CONTROLLER_BUTTON_LEFTSTICK);
+		button_lookup = tern_insert_int(button_lookup, "r3", SDL_CONTROLLER_BUTTON_RIGHTSTICK);
+	}
+	return (int)tern_find_int(button_lookup, name, SDL_CONTROLLER_BUTTON_INVALID);
+}
+
+int render_lookup_axis(char *name)
+{
+	static tern_node *axis_lookup;
+	if (!axis_lookup) {
+		for (int i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++)
+		{
+			axis_lookup = tern_insert_int(axis_lookup, SDL_GameControllerGetStringForAxis(i), i);
+		}
+		//alternative Playstation-style names
+		axis_lookup = tern_insert_int(axis_lookup, "l2", SDL_CONTROLLER_AXIS_TRIGGERLEFT);
+		axis_lookup = tern_insert_int(axis_lookup, "r2", SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
+	}
+	return (int)tern_find_int(axis_lookup, name, SDL_CONTROLLER_AXIS_INVALID);
+}
+
+int32_t render_translate_input_name(int32_t controller, char *name, uint8_t is_axis)
+{
+	tern_node *button_lookup, *axis_lookup;
+	if (controller > MAX_JOYSTICKS || !joysticks[controller]) {
+		return RENDER_NOT_PLUGGED_IN;
+	}
+	
+	if (!SDL_IsGameController(joystick_sdl_index[controller])) {
+		return RENDER_NOT_MAPPED;
+	}
+	SDL_GameController *control = SDL_GameControllerOpen(joystick_sdl_index[controller]);
+	if (!control) {
+		warning("Failed to open game controller %d: %s\n", controller, SDL_GetError());
+		return RENDER_NOT_PLUGGED_IN;
+	}
+	
+	SDL_GameControllerButtonBind cbind;
+	if (is_axis) {
+		
+		int sdl_axis = render_lookup_axis(name);
+		if (sdl_axis == SDL_CONTROLLER_AXIS_INVALID) {
+			SDL_GameControllerClose(control);
+			return RENDER_INVALID_NAME;
+		}
+		cbind = SDL_GameControllerGetBindForAxis(control, sdl_axis);
+	} else {
+		int sdl_button = render_lookup_button(name);
+		if (sdl_button == SDL_CONTROLLER_BUTTON_INVALID) {
+			SDL_GameControllerClose(control);
+			return RENDER_INVALID_NAME;
+		}
+		cbind = SDL_GameControllerGetBindForButton(control, sdl_button);
+	}
+	SDL_GameControllerClose(control);
+	switch (cbind.bindType)
+	{
+	case SDL_CONTROLLER_BINDTYPE_BUTTON:
+		return cbind.value.button;
+	case SDL_CONTROLLER_BINDTYPE_AXIS:
+		return RENDER_AXIS_BIT | cbind.value.axis;
+	case SDL_CONTROLLER_BINDTYPE_HAT:
+		return RENDER_DPAD_BIT | (cbind.value.hat.hat << 4) | cbind.value.hat.hat_mask;
+	}
+	return RENDER_NOT_MAPPED;
+}
+
+int32_t render_dpad_part(int32_t input)
+{
+	return input >> 4 & 0xFFFFFF;
+}
+
+uint8_t render_direction_part(int32_t input)
+{
+	return input & 0xF;
+}
+
+int32_t render_axis_part(int32_t input)
+{
+	return input & 0xFFFFFFF;
+}
+
 void process_events()
 {
 	if (events_processed > MAX_EVENT_POLL_PER_FRAME) {
@@ -1070,7 +1820,6 @@
 #define TOGGLE_MIN_DELAY 250
 void render_toggle_fullscreen()
 {
-	static int in_toggle;
 	//protect against event processing causing us to attempt to toggle while still toggling
 	if (in_toggle) {
 		return;
@@ -1110,36 +1859,6 @@
 	in_toggle = 0;
 }
 
-void render_wait_psg(psg_context * context)
-{
-	SDL_LockMutex(audio_mutex);
-		while (current_psg != NULL) {
-			SDL_CondWait(psg_cond, audio_mutex);
-		}
-		current_psg = context->audio_buffer;
-		SDL_CondSignal(audio_ready);
-
-		context->audio_buffer = context->back_buffer;
-		context->back_buffer = current_psg;
-	SDL_UnlockMutex(audio_mutex);
-	context->buffer_pos = 0;
-}
-
-void render_wait_ym(ym2612_context * context)
-{
-	SDL_LockMutex(audio_mutex);
-		while (current_ym != NULL) {
-			SDL_CondWait(ym_cond, audio_mutex);
-		}
-		current_ym = context->audio_buffer;
-		SDL_CondSignal(audio_ready);
-
-		context->audio_buffer = context->back_buffer;
-		context->back_buffer = current_ym;
-	SDL_UnlockMutex(audio_mutex);
-	context->buffer_pos = 0;
-}
-
 uint32_t render_audio_buffer()
 {
 	return buffer_samples;
@@ -1165,3 +1884,31 @@
 	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, title, message, NULL);
 }
 
+uint32_t render_elapsed_ms(void)
+{
+	return SDL_GetTicks();
+}
+
+void render_sleep_ms(uint32_t delay)
+{
+	return SDL_Delay(delay);
+}
+
+uint8_t render_has_gl(void)
+{
+	return render_gl;
+}
+
+uint8_t render_get_active_framebuffer(void)
+{
+	if (SDL_GetWindowFlags(main_window) & SDL_WINDOW_INPUT_FOCUS) {
+		return FRAMEBUFFER_ODD;
+	}
+	for (int i = 0; i < num_textures - 2; i++)
+	{
+		if (extra_windows[i] && (SDL_GetWindowFlags(extra_windows[i]) & SDL_WINDOW_INPUT_FOCUS)) {
+			return FRAMEBUFFER_USER_START + i; 
+		}
+	}
+	return 0xFF;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/render_sdl.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,18 @@
+#ifndef RENDER_SDL_H_
+#define RENDER_SDL_H_
+
+#include <SDL.h>
+
+SDL_Window *render_get_window(void);
+typedef void (*ui_render_fun)(void);
+typedef void (*event_handler)(SDL_Event *);
+void render_update_display(void);
+void render_set_ui_render_fun(ui_render_fun);
+void render_set_event_handler(event_handler handler);
+void render_set_gl_context_handlers(ui_render_fun destroy, ui_render_fun create);
+SDL_Joystick *render_get_joystick(int index);
+SDL_GameController *render_get_controller(int index);
+int render_lookup_button(char *name);
+int render_lookup_axis(char *name);
+
+#endif //RENDER_SDL_H_
--- a/rom.db	Sun Dec 31 10:11:16 2017 -0800
+++ b/rom.db	Tue Dec 25 11:12:26 2018 -0800
@@ -565,11 +565,15 @@
 		}
 		380000 {
 			device EEPROM
-			last 3FFFFF
+			last 387FFF
 			bits_read {
 				7 sda
 			}
 		}
+		388000 {
+			device jcart
+			last 38FFFF
+		}
 	}
 }
 9f47fcc7bb2f5921cb1c3beb06b668ffb292cb08 {
@@ -623,11 +627,15 @@
 		}
 		380000 {
 			device EEPROM
-			last 3FFFFF
+			last 387FFF
 			bits_read {
 				7 sda
 			}
 		}
+		388000 {
+			device jcart
+			last 38FFFF
+		}
 	}
 }
 T-120096 {
@@ -652,11 +660,15 @@
 		}
 		380000 {
 			device EEPROM
-			last 3FFFFF
+			last 387FFF
 			bits_read {
 				7 sda
 			}
 		}
+		388000 {
+			device jcart
+			last 38FFFF
+		}
 	}
 }
 MK-12056 {
@@ -1203,6 +1215,21 @@
 		}
 	}
 }
+2a561b6e47c93272fe5947084837d9f6f514ed38 {
+	name Squirrel King
+	map {
+		0 {
+			device ROM
+			last 3FFFFF
+		}
+		400000 {
+			device RAM
+			size 2
+			bus both
+			last 7FFFFF
+		}
+	}
+}
 
 #This entry is used by the GUI ROM
 BlstMenu {
@@ -1299,3 +1326,54 @@
 		}
 	}
 }
+cda73e4caf53cbc8f0750b69e5e7f394ad3735d1 {
+	name MegaWiFi Bootloader
+	NOR {
+		size 4194304
+		page_size 128
+		product_id DA45
+		bus both
+		init ROM
+		cmd_address1 AAB
+		cmd_address2 555
+	}
+	map {
+		0 {
+			device NOR
+			last 3FFFFF
+		}
+		A130C0 {
+			device megawifi
+			last A130CF
+		}
+	}
+}
+222a66cdb8865a7f89e5a72418413888bb400176 {
+	#I've personally confirmed this version had a J-Cart
+	#release, but unlike the other revision it runs without it
+	name Pete Sampras Tennis
+	map {
+		0 {
+			device ROM
+			last 1FFFFF
+		}
+		200000 {
+			device jcart
+			last 3FFFFF
+		}
+	}
+}
+4c830ace4590294bb374b4cab71ebebf44d9a07a {
+	#This version will not accept input if J-Cart hardware is missing
+	name Pete Sampras Tennis
+	map {
+		0 {
+			device ROM
+			last 1FFFFF
+		}
+		200000 {
+			device jcart
+			last 3FFFFF
+		}
+	}
+}
--- a/romdb.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/romdb.c	Tue Dec 25 11:12:26 2018 -0800
@@ -11,6 +11,9 @@
 #include "nor.h"
 #include "sega_mapper.h"
 #include "multi_game.h"
+#include "megawifi.h"
+#include "jcart.h"
+#include "blastem.h"
 
 #define DOM_TITLE_START 0x120
 #define DOM_TITLE_END 0x150
@@ -49,6 +52,8 @@
 		free(info->save_buffer);
 		if (info->save_type == SAVE_I2C) {
 			free(info->eeprom_map);
+		} else if (info->save_type == SAVE_NOR) {
+			free(info->nor);
 		}
 	}
 	free(info->map);
@@ -499,15 +504,15 @@
 		if (!page_size) {
 			fatal_error("ROM DB map entry %d with address %s has device type NOR, but the NOR page size is not defined\n", state->index, key);
 		}
-		state->info->save_page_size = atoi(size);
-		if (!state->info->save_page_size) {
-			fatal_error("NOR page size %s is invalid\n", size);
+		uint32_t save_page_size = atoi(page_size);
+		if (!save_page_size) {
+			fatal_error("NOR page size %s is invalid\n", page_size);
 		}
 		char *product_id = tern_find_path(state->root, "NOR\0product_id\0", TVAL_PTR).ptrval;
 		if (!product_id) {
 			fatal_error("ROM DB map entry %d with address %s has device type NOR, but the NOR product ID is not defined\n", state->index, key);
 		}
-		state->info->save_product_id = strtol(product_id, NULL, 16);
+		uint16_t save_product_id = strtol(product_id, NULL, 16);
 		char *bus = tern_find_path(state->root, "NOR\0bus\0", TVAL_PTR).ptrval;
 		if (!strcmp(bus, "odd")) {
 			state->info->save_bus = RAM_FLAG_ODD;
@@ -518,7 +523,26 @@
 		}
 		state->info->save_type = SAVE_NOR;
 		state->info->save_buffer = malloc(state->info->save_size);
-		memset(state->info->save_buffer, 0xFF, state->info->save_size);
+		char *init = tern_find_path_default(state->root, "NOR\0init\0", (tern_val){.ptrval="FF"}, TVAL_PTR).ptrval;
+		if (!strcmp(init, "ROM")) {
+			uint32_t init_size = state->rom_size > state->info->save_size ? state->info->save_size : state->rom_size;
+			memcpy(state->info->save_buffer, state->rom, init_size);
+			if (state->info->save_bus == RAM_FLAG_BOTH) {
+				byteswap_rom(state->info->save_size, (uint16_t *)state->info->save_buffer);
+			}
+		} else {
+			memset(state->info->save_buffer, strtol(init, NULL, 16), state->info->save_size);
+		}
+		state->info->nor = calloc(1, sizeof(nor_state));
+		nor_flash_init(state->info->nor, state->info->save_buffer, state->info->save_size, save_page_size, save_product_id, state->info->save_bus);
+		char *cmd1 = tern_find_path(state->root, "NOR\0cmd_address1\0", TVAL_PTR).ptrval;
+		if (cmd1) {
+			state->info->nor->cmd_address1 = strtol(cmd1, NULL, 16);
+		}
+		char *cmd2 = tern_find_path(state->root, "NOR\0cmd_address2\0", TVAL_PTR).ptrval;
+		if (cmd2) {
+			state->info->nor->cmd_address2 = strtol(cmd2, NULL, 16);
+		}
 	}
 }
 
@@ -585,6 +609,7 @@
 		}
 		if (matching_chunks == 0) {
 			//Nothing mapped in the relevant range for the lock-on cart, ignore this mapping
+			free_rom_info(&lock_info);
 			return;
 		} else if (matching_chunks > 1) {
 			state->info->map_chunks += matching_chunks - 1;
@@ -615,8 +640,7 @@
 			state->info->save_buffer = lock_info.save_buffer;
 			state->info->save_size = lock_info.save_size;
 			state->info->save_mask = lock_info.save_mask;
-			state->info->save_page_size = lock_info.save_page_size;
-			state->info->save_product_id = lock_info.save_product_id;
+			state->info->nor = lock_info.nor;
 			state->info->save_type = lock_info.save_type;
 			state->info->save_bus = lock_info.save_bus;
 			lock_info.save_buffer = NULL;
@@ -666,6 +690,10 @@
 		map->write_8 = nor_flash_write_b;
 		map->read_16 = nor_flash_read_w;
 		map->read_8 = nor_flash_read_b;
+		if (state->info->save_bus == RAM_FLAG_BOTH) {
+			map->flags |= MMAP_READ_CODE | MMAP_CODE;
+			map->buffer = state->info->save_buffer;
+		}
 		map->mask = 0xFFFFFF;
 	} else if (!strcmp(dtype, "Sega mapper")) {
 		state->info->mapper_type = MAPPER_SEGA;
@@ -778,6 +806,27 @@
 		map->mask = 0xFF;
 		map->write_16 = write_multi_game_w;
 		map->write_8 = write_multi_game_b;
+	} else if (!strcmp(dtype, "megawifi")) {
+		if (!strcmp(
+			"on", 
+			tern_find_path_default(config, "system\0megawifi\0", (tern_val){.ptrval="off"}, TVAL_PTR).ptrval)
+		) {
+			map->write_16 = megawifi_write_w;
+			map->write_8 = megawifi_write_b;
+			map->read_16 = megawifi_read_w;
+			map->read_8 = megawifi_read_b;
+			map->mask = 0xFFFFFF;
+		} else {
+			warning("ROM uses MegaWiFi, but it is disabled\n");
+			return;
+		}
+	} else if (!strcmp(dtype, "jcart")) {
+		state->info->mapper_type = MAPPER_JCART;
+		map->write_16 = jcart_write_w;
+		map->write_8 = jcart_write_b;
+		map->read_16 = jcart_read_w;
+		map->read_8 = jcart_read_b;
+		map->mask = 0xFFFFFF;
 	} else {
 		fatal_error("Invalid device type %s for ROM DB map entry %d with address %s\n", dtype, state->index, key);
 	}
--- a/romdb.h	Sun Dec 31 10:11:16 2017 -0800
+++ b/romdb.h	Tue Dec 25 11:12:26 2018 -0800
@@ -31,6 +31,8 @@
 	uint32_t    page_size;
 	uint32_t    current_page;
 	uint32_t    last_write_cycle;
+	uint32_t    cmd_address1;
+	uint32_t    cmd_address2;
 	uint16_t    product_id;
 	uint8_t     mode;
 	uint8_t     cmd_state;
@@ -43,13 +45,14 @@
 	MAPPER_SEGA,
 	MAPPER_REALTEC,
 	MAPPER_XBAND,
-	MAPPER_MULTI_GAME
+	MAPPER_MULTI_GAME,
+	MAPPER_JCART
 };
 
 
 typedef struct rom_info rom_info;
 
-#include "backend.h"
+#include "memmap.h"
 
 struct rom_info {
 	char          *name;
@@ -61,13 +64,12 @@
 	char          *port2_override;
 	char          *ext_override;
 	char          *mouse_mode;
+	nor_state     *nor;
 	uint32_t      num_eeprom;
 	uint32_t      map_chunks;
 	uint32_t      rom_size;
 	uint32_t      save_size;
 	uint32_t      save_mask;
-	uint32_t      save_page_size;
-	uint16_t      save_product_id;
 	uint16_t      mapper_start_index;
 	uint8_t       save_type;
 	uint8_t       save_bus; //only used for NOR currently
@@ -87,6 +89,7 @@
 //Note: free_rom_info only frees things pointed to by a rom_info struct, not the struct itself
 //this is because rom_info structs are typically stack allocated
 void free_rom_info(rom_info *info);
+typedef struct system_header system_header;
 void cart_serialize(system_header *sys, serialize_buffer *buf);
 void cart_deserialize(deserialize_buffer *buf, void *vcontext);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/saves.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,77 @@
+#include <string.h>
+#include <stdlib.h>
+#include "saves.h"
+#include "util.h"
+
+#ifdef _WIN32
+#define localtime_r(a,b) localtime(a)
+#include <windows.h>
+#endif
+//0123456789012345678901234678
+//Slot N - December 31st, XXXX
+#define MAX_DESC_SIZE 40
+
+char *get_slot_name(system_header *system, uint32_t slot_index, char *ext)
+{
+	if (!system->save_dir) {
+		return NULL;
+	}
+	char *fname;
+	if (slot_index < 10) {
+		size_t name_len = strlen("slot_N.") + strlen(ext) + 1;
+		fname = malloc(name_len);
+		snprintf(fname, name_len, "slot_%d.%s", slot_index, ext);
+	} else {
+		size_t name_len = strlen("quicksave.") + strlen(ext) + 1;
+		fname = malloc(name_len);
+		snprintf(fname, name_len, "quicksave.%s", ext);
+	}
+	char const *parts[] = {system->save_dir, PATH_SEP, fname};
+	char *ret = alloc_concat_m(3, parts);
+	free(fname);
+	return ret;
+}
+
+save_slot_info *get_slot_info(system_header *system, uint32_t *num_out)
+{
+	save_slot_info *dst = calloc(11, sizeof(save_slot_info));
+	time_t modtime;
+	struct tm ltime;
+	for (uint32_t i = 0; i <= QUICK_SAVE_SLOT; i++)
+	{
+		char * cur = dst[i].desc = malloc(MAX_DESC_SIZE);
+		char * fname = get_slot_name(system, i, "state");
+		modtime = get_modification_time(fname);
+		free(fname);
+		if (!modtime && system->type == SYSTEM_GENESIS) {
+			fname = get_slot_name(system, i, "gst");
+			modtime = get_modification_time(fname);
+			free(fname);
+		}
+		if (i == QUICK_SAVE_SLOT) {
+			cur += snprintf(cur, MAX_DESC_SIZE, "Quick - ");
+		} else {
+			cur += snprintf(cur, MAX_DESC_SIZE, "Slot %d - ", i);
+		}
+		if (modtime) {
+			strftime(cur, MAX_DESC_SIZE - (cur - dst->desc), "%c", localtime_r(&modtime, &ltime));
+		} else {
+			strcpy(cur, "EMPTY");
+		}
+		dst[i].modification_time = modtime;
+	}
+	*num_out = QUICK_SAVE_SLOT + 1;
+	return dst;
+}
+
+void free_slot_info(save_slot_info *slots)
+{
+	if (!slots) {
+		return;
+	}
+	for (uint32_t i = 0; i <= QUICK_SAVE_SLOT; i++)
+	{
+		free(slots[i].desc);
+	}
+	free(slots);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/saves.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,19 @@
+#ifndef SAVES_H_
+#define SAVES_H_
+
+#include <time.h>
+#include <stdint.h>
+#include "system.h"
+
+#define QUICK_SAVE_SLOT 10
+
+typedef struct {
+	char   *desc;
+	time_t modification_time;
+} save_slot_info;
+
+char *get_slot_name(system_header *system, uint32_t slot_index, char *ext);
+save_slot_info *get_slot_info(system_header *system, uint32_t *num_out);
+void free_slot_info(save_slot_info *slots);
+
+#endif //SAVES_H_
--- a/sms.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/sms.c	Tue Dec 25 11:12:26 2018 -0800
@@ -6,6 +6,8 @@
 #include "render.h"
 #include "util.h"
 #include "debug.h"
+#include "saves.h"
+#include "bindings.h"
 
 static void *memory_io_write(uint32_t location, void *vcontext, uint8_t value)
 {
@@ -292,20 +294,13 @@
 
 static void save_state(sms_context *sms, uint8_t slot)
 {
-	char *save_path;
-	if (slot == QUICK_SAVE_SLOT) {
-		save_path = save_state_path;
-	} else {
-		char slotname[] = "slot_0.state";
-		slotname[5] = '0' + slot;
-		char const *parts[] = {sms->header.save_dir, PATH_SEP, slotname};
-		save_path = alloc_concat_m(3, parts);
-	}
+	char *save_path = get_slot_name(&sms->header, slot, "state");
 	serialize_buffer state;
 	init_serialize(&state);
 	sms_serialize(sms, &state);
 	save_to_file(&state, save_path);
 	printf("Saved state to %s\n", save_path);
+	free(save_path);
 	free(state.data);
 }
 
@@ -324,24 +319,26 @@
 static uint8_t load_state(system_header *system, uint8_t slot)
 {
 	sms_context *sms = (sms_context *)system;
-	char numslotname[] = "slot_0.state";
-	char *slotname;
-	if (slot == QUICK_SAVE_SLOT) {
-		slotname = "quicksave.state";
-	} else {
-		numslotname[5] = '0' + slot;
-		slotname = numslotname;
+	char *statepath = get_slot_name(system, slot, "state");
+	uint8_t ret;
+#ifdef USE_NATIVE
+	if (!sms->z80->native_pc) {
+		ret = get_modification_time(statepath) != 0;
+		if (ret) {
+			system->delayed_load_slot = slot + 1;
+		}
+		goto done;
+		
 	}
-	char const *parts[] = {sms->header.save_dir, PATH_SEP, slotname};
-	char *statepath = alloc_concat_m(3, parts);
-	uint8_t ret = load_state_path(sms, statepath);
+#endif
+	ret = load_state_path(sms, statepath);
+done:
 	free(statepath);
 	return ret;
 }
 
 static void run_sms(system_header *system)
 {
-	render_disable_ym();
 	sms_context *sms = (sms_context *)system;
 	uint32_t target_cycle = sms->z80->current_cycle + 3420*16;
 	//TODO: PAL support
@@ -349,6 +346,11 @@
 	while (!sms->should_return)
 	{
 #ifdef USE_NATIVE
+		if (system->delayed_load_slot) {
+			load_state(system, system->delayed_load_slot - 1);
+			system->delayed_load_slot = 0;
+			
+		}
 		if (system->enter_debugger && sms->z80->pc) {
 			system->enter_debugger = 0;
 			zdebugger(sms->z80, sms->z80->pc);
@@ -390,22 +392,24 @@
 			target_cycle -= adjust;
 		}
 	}
+	bindings_release_capture();
 	vdp_release_framebuffer(sms->vdp);
+	render_pause_source(sms->psg->audio);
 	sms->should_return = 0;
-	render_enable_ym();
 }
 
 static void resume_sms(system_header *system)
 {
 	sms_context *sms = (sms_context *)system;
+	bindings_reacquire_capture();
 	vdp_reacquire_framebuffer(sms->vdp);
+	render_resume_source(sms->psg->audio);
 	run_sms(system);
 }
 
 static void start_sms(system_header *system, char *statefile)
 {
 	sms_context *sms = (sms_context *)system;
-	set_keybindings(&sms->io);
 	
 	z80_assert_reset(sms->z80, 0);
 	z80_clear_reset(sms->z80, 128*15);
@@ -457,19 +461,7 @@
 static void inc_debug_mode(system_header *system)
 {
 	sms_context *sms = (sms_context *)system;
-	sms->vdp->debug++;
-	if (sms->vdp->debug == 7) {
-		sms->vdp->debug = 0;
-	}
-}
-
-static void inc_debug_pal(system_header *system)
-{
-	sms_context *sms = (sms_context *)system;
-	sms->vdp->debug_pal++;
-	if (sms->vdp->debug_pal == 4) {
-		sms->vdp->debug_pal = 0;
-	}
+	vdp_inc_debug_mode(sms->vdp);
 }
 
 static void load_save(system_header *system)
@@ -482,14 +474,74 @@
 	//TODO: Implement me
 }
 
-sms_context *alloc_configure_sms(system_media *media, uint32_t opts, uint8_t force_region, rom_info *info_out)
+static void gamepad_down(system_header *system, uint8_t gamepad_num, uint8_t button)
+{
+	sms_context *sms = (sms_context *)system;
+	if (gamepad_num == GAMEPAD_MAIN_UNIT) {
+		if (button == MAIN_UNIT_PAUSE) {
+			vdp_pbc_pause(sms->vdp);
+		}
+	} else {
+		io_gamepad_down(&sms->io, gamepad_num, button);
+	}
+}
+
+static void gamepad_up(system_header *system, uint8_t gamepad_num, uint8_t button)
+{
+	sms_context *sms = (sms_context *)system;
+	io_gamepad_up(&sms->io, gamepad_num, button);
+}
+
+static void mouse_down(system_header *system, uint8_t mouse_num, uint8_t button)
+{
+	sms_context *sms = (sms_context *)system;
+	io_mouse_down(&sms->io, mouse_num, button);
+}
+
+static void mouse_up(system_header *system, uint8_t mouse_num, uint8_t button)
+{
+	sms_context *sms = (sms_context *)system;
+	io_mouse_up(&sms->io, mouse_num, button);
+}
+
+static void mouse_motion_absolute(system_header *system, uint8_t mouse_num, uint16_t x, uint16_t y)
 {
-	memset(info_out, 0, sizeof(*info_out));
+	sms_context *sms = (sms_context *)system;
+	io_mouse_motion_absolute(&sms->io, mouse_num, x, y);
+}
+
+static void mouse_motion_relative(system_header *system, uint8_t mouse_num, int32_t x, int32_t y)
+{
+	sms_context *sms = (sms_context *)system;
+	io_mouse_motion_relative(&sms->io, mouse_num, x, y);
+}
+
+static void keyboard_down(system_header *system, uint8_t scancode)
+{
+	sms_context *sms = (sms_context *)system;
+	io_keyboard_down(&sms->io, scancode);
+}
+
+static void keyboard_up(system_header *system, uint8_t scancode)
+{
+	sms_context *sms = (sms_context *)system;
+	io_keyboard_up(&sms->io, scancode);
+}
+
+static void config_updated(system_header *system)
+{
+	sms_context *sms = (sms_context *)system;
+	setup_io_devices(config, &system->info, &sms->io);
+}
+
+
+sms_context *alloc_configure_sms(system_media *media, uint32_t opts, uint8_t force_region)
+{
 	sms_context *sms = calloc(1, sizeof(sms_context));
 	uint32_t rom_size = nearest_pow2(media->size);
 	memmap_chunk memory_map[6];
 	if (media->size > 0xC000)  {
-		info_out->map_chunks = 6;
+		sms->header.info.map_chunks = 6;
 		uint8_t *ram_reg_overlap = sms->ram + sizeof(sms->ram) - 4;
 		memory_map[0] = (memmap_chunk){0x0000, 0x0400,  0xFFFF,             0, 0, MMAP_READ,                        media->buffer, NULL, NULL, NULL, NULL};
 		memory_map[1] = (memmap_chunk){0x0400, 0x4000,  0xFFFF,             0, 0, MMAP_READ|MMAP_PTR_IDX|MMAP_CODE, NULL,     NULL, NULL, NULL, NULL};
@@ -498,21 +550,21 @@
 		memory_map[4] = (memmap_chunk){0xC000, 0xFFFC,  sizeof(sms->ram)-1, 0, 0, MMAP_READ|MMAP_WRITE|MMAP_CODE,   sms->ram, NULL, NULL, NULL, NULL};
 		memory_map[5] = (memmap_chunk){0xFFFC, 0x10000, 0x0003,             0, 0, MMAP_READ,                        ram_reg_overlap, NULL, NULL, NULL, mapper_write};
 	} else {
-		info_out->map_chunks = 2;
+		sms->header.info.map_chunks = 2;
 		memory_map[0] = (memmap_chunk){0x0000, 0xC000,  rom_size-1,         0, 0, MMAP_READ,                      media->buffer,  NULL, NULL, NULL, NULL};
 		memory_map[1] = (memmap_chunk){0xC000, 0x10000, sizeof(sms->ram)-1, 0, 0, MMAP_READ|MMAP_WRITE|MMAP_CODE, sms->ram, NULL, NULL, NULL, NULL};
 	};
-	info_out->map = malloc(sizeof(memmap_chunk) * info_out->map_chunks);
-	memcpy(info_out->map, memory_map, sizeof(memmap_chunk) * info_out->map_chunks);
+	sms->header.info.map = malloc(sizeof(memmap_chunk) * sms->header.info.map_chunks);
+	memcpy(sms->header.info.map, memory_map, sizeof(memmap_chunk) * sms->header.info.map_chunks);
 	z80_options *zopts = malloc(sizeof(z80_options));
-	init_z80_opts(zopts, info_out->map, info_out->map_chunks, io_map, 4, 15, 0xFF);
+	init_z80_opts(zopts, sms->header.info.map, sms->header.info.map_chunks, io_map, 4, 15, 0xFF);
 	sms->z80 = init_z80_context(zopts);
 	sms->z80->system = sms;
 	sms->z80->options->gen.debug_cmd_handler = debug_commands;
 	
 	sms->rom = media->buffer;
 	sms->rom_size = rom_size;
-	if (info_out->map_chunks > 2) {
+	if (sms->header.info.map_chunks > 2) {
 		sms->z80->mem_pointers[0] = sms->rom;
 		sms->z80->mem_pointers[1] = sms->rom + 0x4000;
 		sms->z80->mem_pointers[2] = sms->rom + 0x8000;
@@ -521,23 +573,20 @@
 		sms->bank_regs[3] = 0x8000 >> 14;
 	}
 	
-	char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0", TVAL_PTR).ptrval;
-	uint32_t lowpass_cutoff = lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : 3390;
-	
 	//TODO: Detect region and pick master clock based off of that
 	sms->normal_clock = sms->master_clock = 53693175;
 	
 	sms->psg = malloc(sizeof(psg_context));
-	psg_init(sms->psg, render_sample_rate(), sms->master_clock, 15*16, render_audio_buffer(), lowpass_cutoff);
+	psg_init(sms->psg, sms->master_clock, 15*16);
 	
-	sms->vdp = malloc(sizeof(vdp_context));
-	init_vdp_context(sms->vdp, 0);
+	sms->vdp = init_vdp_context(0);
 	sms->vdp->system = &sms->header;
 	
-	info_out->save_type = SAVE_NONE;
-	info_out->name = strdup(media->name);
+	sms->header.info.save_type = SAVE_NONE;
+	sms->header.info.name = strdup(media->name);
 	
-	setup_io_devices(config, info_out, &sms->io);
+	setup_io_devices(config, &sms->header.info, &sms->io);
+	sms->header.has_keyboard = io_has_keyboard(&sms->io);
 	
 	sms->header.set_speed_percent = set_speed_percent;
 	sms->header.start_context = start_sms;
@@ -550,8 +599,16 @@
 	sms->header.request_exit = request_exit;
 	sms->header.soft_reset = soft_reset;
 	sms->header.inc_debug_mode = inc_debug_mode;
-	sms->header.inc_debug_pal = inc_debug_pal;
+	sms->header.gamepad_down = gamepad_down;
+	sms->header.gamepad_up = gamepad_up;
+	sms->header.mouse_down = mouse_down;
+	sms->header.mouse_up = mouse_up;
+	sms->header.mouse_motion_absolute = mouse_motion_absolute;
+	sms->header.mouse_motion_relative = mouse_motion_relative;
+	sms->header.keyboard_down = keyboard_down;
+	sms->header.keyboard_up = keyboard_up;
+	sms->header.config_updated = config_updated;
 	sms->header.type = SYSTEM_SMS;
 	
 	return sms;
-}
\ No newline at end of file
+}
--- a/sms.h	Sun Dec 31 10:11:16 2017 -0800
+++ b/sms.h	Tue Dec 25 11:12:26 2018 -0800
@@ -30,6 +30,6 @@
 	uint8_t       cart_ram[SMS_CART_RAM_SIZE];
 } sms_context;
 
-sms_context *alloc_configure_sms(system_media *media, uint32_t opts, uint8_t force_region, rom_info *info_out);
+sms_context *alloc_configure_sms(system_media *media, uint32_t opts, uint8_t force_region);
 
 #endif //SMS_H_
--- a/stateview.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/stateview.c	Tue Dec 25 11:12:26 2018 -0800
@@ -79,6 +79,10 @@
 {
 }
 
+void controller_add_mappings()
+{
+}
+
 tern_node * config;
 int headless = 0;
 
@@ -112,17 +116,16 @@
 	width = width < 320 ? def_width : width;
 	height = height < 240 ? (width/320) * 240 : height;
 
-	vdp_context context;
 	render_init(width, height, "GST State Viewer", 0);
-	init_vdp_context(&context, 0);
-	vdp_load_gst(&context, state_file);
-	vdp_run_to_vblank(&context);
-	vdp_print_sprite_table(&context);
-	printf("Display %s\n", (context.regs[REG_MODE_2] & DISPLAY_ENABLE) ? "enabled" : "disabled");
-	if (!(context.regs[REG_MODE_2] & DISPLAY_ENABLE)) {
+	vdp_context *context = init_vdp_context(0);
+	vdp_load_gst(context, state_file);
+	vdp_run_to_vblank(context);
+	vdp_print_sprite_table(context);
+	printf("Display %s\n", (context->regs[REG_MODE_2] & DISPLAY_ENABLE) ? "enabled" : "disabled");
+	if (!(context->regs[REG_MODE_2] & DISPLAY_ENABLE)) {
 		puts("Forcing display on");
-		vdp_control_port_write(&context, 0x8000 | REG_MODE_2 << 8 | context.regs[REG_MODE_2] | DISPLAY_ENABLE);
+		vdp_control_port_write(context, 0x8000 | REG_MODE_2 << 8 | context->regs[REG_MODE_2] | DISPLAY_ENABLE);
 	}
-    render_wait_quit(&context);
+    render_wait_quit(context);
     return 0;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/svp.cpu	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,642 @@
+info
+	prefix svp_
+	opcode_size 16
+	body svp_run_op
+	header svp.h
+	include svp_util.c
+	
+regs
+	internal 16 scratch2 x y pad0 st pad1 pc
+	a 32
+	scratch1 32
+	rom ptr16
+	stack 16 stack0 stack1 stack2 stack3 stack4 stack5
+	stackidx 8
+	p 32
+	external 16 pm0 pm1 pm2 xst pm4 ext5 pmc
+	pointers0 8 r0 r1 r2 r3 
+	pointers1 8 r4 r5 r6 r7
+	pm_address 32 5
+	pm_mode 8 5
+	
+	iram 16 1024
+	ram0 16 256
+	ram1 16 256
+	zflag 8
+	nflag 8
+	rpl 16
+	
+flags
+	register st
+	Z 13 zero zflag
+	N 15 sign nflag
+	R 0-2 none rpl
+	
+svp_pop
+	mov stack.stackidx dst
+	add 1 stackidx stackidx
+	switch stackidx
+	case 6
+	mov 0 stackidx
+	end
+	
+svp_push
+	arg src 16
+	sub 1 stackidx stackidx
+	switch stackidx
+	case 0xFF
+	mov 5 stackidx
+	end
+	mov src stack.stackidx
+	
+svp_ram_read
+	arg mode 16
+	arg banki 16
+	arg regi 16
+	local idx 16
+	
+	switch banki
+	case 0
+	meta bank ram0
+	meta reg pointers0.regi
+	
+	default
+	meta bank ram1
+	meta reg pointers1.regi
+	end
+	
+	mov reg idx
+	switch mode
+	case 0
+	meta modestr ""
+	
+	case 1
+	meta modestr +!
+	add 1 reg reg
+	
+	case 2
+	#loop decremenet
+	meta modestr -
+	
+	if rpl
+		local tmp 16
+		mov reg tmp
+		lsl 1 rpl rpl
+		sub 1 rpl rpl
+		local mask 16
+		not rpl mask
+		and reg mask reg
+		sub 1 tmp tmp
+		and rpl tmp tmp
+		or tmp reg reg
+	else
+		sub 1 reg reg
+	end
+	
+	case 3
+	#loop increment
+	meta modestr +
+	
+	and 7 st rpl
+	if rpl
+		local tmp 16
+		mov reg tmp
+		lsl 1 rpl rpl
+		sub 1 rpl rpl
+		local mask 16
+		not rpl mask
+		and reg mask reg
+		add 1 tmp tmp
+		and rpl tmp tmp
+		or tmp reg reg
+	else
+		sub 1 reg reg
+	end
+	end
+	
+	and 255 idx idx
+	meta val bank.idx
+	
+svp_read_ext
+	arg regidxr 16
+	switch regidxr
+	case 7
+	meta val a
+	
+	default
+	#TODO: PMAR stuff
+	meta val external.regidxr
+	end
+	
+svp_write_ext
+	arg regidxw 16
+	switch regidxw
+	case 7
+	and 0xFFFF0000 a a
+	or src a a
+	
+	default
+	#TODO: PMAR stuff
+	mov src external.regidxw
+	end
+	
+svp_alu_op
+	arg P 16
+	arg param 32
+	
+	switch P
+	case 1
+	dis "sub %s" name
+	sub param a a
+	
+	case 3
+	dis "cmp %s" name
+	cmp param a
+	
+	case 4
+	dis "add %s" name
+	add param a a
+	
+	case 5
+	dis "and %s" name
+	and param a a
+	
+	case 6
+	dis "or %s" name
+	or param a a
+	
+	case 7
+	dis "eor %s" name
+	xor param a a
+	end
+	update_flags ZN
+	
+svp_check_cond
+	arg fval 16
+	arg cond 16
+	local invert 8
+	switch cond
+	case 0
+	meta flag 1
+	
+	case 5
+	meta flag zflag
+	
+	case 7
+	meta flag nflag
+	
+	default
+	meta flag 0
+	end
+	
+	if fval
+	meta istrue flag
+	
+	else
+	lnot flag invert
+	meta istrue invert
+	
+	end
+	
+PPP0000000000000 alu_n1
+	invalid P 0
+	invalid P 2
+	meta name "-"
+	svp_alu_op P 0xFFFF0000
+	
+PPP0000000000RRR alu_r
+	invalid P 0
+	invalid P 2
+	local tmp 32 
+	lsl internal.R 16 tmp
+	meta name internal.R
+	svp_alu_op P tmp
+	
+PPP0000000000011 alu_a
+	invalid P 0
+	invalid P 2
+	svp_alu_op P a
+	
+PPP0000000000101 alu_stack
+	invalid P 0
+	invalid P 2
+	local tmp 32
+	meta dst tmp
+	svp_pop
+	meta name "stack"
+	svp_alu_op P tmp
+	
+PPP0000000000111 alu_p
+	invalid P 0
+	invalid P 2
+	meta name p
+	svp_alu_op P p
+	
+PPP0000000001RRR alu_ext
+	invalid P 0
+	invalid P 2
+	local tmp 32
+	svp_read_ext R
+	lsl val 16 tmp
+	meta name val
+	svp_alu_op P tmp
+	
+PPP0001B0000MMRR alu_ram
+	invalid P 0
+	invalid P 2
+	svp_ram_read M B R
+	local tmp 32
+	lsl val 16 tmp
+	
+	switch P
+	case 1
+	dis "sub (%s%s)" reg modestr
+	sub tmp a a
+	
+	case 3
+	dis "cmp (%s%s)" reg modestr
+	cmp tmp a
+	
+	case 4
+	dis "add (%s%s)" reg modestr
+	add tmp a a
+	
+	case 5
+	dis "and (%s%s)" reg modestr
+	and tmp a a
+	
+	case 6
+	dis "or (%s%s)" reg modestr
+	or tmp a a
+	
+	case 7
+	dis "eor (%s%s)" reg modestr
+	xor tmp a a
+	end
+	
+	update_flags ZN
+	
+PPP0101B0000MMRR alu_ram_indirect
+	invalid P 0
+	invalid P 2
+	svp_ram_read M B R
+	svp_prog_ram_read val
+	local tmp 32
+	lsl scratch1 16 tmp
+	
+	switch P
+	case 1
+	dis "sub ((%s%s))" reg modestr
+	sub tmp a a
+	
+	case 3
+	dis "cmp ((%s%s))" reg modestr
+	cmp tmp a
+	
+	case 4
+	dis "add ((%s%s))" reg modestr
+	add tmp a a
+	
+	case 5
+	dis "and ((%s%s))" reg modestr
+	and tmp a a
+	
+	case 6
+	dis "or ((%s%s))" reg modestr
+	or tmp a a
+	
+	case 7
+	dis "eor ((%s%s))" reg modestr
+	xor tmp a a
+	end
+	
+	update_flags ZN
+	
+PPP0000000001111 alu_al
+	invalid P 0
+	invalid P 2
+	local tmp 32
+	lsl a 16 tmp
+	
+	meta name al
+	svp_alu_op P tmp
+	
+PPP0011JAAAAAAAA alu_ram_direct
+	invalid P 0
+	invalid P 2
+	if J
+	meta src ram1.A
+	else
+	meta src ram0.A
+	end
+	svp_alu_op P src
+	
+PPP0010000000000 alu_immed
+	invalid P 0
+	invalid P 2
+	svp_op_fetch
+	svp_alu_op P scratch1
+	
+1001000FCCCC0OOO cond_mod
+	svp_check_cond F C
+	if istrue
+
+	switch O
+	case 2
+	asr a 1 a
+	update_flags ZN
+	
+	case 3
+	lsl a 1 a
+	update_flags ZN
+	
+	case 6
+	neg a a
+	update_flags ZN
+	
+	case 7
+	abs a a
+	update_flags N
+	end
+	end
+
+000000000DDD0SSS ld_int_int
+	dis "ld %s, %s" internal.D internal.S
+	mov internal.S internal.D
+	
+000000000DDD0101 ld_int_stack
+	dis "ld %s, stack" internal.D 
+	meta dst internal.D
+	svp_pop
+	
+0000000000110101 ld_a_stack
+	dis "ld a, stack"
+	local tmp 32
+	meta dst tmp
+	svp_pop
+	lsl tmp 16 tmp
+	and 0xFFFF a a
+	or tmp a a
+	
+0000000001110101 ld_p_stack
+	dis "ld p, stack"
+	local tmp 32
+	meta dst tmp
+	svp_pop
+	lsl tmp 16 tmp
+	and 0xFFFF p p
+	or tmp p p
+	
+0000000001010SSS ld_stack_int
+	dis "ld stack, %s" internal.S
+	svp_push internal.S
+	
+0000000001010011 ld_stack_a
+	dis "ld stack, a"
+	local tmp 32
+	lsr a 16 tmp
+	svp_push tmp
+	
+0000000001010111 ld_stack_p
+	dis "ld stack, p"
+	local tmp 32
+	lsr p 16 tmp
+	svp_push tmp
+	
+0000000000000000 ld_n1_n1
+	#nop?
+	dis "ld -, -"
+	
+0000000000000SSS ld_n1_int
+	#nop?
+	dis "nop??"
+	
+0000000000110111 ld_a_p
+	dis "ld a, p"
+	mov p a
+	
+0000000001110011 ld_p_a
+	dis "ld p, a"
+	mov a p
+	
+0000000000110011 ld_a_a
+	dis "ld a, a"
+	mov a a
+	
+0000000001110111 ld_p_p
+	dis "ld p, p"
+	mov p p
+
+000000000DDD0111 ld_int_p
+	local tmp 32
+	lsr p 16 tmp
+	mov tmp internal.D
+	dis "ld %s, p" internal.D
+	
+000000000DDD0111 ld_int_a
+	local tmp 32
+	lsr a 16 tmp
+	mov tmp internal.D
+	dis "ld %s, a" internal.D
+	
+0000000001110SSS ld_p_int
+	local tmp 32
+	lsl internal.S 16 tmp
+	mov tmp p
+	dis "ld p, %s" internal.S
+	
+0000000000110SSS ld_a_int
+	local tmp 32
+	lsl internal.S 16 tmp
+	mov tmp a
+	dis "ld a, %s" internal.S
+	
+000000000DDD0000 ld_int_n1
+	dis "ld %s, -" internal.D
+	mov 0xFFFF internal.D
+	
+0000000000110000 ld_a_n1
+	dis "ld a, -"
+	and 0xFFFF a a
+	or 0xFFFF0000 a a
+	
+0000000001110000 ld_p_n1
+	dis "ld p, -"
+	and 0xFFFF p p
+	or 0xFFFF0000 p p
+
+000000000DDD1SSS ld_int_ext
+	svp_read_ext S
+	dis "ld %s, %s" internal.D val
+	mov val internal.D
+	
+0000000000111SSS ld_a_ext
+	svp_read_ext S
+	dis "ld a, %s" val
+	local tmp 32
+	lsl val 16 tmp
+	and 0xFFFF a a
+	or tmp a a
+	
+0000000001111SSS ld_p_ext
+	svp_read_ext S
+	dis "ld p, %s" val
+	local tmp 32
+	lsl val 16 tmp
+	and 0xFFFF p p
+	or tmp p p
+	
+000000001DDD0SSS ld_ext_int
+	meta src internal.S
+	svp_write_ext D
+	switch D
+	case 7
+	dis "ld al, %s" src
+	
+	default
+	dis "ld %s, %s" external.D src
+	end
+	
+000000001DDD0011 ld_ext_a
+	local tmp 32
+	lsr a 16 tmp
+	meta src tmp
+	svp_write_ext D
+	switch D
+	case 7
+	dis "ld al, a"
+	
+	default
+	dis "ld %s, a" external.D
+	end
+	
+000000001DDD0111 ld_ext_p
+	local tmp 32
+	lsr p 16 tmp
+	meta src tmp
+	svp_write_ext D
+	switch D
+	case 7
+	dis "ld al, p"
+	
+	default
+	dis "ld %s, p" external.D
+	end
+	
+	
+000000001DDD1SSS ld_ext_ext
+	svp_read_ext S
+	meta src val
+	svp_write_ext D
+	switch D
+	case 7
+	dis "ld al, %s" src
+	default
+	dis "ld %s, %s" external.D src
+	end
+	
+0000001B0DDDMMPP ld_int_ram
+	svp_ram_read M B P
+	dis "ld %s, (%s%s)" internal.D reg modestr
+	mov val internal.D
+	
+0000001B0011MMPP ld_a_ram
+	svp_ram_read M B P
+	dis "ld a, (%s%s)" reg modestr
+	local tmp 32
+	lsl val 16 tmp
+	and 0xFFFF a a
+	or tmp a a
+	
+0000001B0111MMPP ld_p_ram
+	svp_ram_read M B P
+	dis "ld p, (%s%s)" reg modestr
+	local tmp 32
+	lsl val 16 tmp
+	and 0xFFFF p p
+	or tmp p p
+	
+0000001B0101MMPP ld_stack_ram
+	svp_ram_read M B P
+	dis "ld stack, (%s%s)" reg modestr
+	svp_push val
+	
+000010000DDD0000 ld_int_immed
+	svp_op_fetch
+	dis "ld %s, %X" internal.D scratch1
+	mov scratch1 internal.D
+	
+0000100000000000 ld_n1_immed
+	svp_op_fetch
+	dis "ld -, %X" scratch1
+
+0000100000110000 ld_a_immed
+	local tmp 32
+	svp_op_fetch
+	dis "ld a, %X" scratch1
+	lsl 16 scratch1 tmp
+	and 0xFFFF a a
+	or tmp a a
+	
+0000100001010000 ld_stack_immed
+	svp_op_fetch
+	dis "ld stack, %X" scratch1
+	svp_push scratch1
+
+0000100001110000 ld_p_immed
+	local tmp 32
+	svp_op_fetch
+	dis "ld p, %X" scratch1
+	lsl 16 scratch1 tmp
+	and 0xFFFF p p
+	or tmp p p
+	
+000010001DDD0000 ld_ext_immed
+	svp_op_fetch
+	dis "ld %s, %X", external.D, scratch1
+	meta src scratch1
+	svp_write_ext D
+	switch D
+	case 7
+	dis "ld al, %X" scratch1
+	
+	default
+	dis "ld %s, %X" external.D scratch1
+	end
+	
+0100100FCCCC0000 call_cond
+	svp_check_cond F C
+	svp_op_fetch
+	
+	if istrue
+	svp_push pc
+	mov scratch1 pc
+	end
+	
+0100110FCCCC0000 bra_cond
+	svp_check_cond F C
+	svp_op_fetch
+	if istrue
+	mov scratch1 pc
+	end
+	
+svp_prog_ram_read
+	arg src 16
+	cycles 1
+	cmp 1024 src
+	
+	if >=U
+	add src src scratch1
+	ocall prog_read_16
+	
+	else
+	mov iram.src scratch1
+	end
+
+svp_op_fetch
+	svp_prog_ram_read pc
+	add 1 pc pc
+	
+svp_run_op
+	svp_op_fetch
+	dispatch scratch1
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/svp_util.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,6 @@
+
+void svp_prog_read_16(svp_context *context)
+{
+	uint16_t address = context->scratch1 >> 1;
+	context->scratch1 = context->rom[address];
+}
--- a/system.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/system.c	Tue Dec 25 11:12:26 2018 -0800
@@ -48,7 +48,7 @@
 	return SYSTEM_UNKNOWN;
 }
 
-system_header *alloc_config_system(system_type stype, system_media *media, uint32_t opts, uint8_t force_region, rom_info *info_out)
+system_header *alloc_config_system(system_type stype, system_media *media, uint32_t opts, uint8_t force_region)
 {
 	void *lock_on = NULL;
 	uint32_t lock_on_size = 0;
@@ -59,10 +59,10 @@
 	switch (stype)
 	{
 	case SYSTEM_GENESIS:
-		return &(alloc_config_genesis(media->buffer, media->size, lock_on, lock_on_size, opts, force_region, info_out))->header;
+		return &(alloc_config_genesis(media->buffer, media->size, lock_on, lock_on_size, opts, force_region))->header;
 #ifndef NO_Z80
 	case SYSTEM_SMS:
-		return &(alloc_configure_sms(media, opts, force_region, info_out))->header;
+		return &(alloc_configure_sms(media, opts, force_region))->header;
 #endif
 	default:
 		return NULL;
--- a/system.h	Sun Dec 31 10:11:16 2017 -0800
+++ b/system.h	Tue Dec 25 11:12:26 2018 -0800
@@ -21,8 +21,12 @@
 typedef uint16_t (*system_fun_r16)(system_header *);
 typedef void (*system_str_fun)(system_header *, char *);
 typedef uint8_t (*system_str_fun_r8)(system_header *, char *);
-typedef void (*speed_system_fun)(system_header *, uint32_t);
+typedef void (*system_u32_fun)(system_header *, uint32_t);
+typedef void (*system_u8_fun)(system_header *, uint8_t);
 typedef uint8_t (*system_u8_fun_r8)(system_header *, uint8_t);
+typedef void (*system_u8_u8_fun)(system_header *, uint8_t, uint8_t);
+typedef void (*system_mabs_fun)(system_header *, uint8_t, uint16_t, uint16_t);
+typedef void (*system_mrel_fun)(system_header *, uint8_t, int32_t, int32_t);
 
 #include "arena.h"
 #include "romdb.h"
@@ -38,15 +42,26 @@
 	system_fun        soft_reset;
 	system_fun        free_context;
 	system_fun_r16    get_open_bus_value;
-	speed_system_fun  set_speed_percent;
+	system_u32_fun    set_speed_percent;
 	system_fun        inc_debug_mode;
-	system_fun        inc_debug_pal;
+	system_u8_u8_fun  gamepad_down;
+	system_u8_u8_fun  gamepad_up;
+	system_u8_u8_fun  mouse_down;
+	system_u8_u8_fun  mouse_up;
+	system_mabs_fun   mouse_motion_absolute;
+	system_mrel_fun   mouse_motion_relative;
+	system_u8_fun     keyboard_down;
+	system_u8_fun     keyboard_up;
+	system_fun        config_updated;
+	rom_info          info;
 	arena             *arena;
 	char              *next_rom;
 	char              *save_dir;
 	uint8_t           enter_debugger;
 	uint8_t           should_exit;
 	uint8_t           save_state;
+	uint8_t           delayed_load_slot;
+	uint8_t           has_keyboard;
 	debugger_type     debugger_type;
 	system_type       type;
 };
@@ -63,6 +78,6 @@
 #define OPT_ADDRESS_LOG (1U << 31U)
 
 system_type detect_system_type(system_media *media);
-system_header *alloc_config_system(system_type stype, system_media *media, uint32_t opts, uint8_t force_region, rom_info *info_out);
+system_header *alloc_config_system(system_type stype, system_media *media, uint32_t opts, uint8_t force_region);
 
 #endif //SYSTEM_H_
--- a/tern.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/tern.c	Tue Dec 25 11:12:26 2018 -0800
@@ -45,6 +45,12 @@
 		(*cur)->left = NULL;
 		(*cur)->right = NULL;
 		(*cur)->el = 0;
+		(*cur)->valtype = TVAL_NONE;
+	}
+	if ((*cur)->valtype == TVAL_PTR) {
+		//not freeing tern nodes can also cause leaks, but handling freeing those here is problematic
+		//since updating a sub-tree may involve creating a new root node
+		free((*cur)->straight.value.ptrval);
 	}
 	(*cur)->straight.value = value;
 	(*cur)->valtype = valtype;
@@ -132,6 +138,39 @@
 	return NULL;
 }
 
+uint8_t tern_delete(tern_node **head, char const *key, tern_val *out)
+{
+	tern_node *cur = *head, **last = head;
+	while (cur)
+	{
+		if (cur->el == *key) {
+			if (*key) {
+				last = &cur->straight.next;
+				cur = cur->straight.next;
+				key++;
+			} else {
+				break;
+			}
+		} else if (*key < cur->el) {
+			last = &cur->left;
+			cur = cur->left;
+		} else {
+			last = &cur->right;
+			cur = cur->right;
+		}
+	}
+	if (!cur) {
+		return TVAL_NONE;
+	}
+	*last = cur->right;
+	uint8_t valtype = cur->valtype;
+	if (out) {
+		*out = cur->straight.value;
+	}
+	free(cur);
+	return valtype;
+}
+
 tern_val tern_find_path_default(tern_node *head, char const *key, tern_val def, uint8_t req_valtype)
 {
 	tern_val ret;
@@ -175,6 +214,37 @@
 	return tern_insert(head, key, val, TVAL_NODE);
 }
 
+tern_node *tern_insert_path(tern_node *head, char const *key, tern_val val, uint8_t valtype)
+{
+	const char *next_key = key + strlen(key) + 1;
+	if (*next_key) {
+		tern_node *child = tern_find_node(head, key);
+		child = tern_insert_path(child, next_key, val, valtype);
+		return tern_insert_node(head, key, child);
+	} else {
+		return tern_insert(head, key, val, valtype);
+	}
+}
+
+uint8_t tern_delete_path(tern_node **head, char const *key, tern_val *out)
+{
+	const char *next_key = key + strlen(key) + 1;
+	if (*next_key) {
+		tern_node *child = tern_find_node(*head, key);
+		if (!child) {
+			return TVAL_NONE;
+		}
+		tern_node *tmp = child;
+		uint8_t valtype = tern_delete_path(&tmp, next_key, out);
+		if (tmp != child) {
+			*head = tern_insert_node(*head, key, tmp);
+		}
+		return valtype;
+	} else {
+		return tern_delete(head, key, out);
+	}
+}
+
 uint32_t tern_count(tern_node *head)
 {
 	uint32_t count = 0;
@@ -202,7 +272,7 @@
 	if (head->left) {
 		tern_foreach_int(head->left, fun, data, keybuf, pos);
 	}
-	if (head->el) {
+	if (head->el && head->straight.next) {
 		if (pos == MAX_ITER_KEY) {
 			fatal_error("tern_foreach_int: exceeded maximum key size");
 		}
--- a/tern.h	Sun Dec 31 10:11:16 2017 -0800
+++ b/tern.h	Tue Dec 25 11:12:26 2018 -0800
@@ -43,10 +43,13 @@
 void * tern_find_ptr_default(tern_node * head, char const * key, void * def);
 void * tern_find_ptr(tern_node * head, char const * key);
 tern_node *tern_find_node(tern_node *head, char const *key);
+uint8_t tern_delete(tern_node **head, char const *key, tern_val *out);
 tern_val tern_find_path_default(tern_node *head, char const *key, tern_val def, uint8_t req_valtype);
 tern_val tern_find_path(tern_node *head, char const *key, uint8_t valtype);
+uint8_t tern_delete_path(tern_node **head, char const *key, tern_val *out);
 tern_node * tern_insert_ptr(tern_node * head, char const * key, void * value);
 tern_node * tern_insert_node(tern_node *head, char const *key, tern_node *value);
+tern_node *tern_insert_path(tern_node *head, char const *key, tern_val val, uint8_t valtype);
 uint32_t tern_count(tern_node *head);
 void tern_foreach(tern_node *head, iter_fun fun, void *data);
 char * tern_int_key(uint32_t key, char * buf);
--- a/util.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/util.c	Tue Dec 25 11:12:26 2018 -0800
@@ -204,6 +204,85 @@
 	*(output++) = 0;
 }
 
+char *utf16be_to_utf8(uint8_t *buf, uint32_t max_size)
+{
+	uint8_t *cur = buf;
+	uint32_t converted_size = 0;
+	for (uint32_t i = 0; i < max_size; i++, cur+=2)
+	{
+		uint16_t code = *cur << 16 | cur[1];
+		if (!code) {
+			break;
+		}
+		if (code < 0x80) {
+			converted_size++;
+		} else if (code < 0x800) {
+			converted_size += 2;
+		} else {
+			//TODO: Deal with surrogate pairs
+			converted_size += 3;
+		}
+	}
+	char *out = malloc(converted_size + 1);
+	char *cur_out = out;
+	cur = buf;
+	for (uint32_t i = 0; i < max_size; i++, cur+=2)
+	{
+		uint16_t code = *cur << 16 | cur[1];
+		if (!code) {
+			break;
+		}
+		if (code < 0x80) {
+			*(cur_out++) = code;
+		} else if (code < 0x800) {
+			*(cur_out++) = 0xC0 | code >> 6;
+			*(cur_out++) = 0x80 | (code & 0x3F);
+		} else {
+			//TODO: Deal with surrogate pairs
+			*(cur_out++) = 0xF0 | code >> 12;
+			*(cur_out++) = 0x80 | (code >> 6 & 0x3F);
+			*(cur_out++) = 0x80 | (code & 0x3F);
+		}
+	}
+	*cur_out = 0;
+	return out;
+}
+
+int utf8_codepoint(const char **text)
+{
+	uint8_t initial = **text;
+	(*text)++;
+	if (initial < 0x80) {
+		return initial;
+	}
+	int base = 0;
+	uint8_t extended_bytes = 0;
+	if ((initial & 0xE0) == 0xC0) {
+		base = 0x80;
+		initial &= 0x1F;
+		extended_bytes = 1;
+	} else if ((initial & 0xF0) == 0xE0) {
+		base = 0x800;
+		initial &= 0xF;
+		extended_bytes = 2;
+	} else if ((initial & 0xF8) == 0xF0) {
+		base = 0x10000;
+		initial &= 0x7;
+		extended_bytes = 3;
+	}
+	int value = initial;
+	for (uint8_t i = 0; i < extended_bytes; i++)
+	{
+		if ((**text & 0xC0) != 0x80) {
+			return -1;
+		}
+		value = value << 6;
+		value |= (**text) & 0x3F;
+		(*text)++;
+	}
+	return value + base;
+}
+
 char is_path_sep(char c)
 {
 #ifdef _WIN32
@@ -224,11 +303,11 @@
 	return is_path_sep(path[0]);
 }
 
-char * basename_no_extension(char *path)
+char * basename_no_extension(const char *path)
 {
-	char *lastdot = NULL;
-	char *lastslash = NULL;
-	char *cur;
+	const char *lastdot = NULL;
+	const char *lastslash = NULL;
+	const char *cur;
 	for (cur = path; *cur; cur++)
 	{
 		if (*cur == '.') {
@@ -250,11 +329,11 @@
 	return barename;
 }
 
-char *path_extension(char *path)
+char *path_extension(char const *path)
 {
-	char *lastdot = NULL;
-	char *lastslash = NULL;
-	char *cur;
+	char const *lastdot = NULL;
+	char const *lastslash = NULL;
+	char const *cur;
 	for (cur = path; *cur; cur++)
 	{
 		if (*cur == '.') {
@@ -270,10 +349,28 @@
 	return strdup(lastdot+1);
 }
 
-char * path_dirname(char *path)
+uint8_t path_matches_extensions(char *path, char **ext_list, uint32_t num_exts)
 {
-	char *lastslash = NULL;
-	char *cur;
+	char *ext = path_extension(path);
+	if (!ext) {
+		return 0;
+	}
+	uint32_t extidx;
+	for (extidx = 0; extidx < num_exts; extidx++)
+	{
+		if (!strcasecmp(ext, ext_list[extidx])) {
+			free(ext);
+			return 1;
+		}
+	}
+	free(ext);
+	return 0;
+}
+
+char * path_dirname(const char *path)
+{
+	const char *lastslash = NULL;
+	const char *cur;
 	for (cur = path; *cur; cur++)
 	{
 		if (is_path_sep(*cur)) {
@@ -489,7 +586,7 @@
 	return (time_t)wintime;
 }
 
-int ensure_dir_exists(char *path)
+int ensure_dir_exists(const char *path)
 {
 	if (CreateDirectory(path, NULL)) {
 		return 1;
@@ -626,6 +723,7 @@
 	if (numret) {
 		*numret = pos;
 	}
+	closedir(d);
 	return ret;
 }
 
@@ -643,7 +741,7 @@
 #endif
 }
 
-int ensure_dir_exists(char *path)
+int ensure_dir_exists(const char *path)
 {
 	struct stat st;
 	if (stat(path, &st)) {
@@ -680,6 +778,22 @@
 	free(list);
 }
 
+static int sort_dir_alpha(const void *a, const void *b)
+{
+	const dir_entry *da, *db;
+	da = a;
+	db = b;
+	if (da->is_dir != db->is_dir) {
+		return db->is_dir - da->is_dir;
+	}
+	return strcasecmp(((dir_entry *)a)->name, ((dir_entry *)b)->name);
+}
+
+void sort_dir_list(dir_entry *list, size_t num_entries)
+{
+	qsort(list, num_entries, sizeof(dir_entry), sort_dir_alpha);
+}
+
 #ifdef __ANDROID__
 
 #include <SDL.h>
@@ -759,6 +873,7 @@
 	} else {
 		ret = NULL;
 	}
+	fclose(f);
 	return ret;
 }
 
--- a/util.h	Sun Dec 31 10:11:16 2017 -0800
+++ b/util.h	Tue Dec 25 11:12:26 2018 -0800
@@ -34,16 +34,22 @@
 char * split_keyval(char * text);
 //Takes a binary byte buffer and produces a lowercase hex string
 void bin_to_hex(uint8_t *output, uint8_t *input, uint64_t size);
+//Takes an (optionally) null-terminated UTF16-BE string and converts a maximum of max_size code-units to UTF-8
+char *utf16be_to_utf8(uint8_t *buf, uint32_t max_size);
+//Returns the next Unicode codepoint from a utf-8 string
+int utf8_codepoint(const char **text);
 //Determines whether a character is a valid path separator for the current platform
 char is_path_sep(char c);
 //Determines whether a path is considered an absolute path on the current platform
 char is_absolute_path(char *path);
 //Returns the basename of a path with th extension (if any) stripped
-char * basename_no_extension(char *path);
+char * basename_no_extension(const char *path);
 //Returns the extension from a path or NULL if there is no extension
-char *path_extension(char *path);
+char *path_extension(char const *path);
+//Returns true if the given path matches one of the extensions in the list
+uint8_t path_matches_extensions(char *path, char **ext_list, uint32_t num_exts);
 //Returns the directory portion of a path or NULL if there is no directory part
-char *path_dirname(char *path);
+char *path_dirname(const char *path);
 //Gets the smallest power of two that is >= a certain value, won't work for values > 0x80000000
 uint32_t nearest_pow2(uint32_t val);
 //Should be called by main with the value of argv[0] for use by get_exe_dir
@@ -62,10 +68,12 @@
 dir_entry *get_dir_list(char *path, size_t *numret);
 //Frees a dir list returned by get_dir_list
 void free_dir_list(dir_entry *list, size_t numentries);
+//Performs a case-insensitive sort by file name on a dir list
+void sort_dir_list(dir_entry *list, size_t num_entries);
 //Gets the modification time of a file
 time_t get_modification_time(char *path);
 //Recusrively creates a directory if it does not exist
-int ensure_dir_exists(char *path);
+int ensure_dir_exists(const char *path);
 //Returns the contents of a symlink in a newly allocated string
 char * readlink_alloc(char * path);
 //Prints an error message to stderr and to a message box if not in headless mode and then exits
--- a/vdp.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/vdp.c	Tue Dec 25 11:12:26 2018 -0800
@@ -19,7 +19,6 @@
 #define MAP_BIT_H_FLIP 0x800
 #define MAP_BIT_V_FLIP 0x1000
 
-#define SCROLL_BUFFER_SIZE 32
 #define SCROLL_BUFFER_MASK (SCROLL_BUFFER_SIZE-1)
 #define SCROLL_BUFFER_DRAW (SCROLL_BUFFER_SIZE/2)
 
@@ -138,13 +137,9 @@
 
 static uint8_t color_map_init_done;
 
-void init_vdp_context(vdp_context * context, uint8_t region_pal)
+vdp_context *init_vdp_context(uint8_t region_pal)
 {
-	memset(context, 0, sizeof(*context));
-	context->vdpmem = malloc(VRAM_SIZE);
-	memset(context->vdpmem, 0, VRAM_SIZE);
-	/*
-	*/
+	vdp_context *context = calloc(1, sizeof(vdp_context) + VRAM_SIZE);
 	if (headless) {
 		context->output = malloc(LINEBUF_SIZE * sizeof(uint32_t));
 		context->output_pitch = 0;
@@ -152,10 +147,6 @@
 		context->cur_buffer = FRAMEBUFFER_ODD;
 		context->fb = render_get_framebuffer(FRAMEBUFFER_ODD, &context->output_pitch);
 	}
-	context->linebuf = malloc(LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2);
-	memset(context->linebuf, 0, LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2);
-	context->tmp_buf_a = context->linebuf + LINEBUF_SIZE;
-	context->tmp_buf_b = context->tmp_buf_a + SCROLL_BUFFER_SIZE;
 	context->sprite_draws = MAX_DRAWS;
 	context->fifo_write = 0;
 	context->fifo_read = -1;
@@ -250,12 +241,11 @@
 	if (!headless) {
 		context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * context->border_top);
 	}
+	return context;
 }
 
 void vdp_free(vdp_context *context)
 {
-	free(context->vdpmem);
-	free(context->linebuf);
 	free(context);
 }
 
@@ -528,6 +518,8 @@
 		   context->regs[REG_DMASRC_H],
 		       context->regs[REG_DMASRC_H] << 17 | context->regs[REG_DMASRC_M] << 9 | context->regs[REG_DMASRC_L] << 1,
 			   src_types[context->regs[REG_DMASRC_H] >> 6 & 3]);
+	uint8_t old_flags = context->flags;
+	uint8_t old_flags2 = context->flags2;
 	printf("\n**Internal Group**\n"
 	       "Address: %X\n"
 	       "CD:      %X - %s\n"
@@ -541,8 +533,9 @@
 		   (context->flags & FLAG_PENDING) ? "word" : (context->flags2 & FLAG2_BYTE_PENDING) ? "byte" : "none",
 		   context->vcounter, context->hslot*2, (context->flags2 & FLAG2_VINT_PENDING) ? "true" : "false",
 		   (context->flags2 & FLAG2_HINT_PENDING) ? "true" : "false", vdp_control_port_read(context));
-
-	//TODO: Window Group, DMA Group
+	//restore flags as calling vdp_control_port_read can change them
+	context->flags = old_flags;
+	context->flags2 = old_flags2;
 }
 
 static uint8_t is_active(vdp_context *context)
@@ -875,14 +868,17 @@
 	context->vdpmem[address] = value;
 }
 
+#define DMA_FILL 0x80
+#define DMA_COPY 0xC0
+#define DMA_TYPE_MASK 0xC0
 static void external_slot(vdp_context * context)
 {
-	if ((context->flags & FLAG_DMA_RUN) && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80 && context->fifo_read < 0) {
+	if ((context->flags & FLAG_DMA_RUN) && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_FILL && context->fifo_read < 0) {
 		context->fifo_read = (context->fifo_write-1) & (FIFO_SIZE-1);
 		fifo_entry * cur = context->fifo + context->fifo_read;
 		cur->cycle = context->cycles;
 		cur->address = context->address;
-		cur->partial = 2;
+		cur->partial = 1;
 		vdp_advance_dma(context);
 	}
 	fifo_entry * start = context->fifo + context->fifo_read;
@@ -893,20 +889,16 @@
 			if ((context->regs[REG_MODE_2] & (BIT_128K_VRAM|BIT_MODE_5)) == (BIT_128K_VRAM|BIT_MODE_5)) {
 				vdp_check_update_sat(context, start->address, start->value);
 				write_vram_word(context, start->address, start->value);
-			} else if (start->partial) {
-				//printf("VRAM Write: %X to %X at %d (line %d, slot %d)\n", start->value, start->address ^ 1, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16);
-				uint8_t byte = start->partial == 2 ? start->value >> 8 : start->value;
-				if (start->partial > 1) {
-					vdp_check_update_sat_byte(context, start->address ^ 1, byte);
-				}
+			} else {
+				uint8_t byte = start->partial == 1 ? start->value >> 8 : start->value;
+				vdp_check_update_sat_byte(context, start->address ^ 1, byte);
 				write_vram_byte(context, start->address ^ 1, byte);
-			} else {
-				//printf("VRAM Write High: %X to %X at %d (line %d, slot %d)\n", start->value >> 8, start->address, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16);
-				vdp_check_update_sat(context, start->address, start->value);
-				write_vram_byte(context, start->address, start->value >> 8);
-				start->partial = 1;
-				//skip auto-increment and removal of entry from fifo
-				return;
+				if (!start->partial) {
+					start->address = start->address ^ 1;
+					start->partial = 1;
+					//skip auto-increment and removal of entry from fifo
+					return;
+				}
 			}
 			break;
 		case CRAM_WRITE: {
@@ -921,7 +913,7 @@
 				}
 				write_cram(context, start->address, val);
 			} else {
-				write_cram(context, start->address, start->partial == 2 ? context->fifo[context->fifo_write].value : start->value);
+				write_cram(context, start->address, start->partial ? context->fifo[context->fifo_write].value : start->value);
 			}
 			break;
 		}
@@ -937,7 +929,7 @@
 						context->vsram[(start->address/2) & 63] |= start->value;
 					}
 				} else {
-					context->vsram[(start->address/2) & 63] = start->partial == 2 ? context->fifo[context->fifo_write].value : start->value;
+					context->vsram[(start->address/2) & 63] = start->partial ? context->fifo[context->fifo_write].value : start->value;
 				}
 			}
 
@@ -945,12 +937,12 @@
 		}
 		context->fifo_read = (context->fifo_read+1) & (FIFO_SIZE-1);
 		if (context->fifo_read == context->fifo_write) {
-			if ((context->cd & 0x20) && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) {
+			if ((context->cd & 0x20) && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_FILL) {
 				context->flags |= FLAG_DMA_RUN;
 			}
 			context->fifo_read = -1;
 		}
-	} else if ((context->flags & FLAG_DMA_RUN) && (context->regs[REG_DMASRC_H] & 0xC0) == 0xC0) {
+	} else if ((context->flags & FLAG_DMA_RUN) && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_COPY) {
 		if (context->flags & FLAG_READ_FETCHED) {
 			write_vram_byte(context, context->address ^ 1, context->prefetch);
 			
@@ -1275,6 +1267,7 @@
 static void render_map_output(uint32_t line, int32_t col, vdp_context * context)
 {
 	uint32_t *dst;
+	uint8_t *debug_dst;
 	uint8_t output_disabled = (context->test_port & TEST_BIT_DISABLE) != 0;
 	uint8_t test_layer = context->test_port >> 7 & 3;
 	if (context->state == PREPARING && !test_layer) {
@@ -1307,31 +1300,34 @@
 	{
 		col-=2;
 		dst = context->output + BORDER_LEFT + col * 8;
-		if (context->debug < 2) {
-			sprite_buf = context->linebuf + col * 8;
-			uint8_t a_src, src;
-			if (context->flags & FLAG_WINDOW) {
-				plane_a_off = context->buf_a_off;
-				a_src = DBG_SRC_W;
-			} else {
-				plane_a_off = context->buf_a_off - (context->hscroll_a & 0xF);
-				a_src = DBG_SRC_A;
-			}
-			plane_b_off = context->buf_b_off - (context->hscroll_b & 0xF);
-			//printf("A | tmp_buf offset: %d\n", 8 - (context->hscroll_a & 0x7));
+		debug_dst = context->layer_debug_buf + BORDER_LEFT + col * 8;
+		
+		sprite_buf = context->linebuf + col * 8;
+		uint8_t a_src, src;
+		if (context->flags & FLAG_WINDOW) {
+			plane_a_off = context->buf_a_off;
+			a_src = DBG_SRC_W;
+		} else {
+			plane_a_off = context->buf_a_off - (context->hscroll_a & 0xF);
+			a_src = DBG_SRC_A;
+		}
+		plane_b_off = context->buf_b_off - (context->hscroll_b & 0xF);
+		//printf("A | tmp_buf offset: %d\n", 8 - (context->hscroll_a & 0x7));
 
-			if (context->regs[REG_MODE_4] & BIT_HILIGHT) {
-				for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) {
-					plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK);
-					plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK);
-					uint8_t pixel = context->regs[REG_BG_COLOR];
-					uint32_t *colors = context->colors;
-					src = DBG_SRC_BG;
+		if (context->regs[REG_MODE_4] & BIT_HILIGHT) {
+			for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) {
+				plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK);
+				plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK);
+				uint8_t pixel = context->regs[REG_BG_COLOR];
+				uint32_t *colors = context->colors;
+				src = DBG_SRC_BG;
+				uint8_t intensity = 0;
+				if (col || !(context->regs[REG_MODE_1] & BIT_COL0_MASK) || i >= 8) {
 					if (*plane_b & 0xF) {
 						pixel = *plane_b;
 						src = DBG_SRC_B;
 					}
-					uint8_t intensity = *plane_b & BUF_BIT_PRIORITY;
+					intensity = *plane_b & BUF_BIT_PRIORITY;
 					if (*plane_a & 0xF && (*plane_a & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
 						pixel = *plane_a;
 						src = a_src;
@@ -1352,57 +1348,53 @@
 							}
 						}
 					}
-					if (output_disabled) {
-						pixel = 0x3F;
-					}
-					if (!intensity) {
-						src |= DBG_SHADOW;
-						colors += CRAM_SIZE;
-					} else if (intensity ==  BUF_BIT_PRIORITY*2) {
-						src |= DBG_HILIGHT;
-						colors += CRAM_SIZE*2;
+				}
+				if (output_disabled) {
+					pixel = 0x3F;
+				}
+				if (!intensity) {
+					src |= DBG_SHADOW;
+					colors += CRAM_SIZE;
+				} else if (intensity ==  BUF_BIT_PRIORITY*2) {
+					src |= DBG_HILIGHT;
+					colors += CRAM_SIZE*2;
+				}
+				//TODO: Verify how test register stuff interacts with shadow/highlight
+				//TODO: Simulate CRAM corruption from bus fight
+				switch (test_layer)
+				{
+				case 1:
+					pixel &= *sprite_buf;
+					if (output_disabled && pixel) {
+						src = DBG_SRC_S;
 					}
-					//TODO: Verify how test register stuff interacts with shadow/highlight
-					//TODO: Simulate CRAM corruption from bus fight
-					switch (test_layer)
-					{
-					case 1:
-						pixel &= *sprite_buf;
-						if (output_disabled && pixel) {
-							src = DBG_SRC_S;
-						}
-						break;
-					case 2:
-						pixel &= *plane_a;
-						if (output_disabled && pixel) {
-							src = DBG_SRC_A;
-						}
-						break;
-					case 3:
-						pixel &= *plane_b;
-						if (output_disabled && pixel) {
-							src = DBG_SRC_B;
-						}
-						break;
+					break;
+				case 2:
+					pixel &= *plane_a;
+					if (output_disabled && pixel) {
+						src = DBG_SRC_A;
+					}
+					break;
+				case 3:
+					pixel &= *plane_b;
+					if (output_disabled && pixel) {
+						src = DBG_SRC_B;
 					}
-
-					uint32_t outpixel;
-					if (context->debug) {
-						outpixel = context->debugcolors[src];
-					} else {
-						outpixel = colors[pixel & 0x3F];
-					}
-					*(dst++) = outpixel;
+					break;
 				}
-			} else {
-				for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) {
-					plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK);
-					plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK);
-					uint8_t pixel = context->regs[REG_BG_COLOR];
-					src = DBG_SRC_BG;
-					if (output_disabled) {
-						pixel = 0x3F;
-					} else {
+				*(debug_dst++) = src;
+				*(dst++) = colors[pixel & 0x3F];
+			}
+		} else {
+			for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) {
+				plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK);
+				plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK);
+				uint8_t pixel = context->regs[REG_BG_COLOR];
+				src = DBG_SRC_BG;
+				if (output_disabled) {
+					pixel = 0x3F;
+				} else {
+					if (col || !(context->regs[REG_MODE_1] & BIT_COL0_MASK) || i >= 8) {
 						if (*plane_b & 0xF) {
 							pixel = *plane_b;
 							src = DBG_SRC_B;
@@ -1416,83 +1408,36 @@
 							src = DBG_SRC_S;
 						}
 					}
-					//TODO: Simulate CRAM corruption from bus fight
-					switch (test_layer)
-					{
-					case 1:
-						pixel &= *sprite_buf;
-						if (output_disabled && pixel) {
-							src = DBG_SRC_S;
-						}
-						break;
-					case 2:
-						pixel &= *plane_a;
-						if (output_disabled && pixel) {
-							src = DBG_SRC_A;
-						}
-						break;
-					case 3:
-						pixel &= *plane_b;
-						if (output_disabled && pixel) {
-							src = DBG_SRC_B;
-						}
-						break;
-					}
-					uint32_t outpixel;
-					if (context->debug) {
-						outpixel = context->debugcolors[src];
-					} else {
-						outpixel = context->colors[pixel & 0x3F];
-					}
-					*(dst++) = outpixel;
 				}
-			}
-		} else if (context->debug == 2) {
-			if (col < 32) {
-				*(dst++) = context->colors[col * 2];
-				*(dst++) = context->colors[col * 2];
-				*(dst++) = context->colors[col * 2];
-				*(dst++) = context->colors[col * 2];
-				*(dst++) = context->colors[col * 2 + 1];
-				*(dst++) = context->colors[col * 2 + 1];
-				*(dst++) = context->colors[col * 2 + 1];
-				*(dst++) = context->colors[col * 2 + 1];
-				*(dst++) = context->colors[col * 2 + 2];
-				*(dst++) = context->colors[col * 2 + 2];
-				*(dst++) = context->colors[col * 2 + 2];
-				*(dst++) = context->colors[col * 2 + 2];
-				*(dst++) = context->colors[col * 2 + 3];
-				*(dst++) = context->colors[col * 2 + 3];
-				*(dst++) = context->colors[col * 2 + 3];
-				*(dst++) = context->colors[col * 2 + 3];
-			} else if (col == 32 || line >= 192) {
-				for (int32_t i = 0; i < 16; i ++) {
-					*(dst++) = 0;
+				//TODO: Simulate CRAM corruption from bus fight
+				switch (test_layer)
+				{
+				case 1:
+					pixel &= *sprite_buf;
+					if (output_disabled && pixel) {
+						src = DBG_SRC_S;
+					}
+					break;
+				case 2:
+					pixel &= *plane_a;
+					if (output_disabled && pixel) {
+						src = DBG_SRC_A;
+					}
+					break;
+				case 3:
+					pixel &= *plane_b;
+					if (output_disabled && pixel) {
+						src = DBG_SRC_B;
+					}
+					break;
 				}
-			} else {
-				for (int32_t i = 0; i < 16; i ++) {
-					*(dst++) = context->colors[line / 3 + (col - 34) * 0x20];
-				}
-			}
-		} else {
-			uint32_t base = (context->debug - 3) * 0x200;
-			uint32_t cell = base + (line / 8) * (context->regs[REG_MODE_4] & BIT_H40 ? 40 : 32) + col;
-			uint32_t address = (cell * 32 + (line % 8) * 4) & 0xFFFF;
-			for (int32_t i = 0; i < 4; i ++) {
-				*(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] >> 4)];
-				*(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] & 0xF)];
-				address++;
-			}
-			cell++;
-			address = (cell * 32 + (line % 8) * 4) & 0xFFFF;
-			for (int32_t i = 0; i < 4; i ++) {
-				*(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] >> 4)];
-				*(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] & 0xF)];
-				address++;
+				*(dst++) = context->colors[pixel & 0x3F];
+				*(debug_dst++) = src;
 			}
 		}
 	} else {
 		dst = context->output;
+		debug_dst = context->layer_debug_buf;
 		uint8_t pixel = context->regs[REG_BG_COLOR] & 0x3F;
 		if (output_disabled) {
 			pixel = 0x3F;
@@ -1503,9 +1448,11 @@
 			{
 			case 1:
 				bg_color = context->colors[0];
-				for (int i = 0; i < BORDER_LEFT; i++, dst++)
+				for (int i = 0; i < BORDER_LEFT; i++, dst++, debug_dst++)
 				{
 					*dst = bg_color;
+					*debug_dst = DBG_SRC_BG;
+					
 				}
 				break;
 			case 2: {
@@ -1515,9 +1462,10 @@
 				i = 0;
 				uint8_t buf_off = context->buf_a_off - (context->hscroll_a & 0xF) + (16 - BORDER_LEFT);
 				//uint8_t *src = context->tmp_buf_a + ((context->buf_a_off + (i ? 0 : (16 - BORDER_LEFT) - (context->hscroll_a & 0xF))) & SCROLL_BUFFER_MASK); 
-				for (; i < BORDER_LEFT; buf_off++, i++, dst++)
+				for (; i < BORDER_LEFT; buf_off++, i++, dst++, debug_dst++)
 				{
 					*dst = context->colors[context->tmp_buf_a[buf_off & SCROLL_BUFFER_MASK]];
+					*debug_dst = DBG_SRC_A;
 				}
 				break;
 			}
@@ -1527,17 +1475,19 @@
 				i = 0;
 				uint8_t buf_off = context->buf_b_off - (context->hscroll_b & 0xF) + (16 - BORDER_LEFT);
 				//uint8_t *src = context->tmp_buf_b + ((context->buf_b_off + (i ? 0 : (16 - BORDER_LEFT) - (context->hscroll_b & 0xF))) & SCROLL_BUFFER_MASK); 
-				for (; i < BORDER_LEFT; buf_off++, i++, dst++)
+				for (; i < BORDER_LEFT; buf_off++, i++, dst++, debug_dst++)
 				{
 					*dst = context->colors[context->tmp_buf_b[buf_off & SCROLL_BUFFER_MASK]];
+					*debug_dst = DBG_SRC_B;
 				}
 				break;
 			}
 			}
 		} else {
-			for (int i = 0; i < BORDER_LEFT; i++, dst++)
+			for (int i = 0; i < BORDER_LEFT; i++, dst++, debug_dst++)
 			{
 				*dst = bg_color;
+				*debug_dst = DBG_SRC_BG;
 			}
 		}
 	}
@@ -1588,6 +1538,7 @@
 	
 	uint8_t bgcolor = 0x10 | (context->regs[REG_BG_COLOR] & 0xF) + CRAM_SIZE*3;
 	uint32_t *dst = context->output + col * 8 + BORDER_LEFT;
+	uint8_t *debug_dst = context->layer_debug_buf + col * 8 + BORDER_LEFT;
 	if (context->state == PREPARING) {
 		for (int i = 0; i < 16; i++)
 		{
@@ -1596,65 +1547,30 @@
 		context->done_output = dst;
 		return;
 	}
-	if (context->debug < 2) {
-		if (col || !(context->regs[REG_MODE_1] & BIT_COL0_MASK)) {
-			uint8_t *sprite_src = context->linebuf + col * 8;
-			if (context->regs[REG_MODE_1] & BIT_SPRITE_8PX) {
-				sprite_src += 8;
-			}
-			for (int i = 0; i < 8; i++, sprite_src++)
-			{
-				uint8_t *bg_src = context->tmp_buf_a + ((8 + i + col * 8 - (context->hscroll_a & 0x7)) & 15);
-				if ((*bg_src & 0x4F) > 0x40 || !*sprite_src) {
-					//background plane has priority and is opaque or sprite layer is transparent
-					if (context->debug) {
-						*(dst++) = context->debugcolors[DBG_SRC_A];
-					} else {
-						*(dst++) = context->colors[(*bg_src & 0x1F) + CRAM_SIZE*3];
-					}
-				} else {
-					//sprite layer is opaque and not covered by high priority BG pixels
-					if (context->debug) {
-						*(dst++) = context->debugcolors[DBG_SRC_S];
-					} else {
-						*(dst++) = context->colors[*sprite_src | 0x10 + CRAM_SIZE*3];
-					}
-				}
-			}
-		} else {
-			for (int i = 0; i < 8; i++)
-			{
-				*(dst++) = context->colors[bgcolor];
+	
+	if (col || !(context->regs[REG_MODE_1] & BIT_COL0_MASK)) {
+		uint8_t *sprite_src = context->linebuf + col * 8;
+		if (context->regs[REG_MODE_1] & BIT_SPRITE_8PX) {
+			sprite_src += 8;
+		}
+		for (int i = 0; i < 8; i++, sprite_src++)
+		{
+			uint8_t *bg_src = context->tmp_buf_a + ((8 + i + col * 8 - (context->hscroll_a & 0x7)) & 15);
+			if ((*bg_src & 0x4F) > 0x40 || !*sprite_src) {
+				//background plane has priority and is opaque or sprite layer is transparent
+				*(dst++) = context->colors[(*bg_src & 0x1F) + CRAM_SIZE*3];
+				*(debug_dst++) = DBG_SRC_A;
+			} else {
+				//sprite layer is opaque and not covered by high priority BG pixels
+				*(dst++) = context->colors[*sprite_src | 0x10 + CRAM_SIZE*3];
+				*(debug_dst++) = DBG_SRC_S;
 			}
 		}
-	} else if (context->debug == 2) {
+	} else {
 		for (int i = 0; i < 8; i++)
 		{
-			*(dst++) = context->colors[CRAM_SIZE*3 + col];
-		}
-	} else {
-		uint32_t cell = (line / 8) * 32 + col;
-		uint32_t address = cell * 32 + (line % 8) * 4;
-		uint32_t m4_address = mode4_address_map[address & 0x3FFF];
-		uint32_t pixel = planar_to_chunky[context->vdpmem[m4_address]] << 1;
-		pixel |= planar_to_chunky[context->vdpmem[m4_address + 1]];
-		m4_address = mode4_address_map[(address + 2) & 0x3FFF];
-		pixel |= planar_to_chunky[context->vdpmem[m4_address]] << 3;
-		pixel |= planar_to_chunky[context->vdpmem[m4_address + 1]] << 2;
-		if (context->debug_pal < 2) {
-			for (int i = 28; i >= 0; i -= 4)
-			{
-				*(dst++) = context->colors[CRAM_SIZE*3 | (context->debug_pal << 4) | (pixel >> i & 0xF)];
-			}
-		} else {
-			for (int i = 28; i >= 0; i -= 4)
-			{
-				uint8_t value = (pixel >> i & 0xF) * 17;
-				if (context->debug_pal == 3) {
-					value = 255 - value;
-				}
-				*(dst++) = render_map_color(value, value, value);
-			}
+			*(dst++) = context->colors[bgcolor];
+			*(debug_dst++) = DBG_SRC_BG;
 		}
 	}
 	context->done_output = dst;
@@ -1674,31 +1590,69 @@
 	}
 	last_line = context->cycles;
 #endif
-	context->vcounter++;
-	
+	uint16_t jump_start, jump_end;
 	uint8_t is_mode_5 = context->regs[REG_MODE_2] & BIT_MODE_5;
 	if (is_mode_5) {
 		if (context->flags2 & FLAG2_REGION_PAL) {
 			if (context->regs[REG_MODE_2] & BIT_PAL) {
-				if (context->vcounter == 0x10B) {
-					context->vcounter = 0x1D2;
-				}
-			} else if (context->vcounter == 0x103){
-				context->vcounter = 0x1CA;
+				jump_start = 0x10B;
+				jump_end = 0x1D2;
+			} else {
+				jump_start = 0x103;
+				jump_end = 0x1CA;
 			}
+		} else if (context->regs[REG_MODE_2] & BIT_PAL) {
+			jump_start = 0x100;
+			jump_end = 0x1FA;
 		} else {
-			if (context->regs[REG_MODE_2] & BIT_PAL) {
-				if (context->vcounter == 0x100) {
-					context->vcounter = 0x1FA;
+			jump_start = 0xEB;
+			jump_end = 0x1E5;
+		}
+	} else {
+		jump_start = 0xDB;
+		jump_end = 0x1D5;
+	}
+
+	if (context->enabled_debuggers & (1 << VDP_DEBUG_CRAM | 1 << VDP_DEBUG_COMPOSITE)) {
+		uint32_t line = context->vcounter;
+		if (line >= jump_end) {
+			line -= jump_end - jump_start;
+		}
+		uint32_t total_lines = (context->flags2 & FLAG2_REGION_PAL) ? 313 : 262;
+		
+		if (total_lines - line <= context->border_top) {
+			line -= total_lines - context->border_top;
+		} else {
+			line += context->border_top;
+		}
+		if (context->enabled_debuggers & (1 << VDP_DEBUG_CRAM)) {
+			uint32_t *fb = context->debug_fbs[VDP_DEBUG_CRAM] + context->debug_fb_pitch[VDP_DEBUG_CRAM] * line / sizeof(uint32_t);
+			for (int i = 0; i < 64; i++)
+			{
+				for (int x = 0; x < 8; x++)
+				{
+					*(fb++) = context->colors[i];
 				}
-			} else if (context->vcounter == 0xEB) {
-				context->vcounter = 0x1E5;
 			}
 		}
-	} else if (context->vcounter == 0xDB) {
-		context->vcounter = 0x1D5;
+		if (
+			context->enabled_debuggers & (1 << VDP_DEBUG_COMPOSITE)
+			&& line < (context->inactive_start + context->border_bot + context->border_top)
+		) {
+			uint32_t *fb = context->debug_fbs[VDP_DEBUG_COMPOSITE] + context->debug_fb_pitch[VDP_DEBUG_COMPOSITE] * line / sizeof(uint32_t);
+			for (int i = 0; i < LINEBUF_SIZE; i++)
+			{
+				*(fb++) = context->debugcolors[context->layer_debug_buf[i]];
+			}
+		}
 	}
-	context->vcounter &= 0x1FF;
+	
+	context->vcounter++;
+	if (context->vcounter == jump_start) {
+		context->vcounter = jump_end;
+	} else {
+		context->vcounter &= 0x1FF;
+	}
 	if (context->state == PREPARING) {
 		context->state = ACTIVE;
 	}
@@ -1717,6 +1671,190 @@
 	}
 }
 
+static void vdp_update_per_frame_debug(vdp_context *context)
+{
+	if (context->enabled_debuggers & (1 << VDP_DEBUG_PLANE)) {
+		uint32_t pitch;
+		uint32_t *fb = render_get_framebuffer(context->debug_fb_indices[VDP_DEBUG_PLANE], &pitch);
+		uint16_t hscroll_mask;
+		uint16_t v_mul;
+		uint16_t vscroll_mask = 0x1F | (context->regs[REG_SCROLL] & 0x30) << 1;
+		switch(context->regs[REG_SCROLL] & 0x3)
+		{
+		case 0:
+			hscroll_mask = 0x1F;
+			v_mul = 64;
+			break;
+		case 0x1:
+			hscroll_mask = 0x3F;
+			v_mul = 128;
+			break;
+		case 0x2:
+			//TODO: Verify this behavior
+			hscroll_mask = 0x1F;
+			v_mul = 0;
+			break;
+		case 0x3:
+			hscroll_mask = 0x7F;
+			v_mul = 256;
+			break;
+		}
+		uint16_t table_address;
+		switch(context->debug_modes[VDP_DEBUG_PLANE] % 3)
+		{
+		case 0:
+			table_address = context->regs[REG_SCROLL_A] << 10 & 0xE000;
+			break;
+		case 1:
+			table_address = context->regs[REG_SCROLL_B] << 13 & 0xE000;
+			break;
+		case 2:
+			table_address = context->regs[REG_WINDOW] << 10;
+			if (context->regs[REG_MODE_4] & BIT_H40) {
+				table_address &= 0xF000;
+				v_mul = 128;
+				hscroll_mask = 0x3F;
+			} else {
+				table_address &= 0xF800;
+				v_mul = 64;
+				hscroll_mask = 0x1F;
+			}
+			vscroll_mask = 0x1F;
+			break;
+		}
+		uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR & 0x3F]];
+		for (uint16_t row = 0; row < 128; row++)
+		{
+			uint16_t row_address = table_address + (row & vscroll_mask) * v_mul;
+			for (uint16_t col = 0; col < 128; col++)
+			{
+				uint16_t address = row_address + (col & hscroll_mask) * 2;
+				//pccv hnnn nnnn nnnn
+				//
+				uint16_t entry = context->vdpmem[address] << 8 | context->vdpmem[address + 1];
+				uint8_t pal = entry >> 9 & 0x30;
+				
+				uint32_t *dst = fb + (row * pitch * 8 / sizeof(uint32_t)) + col * 8;
+				address = (entry & 0x7FF) * 32;
+				int y_diff = 4;
+				if (entry & 0x1000) {
+					y_diff = -4;
+					address += 7 * 4;
+				}
+				int x_diff = 1;
+				if (entry & 0x800) {
+					x_diff = -1;
+					address += 3;
+				}
+				for (int y = 0; y < 8; y++)
+				{
+					uint16_t trow_address = address;
+					uint32_t *row_dst = dst;
+					for (int x = 0; x < 4; x++)
+					{
+						uint8_t byte = context->vdpmem[trow_address];
+						trow_address += x_diff;
+						uint8_t left, right;
+						if (x_diff > 0) {
+							left = byte >> 4;
+							right = byte & 0xF;
+						} else {
+							left = byte & 0xF;
+							right = byte >> 4;
+						}
+						*(row_dst++) = left ? context->colors[left|pal] : bg_color;
+						*(row_dst++) = right ? context->colors[right|pal] : bg_color;
+					}
+					address += y_diff;
+					dst += pitch / sizeof(uint32_t);
+				}
+			}
+		}
+		render_framebuffer_updated(context->debug_fb_indices[VDP_DEBUG_PLANE], 1024);
+	}
+	
+	if (context->enabled_debuggers & (1 << VDP_DEBUG_VRAM)) {
+		uint32_t pitch;
+		uint32_t *fb = render_get_framebuffer(context->debug_fb_indices[VDP_DEBUG_VRAM], &pitch);
+		
+		uint8_t pal = (context->debug_modes[VDP_DEBUG_VRAM] % 4) << 4;
+		for (int y = 0; y < 512; y++)
+		{
+			uint32_t *line = fb + y * pitch / sizeof(uint32_t);
+			int row = y >> 4;
+			int yoff = y >> 1 & 7;
+			for (int col = 0; col < 64; col++)
+			{
+				uint16_t address = (row * 64 + col) * 32 + yoff * 4;
+				for (int x = 0; x < 4; x++)
+				{
+					uint8_t byte = context->vdpmem[address++];
+					uint8_t left = byte >> 4 | pal;
+					uint8_t right = byte & 0xF | pal;
+					*(line++) = context->colors[left];
+					*(line++) = context->colors[left];
+					*(line++) = context->colors[right];
+					*(line++) = context->colors[right];
+				}
+			}
+		}
+		
+		render_framebuffer_updated(context->debug_fb_indices[VDP_DEBUG_VRAM], 1024);
+	}
+	
+	if (context->enabled_debuggers & (1 << VDP_DEBUG_CRAM)) {
+		uint32_t starting_line = 512 - 32*4;
+		uint32_t *line = context->debug_fbs[VDP_DEBUG_CRAM] 
+			+ context->debug_fb_pitch[VDP_DEBUG_CRAM]  * starting_line / sizeof(uint32_t);
+		for (int pal = 0; pal < 4; pal ++)
+		{
+			uint32_t *cur;
+			for (int y = 0; y < 31; y++)
+			{
+				cur = line;
+				for (int offset = 0; offset < 16; offset++)
+				{
+					for (int x = 0; x < 31; x++)
+					{
+						*(cur++) = context->colors[pal * 16 + offset];
+					}
+					*(cur++) = 0xFF000000;
+				}
+				line += context->debug_fb_pitch[VDP_DEBUG_CRAM] / sizeof(uint32_t);
+			}
+			cur = line;
+			for (int x = 0; x < 512; x++)
+			{
+				*(cur++) = 0xFF000000;
+			}
+			line += context->debug_fb_pitch[VDP_DEBUG_CRAM] / sizeof(uint32_t);
+		}
+		render_framebuffer_updated(context->debug_fb_indices[VDP_DEBUG_CRAM], 512);
+		context->debug_fbs[VDP_DEBUG_CRAM] = render_get_framebuffer(context->debug_fb_indices[VDP_DEBUG_CRAM], &context->debug_fb_pitch[VDP_DEBUG_CRAM]);
+	}
+	if (context->enabled_debuggers & (1 << VDP_DEBUG_COMPOSITE)) {
+		render_framebuffer_updated(context->debug_fb_indices[VDP_DEBUG_COMPOSITE], LINEBUF_SIZE);
+		context->debug_fbs[VDP_DEBUG_COMPOSITE] = render_get_framebuffer(context->debug_fb_indices[VDP_DEBUG_COMPOSITE], &context->debug_fb_pitch[VDP_DEBUG_COMPOSITE]);
+	}		
+}
+
+void vdp_force_update_framebuffer(vdp_context *context)
+{
+	uint16_t lines_max = (context->flags2 & FLAG2_REGION_PAL) 
+			? 240 + BORDER_TOP_V30_PAL + BORDER_BOT_V30_PAL 
+			: 224 + BORDER_TOP_V28 + BORDER_BOT_V28;
+			
+	uint16_t to_fill = lines_max - context->output_lines;
+	memset(
+		((char *)context->fb) + context->output_pitch * context->output_lines,
+		0,
+		to_fill * context->output_pitch
+	);
+	render_framebuffer_updated(context->cur_buffer, context->h40_lines > context->output_lines / 2 ? LINEBUF_SIZE : (256+HORIZ_BORDER));
+	context->fb = render_get_framebuffer(context->cur_buffer, &context->output_pitch);
+	vdp_update_per_frame_debug(context);
+}
+
 static void advance_output_line(vdp_context *context)
 {
 	if (headless) {
@@ -1733,6 +1871,7 @@
 			render_framebuffer_updated(context->cur_buffer, context->h40_lines > (context->inactive_start + context->border_top) / 2 ? LINEBUF_SIZE : (256+HORIZ_BORDER));
 			context->cur_buffer = context->flags2 & FLAG2_EVEN_FIELD ? FRAMEBUFFER_EVEN : FRAMEBUFFER_ODD;
 			context->fb = render_get_framebuffer(context->cur_buffer, &context->output_pitch);
+			vdp_update_per_frame_debug(context);
 			context->h40_lines = 0;
 			context->frame++;
 			context->output_lines = 0;
@@ -2763,18 +2902,26 @@
 			active_line = 0x200;
 		}
 	}
-	uint32_t *dst = (
-		context->vcounter < context->inactive_start + context->border_bot 
-		|| context->vcounter >= 0x200 - context->border_top
-	) && context->hslot >= BG_START_SLOT && context->hslot < bg_end_slot
-		? context->output + 2 * (context->hslot - BG_START_SLOT)
-		: NULL;
+	uint32_t *dst;
+	uint8_t *debug_dst;
+	if (
+		(
+			context->vcounter < context->inactive_start + context->border_bot 
+			|| context->vcounter >= 0x200 - context->border_top
+		) && context->hslot >= BG_START_SLOT && context->hslot < bg_end_slot
+	) {
+		dst = context->output + 2 * (context->hslot - BG_START_SLOT);
+		debug_dst = context->layer_debug_buf + 2 * (context->hslot - BG_START_SLOT);
+	} else {
+		dst = NULL;
+	}
 		
 	if (
 		!dst && context->vcounter == context->inactive_start + context->border_bot
 		&& context->hslot >= line_change  && context->hslot < bg_end_slot
 	) {
 		dst = context->output + 2 * (context->hslot - BG_START_SLOT);
+		debug_dst = context->layer_debug_buf + 2 * (context->hslot - BG_START_SLOT);
 	}
 		
 	uint8_t test_layer = context->test_port >> 7 & 3;
@@ -2790,6 +2937,7 @@
 			|| context->vcounter >= 0x200 - context->border_top
 		)) {
 			dst = context->output + (context->hslot - BG_START_SLOT) * 2;
+			debug_dst = context->layer_debug_buf + 2 * (context->hslot - BG_START_SLOT);
 		} else if (context->hslot == bg_end_slot) {
 			advance_output_line(context);
 			dst = NULL;
@@ -2850,17 +2998,22 @@
 			}
 			if (dst >= context->done_output) {
 				*(dst++) = bg_color;
+				*(debug_dst++) = DBG_SRC_BG;
 			} else {
 				dst++;
+				debug_dst++;
 			}
 			if (dst >= context->done_output) {
 				*(dst++) = bg_color;
+				*(debug_dst++) = DBG_SRC_BG;
 				context->done_output = dst;
 			} else {
 				dst++;
+				debug_dst++;
 			}
 			if (context->hslot == (bg_end_slot-1)) {
 				*(dst++) = bg_color;
+				*(debug_dst++) = DBG_SRC_BG;
 				context->done_output = dst;
 			}
 		}
@@ -2949,7 +3102,7 @@
 		}
 		uint32_t min_dma_complete = dmalen * (context->regs[REG_MODE_4] & BIT_H40 ? 16 : 20);
 		if (
-			(context->regs[REG_DMASRC_H] & 0xC0) == 0xC0 
+			(context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_COPY 
 			|| (((context->cd & 0xF) == VRAM_WRITE) && !(context->regs[REG_MODE_2] & BIT_128K_VRAM))) {
 			//DMA copies take twice as long to complete since they require a read and a write
 			//DMA Fills and transfers to VRAM also take twice as long as it requires 2 writes for a single word
@@ -3025,7 +3178,7 @@
 		//printf("New Address: %X, New CD: %X\n", context->address, context->cd);
 		if (context->cd & 0x20) {
 			//
-			if((context->regs[REG_DMASRC_H] & 0xC0) != 0x80) {
+			if((context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) != DMA_FILL) {
 				//DMA copy or 68K -> VDP, transfer starts immediately
 				//printf("DMA start (length: %X) at cycle %d, frame: %d, vcounter: %d, hslot: %d\n", (context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L], context->cycles, context->frame, context->vcounter, context->hslot);
 				if (!(context->regs[REG_DMASRC_H] & 0x80)) {
@@ -3106,7 +3259,7 @@
 int vdp_data_port_write(vdp_context * context, uint16_t value)
 {
 	//printf("data port write: %X at %d\n", value, context->cycles);
-	if (context->flags & FLAG_DMA_RUN && (context->regs[REG_DMASRC_H] & 0xC0) != 0x80) {
+	if (context->flags & FLAG_DMA_RUN && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) != DMA_FILL) {
 		return -1;
 	}
 	if (context->flags & FLAG_PENDING) {
@@ -3118,7 +3271,7 @@
 	/*if (context->fifo_cur == context->fifo_end) {
 		printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles);
 	}*/
-	if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) {
+	if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_FILL) {
 		context->flags &= ~FLAG_DMA_RUN;
 	}
 	while (context->fifo_write == context->fifo_read) {
@@ -3154,7 +3307,7 @@
 	/*if (context->fifo_cur == context->fifo_end) {
 		printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles);
 	}*/
-	if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) {
+	if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_FILL) {
 		context->flags &= ~FLAG_DMA_RUN;
 	}
 	while (context->fifo_write == context->fifo_read) {
@@ -3721,3 +3874,63 @@
 	context->pending_hint_start = load_int32(buf);
 	update_video_params(context);
 }
+
+void vdp_toggle_debug_view(vdp_context *context, uint8_t debug_type)
+{
+	if (context->enabled_debuggers & 1 << debug_type) {
+		render_destroy_window(context->debug_fb_indices[debug_type]);
+		context->enabled_debuggers &= ~(1 << debug_type);
+	} else {
+		uint32_t width,height;
+		uint8_t fetch_immediately = 0;
+		char *caption;
+		switch(debug_type)
+		{
+		case VDP_DEBUG_PLANE:
+			caption = "BlastEm - VDP Plane Debugger";
+			width = height = 1024;
+			break;
+		case VDP_DEBUG_VRAM:
+			caption = "BlastEm - VDP VRAM Debugger";
+			width = 1024;
+			height = 512;
+			break;
+		case VDP_DEBUG_CRAM:
+			caption = "BlastEm - VDP CRAM Debugger";
+			width = 512;
+			height = 512;
+			fetch_immediately = 1;
+			break;
+		case VDP_DEBUG_COMPOSITE:
+			caption = "BlastEm - VDP Plane Composition Debugger";
+			width = LINEBUF_SIZE;
+			height = context->inactive_start + context->border_top + context->border_bot;
+			fetch_immediately = 1;
+			break;
+		default:
+			return;
+		}
+		context->debug_fb_indices[debug_type] = render_create_window(caption, width, height);
+		if (context->debug_fb_indices[debug_type]) {
+			context->enabled_debuggers |= 1 << debug_type;
+		}
+		if (fetch_immediately) {
+			context->debug_fbs[debug_type] = render_get_framebuffer(context->debug_fb_indices[debug_type], &context->debug_fb_pitch[debug_type]);
+		}
+	}
+}
+
+void vdp_inc_debug_mode(vdp_context *context)
+{
+	uint8_t active = render_get_active_framebuffer();
+	if (active < FRAMEBUFFER_USER_START) {
+		return;
+	}
+	for (int i = 0; i < VDP_NUM_DEBUG_TYPES; i++)
+	{
+		if (context->enabled_debuggers & (1 << i) && context->debug_fb_indices[i] == active) {
+			context->debug_modes[i]++;
+			return;
+		}
+	}
+}
--- a/vdp.h	Sun Dec 31 10:11:16 2017 -0800
+++ b/vdp.h	Tue Dec 25 11:12:26 2018 -0800
@@ -19,6 +19,7 @@
 #define BORDER_RIGHT 14
 #define HORIZ_BORDER (BORDER_LEFT+BORDER_RIGHT)
 #define LINEBUF_SIZE (320+HORIZ_BORDER) //H40 + full border
+#define SCROLL_BUFFER_SIZE 32
 #define BORDER_BOTTOM 13 //TODO: Replace with actual value
 #define MAX_DRAWS 40
 #define MAX_DRAWS_H32 32
@@ -147,74 +148,89 @@
 	uint8_t  partial;
 } fifo_entry;
 
+enum {
+	VDP_DEBUG_PLANE,
+	VDP_DEBUG_VRAM,
+	VDP_DEBUG_CRAM,
+	VDP_DEBUG_COMPOSITE,
+	VDP_NUM_DEBUG_TYPES
+};
+
 typedef struct {
-	fifo_entry  fifo[FIFO_SIZE];
-	int32_t     fifo_write;
-	int32_t     fifo_read;
-	uint32_t    address;
-	uint32_t    serial_address;
-	uint8_t     cd;
-	uint8_t	    flags;
-	uint8_t     regs[VDP_REGS];
-	//cycle count in MCLKs
-	uint32_t    cycles;
-	uint32_t    pending_vint_start;
-	uint32_t    pending_hint_start;
-	uint8_t     *vdpmem;
-	//stores 2-bit palette + 4-bit palette index + priority for current sprite line
-	uint8_t     *linebuf;
-	//pointer to current line in framebuffer
-	uint32_t    *output;
-	uint32_t    *done_output;
-	uint32_t    *fb;
 	system_header  *system;
-	uint16_t    cram[CRAM_SIZE];
-	uint32_t    colors[CRAM_SIZE*4];
-	uint32_t    debugcolors[1 << (3 + 1 + 1 + 1)];//3 bits for source, 1 bit for priority, 1 bit for shadow, 1 bit for hilight
-	uint16_t    vsram[VSRAM_SIZE];
-	uint16_t    vscroll_latch[2];
-	uint32_t    output_pitch;
-	uint32_t    frame;
-	uint16_t    vcounter;
-	uint16_t    inactive_start;
-	uint16_t    border_top;
-	uint16_t    border_bot;
-	uint16_t    hscroll_a;
-	uint16_t    hscroll_b;
-	uint16_t    h40_lines;
-	uint16_t    output_lines;
-	sprite_draw sprite_draw_list[MAX_DRAWS];
-	sprite_info sprite_info_list[MAX_SPRITES_LINE];
-	uint8_t     sat_cache[SAT_CACHE_SIZE];
-	uint16_t    col_1;
-	uint16_t    col_2;
-	uint16_t    hv_latch;
-	uint16_t    prefetch;
-	uint16_t    test_port;
-	uint8_t     hslot; //hcounter/2
-	uint8_t	    sprite_index;
-	uint8_t     sprite_draws;
-	int8_t      slot_counter;
-	int8_t      cur_slot;
-	uint8_t     max_sprites_frame;
-	uint8_t     max_sprites_line;
-	uint8_t     fetch_tmp[2];
-	uint8_t     v_offset;
-	uint8_t     hint_counter;
-	uint8_t     flags2;
-	uint8_t     double_res;
-	uint8_t     buf_a_off;
-	uint8_t     buf_b_off;
-	uint8_t     debug;
-	uint8_t     debug_pal;
-	uint8_t     pending_byte;
-	uint8_t     state;
-	uint8_t     cur_buffer;
-	uint8_t     *tmp_buf_a;
-	uint8_t     *tmp_buf_b;
+	//pointer to current line in framebuffer
+	uint32_t       *output;
+	uint32_t       *done_output;
+	//pointer to current framebuffer
+	uint32_t       *fb;
+	uint32_t       *debug_fbs[VDP_NUM_DEBUG_TYPES];
+	uint32_t       output_pitch;
+	uint32_t       debug_fb_pitch[VDP_NUM_DEBUG_TYPES];
+	fifo_entry     fifo[FIFO_SIZE];
+	int32_t        fifo_write;
+	int32_t        fifo_read;
+	uint32_t       address;
+	uint32_t       serial_address;
+	uint32_t       colors[CRAM_SIZE*4];
+	uint32_t       debugcolors[1 << (3 + 1 + 1 + 1)];//3 bits for source, 1 bit for priority, 1 bit for shadow, 1 bit for hilight
+	uint16_t       cram[CRAM_SIZE];
+	uint32_t       frame;
+	uint8_t        cd;
+	uint8_t	       flags;
+	uint8_t        regs[VDP_REGS];
+	//cycle count in MCLKs
+	uint32_t       cycles;
+	uint32_t       pending_vint_start;
+	uint32_t       pending_hint_start;
+	uint16_t       vsram[VSRAM_SIZE];
+	uint16_t       vscroll_latch[2];
+	uint16_t       vcounter;
+	uint16_t       inactive_start;
+	uint16_t       border_top;
+	uint16_t       border_bot;
+	uint16_t       hscroll_a;
+	uint16_t       hscroll_b;
+	uint16_t       h40_lines;
+	uint16_t       output_lines;
+	sprite_draw    sprite_draw_list[MAX_DRAWS];
+	sprite_info    sprite_info_list[MAX_SPRITES_LINE];
+	uint8_t        sat_cache[SAT_CACHE_SIZE];
+	uint16_t       col_1;
+	uint16_t       col_2;
+	uint16_t       hv_latch;
+	uint16_t       prefetch;
+	uint16_t       test_port;
+	//stores 2-bit palette + 4-bit palette index + priority for current sprite line
+	uint8_t        linebuf[LINEBUF_SIZE];
+	uint8_t        layer_debug_buf[LINEBUF_SIZE];
+	uint8_t        hslot; //hcounter/2
+	uint8_t	       sprite_index;
+	uint8_t        sprite_draws;
+	int8_t         slot_counter;
+	int8_t         cur_slot;
+	uint8_t        max_sprites_frame;
+	uint8_t        max_sprites_line;
+	uint8_t        fetch_tmp[2];
+	uint8_t        v_offset;
+	uint8_t        hint_counter;
+	uint8_t        flags2;
+	uint8_t        double_res;
+	uint8_t        buf_a_off;
+	uint8_t        buf_b_off;
+	uint8_t        pending_byte;
+	uint8_t        state;
+	uint8_t        cur_buffer;
+	uint8_t        tmp_buf_a[SCROLL_BUFFER_SIZE];
+	uint8_t        tmp_buf_b[SCROLL_BUFFER_SIZE];
+	uint8_t        enabled_debuggers;
+	uint8_t        debug_fb_indices[VDP_NUM_DEBUG_TYPES];
+	uint8_t        debug_modes[VDP_NUM_DEBUG_TYPES];
+	uint8_t        vdpmem[];
 } vdp_context;
 
-void init_vdp_context(vdp_context * context, uint8_t region_pal);
+
+
+vdp_context *init_vdp_context(uint8_t region_pal);
 void vdp_free(vdp_context *context);
 void vdp_run_context_full(vdp_context * context, uint32_t target_cycles);
 void vdp_run_context(vdp_context * context, uint32_t target_cycles);
@@ -252,5 +268,8 @@
 void vdp_reacquire_framebuffer(vdp_context *context);
 void vdp_serialize(vdp_context *context, serialize_buffer *buf);
 void vdp_deserialize(deserialize_buffer *buf, void *vcontext);
+void vdp_force_update_framebuffer(vdp_context *context);
+void vdp_toggle_debug_view(vdp_context *context, uint8_t debug_type);
+void vdp_inc_debug_mode(vdp_context *context);
 
 #endif //VDP_H_
--- a/vgmplay.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/vgmplay.c	Tue Dec 25 11:12:26 2018 -0800
@@ -62,6 +62,10 @@
 {
 }
 
+void controller_add_mappings()
+{
+}
+
 int headless = 0;
 
 #define CYCLE_LIMIT MCLKS_NTSC/60
@@ -108,10 +112,10 @@
 	uint32_t lowpass_cutoff = lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : 3390;
 
 	ym2612_context y_context;
-	ym_init(&y_context, render_sample_rate(), MCLKS_NTSC, MCLKS_PER_YM, render_audio_buffer(), opts, lowpass_cutoff);
+	ym_init(&y_context, MCLKS_NTSC, MCLKS_PER_YM, opts);
 
 	psg_context p_context;
-	psg_init(&p_context, render_sample_rate(), MCLKS_NTSC, MCLKS_PER_PSG, render_audio_buffer(), lowpass_cutoff);
+	psg_init(&p_context, MCLKS_NTSC, MCLKS_PER_PSG);
 
 	FILE * f = fopen(argv[1], "rb");
 	vgm_header header;
--- a/ym2612.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/ym2612.c	Tue Dec 25 11:12:26 2018 -0800
@@ -116,12 +116,10 @@
 	}
 	log_context = NULL;
 }
-#define BUFFER_INC_RES 0x40000000UL
 
 void ym_adjust_master_clock(ym2612_context * context, uint32_t master_clock)
 {
-	uint64_t old_inc = context->buffer_inc;
-	context->buffer_inc = ((BUFFER_INC_RES * (uint64_t)context->sample_rate) / (uint64_t)master_clock) * (uint64_t)context->clock_inc * NUM_OPERATORS;
+	render_audio_adjust_clock(context->audio, master_clock, context->clock_inc * NUM_OPERATORS);
 }
 
 #ifdef __ANDROID__
@@ -163,23 +161,13 @@
 	}
 }
 
-void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t sample_limit, uint32_t options, uint32_t lowpass_cutoff)
+void ym_init(ym2612_context * context, uint32_t master_clock, uint32_t clock_div, uint32_t options)
 {
 	static uint8_t registered_finalize;
 	dfopen(debug_file, "ym_debug.txt", "w");
 	memset(context, 0, sizeof(*context));
-	context->audio_buffer = malloc(sizeof(*context->audio_buffer) * sample_limit*2);
-	context->back_buffer = malloc(sizeof(*context->audio_buffer) * sample_limit*2);
-	context->sample_rate = sample_rate;
 	context->clock_inc = clock_div * 6;
-	ym_adjust_master_clock(context, master_clock);
-	
-	double rc = (1.0 / (double)lowpass_cutoff) / (2.0 * M_PI);
-	double dt = 1.0 / ((double)master_clock / (double)(context->clock_inc * NUM_OPERATORS));
-	double alpha = dt / (dt + rc);
-	context->lowpass_alpha = (int32_t)(((double)0x10000) * alpha);
-
-	context->sample_limit = sample_limit*2;
+	context->audio = render_audio_source(master_clock, context->clock_inc * NUM_OPERATORS, 2);
 	
 	//some games seem to expect that the LR flags start out as 1
 	for (int i = 0; i < NUM_CHANNELS; i++) {
@@ -191,7 +179,7 @@
 				fprintf(stderr, "Failed to open WAVE log file %s for writing\n", fname);
 				continue;
 			}
-			if (!wave_init(f, sample_rate, 16, 1)) {
+			if (!wave_init(f, master_clock / (context->clock_inc * NUM_OPERATORS), 16, 1)) {
 				fclose(f);
 				context->channels[i].logfile = NULL;
 			}
@@ -266,13 +254,10 @@
 
 void ym_free(ym2612_context *context)
 {
+	render_free_source(context->audio);
 	if (context == log_context) {
 		ym_finalize_log();
 	}
-	free(context->audio_buffer);
-	//TODO: Figure out how to make this 100% safe
-	//audio thread could still be using this
-	free(context->back_buffer);
 	free(context);
 }
 
@@ -607,7 +592,6 @@
 		if (context->current_op == NUM_OPERATORS) {
 			context->current_op = 0;
 			
-			context->buffer_fraction += context->buffer_inc;
 			int16_t left = 0, right = 0;
 			for (int i = 0; i < NUM_CHANNELS; i++) {
 				int16_t value = context->channels[i].output;
@@ -621,7 +605,7 @@
 						value |= 0xC000;
 					}
 				}
-				if (context->channels[i].logfile && context->buffer_fraction > BUFFER_INC_RES) {
+				if (context->channels[i].logfile) {
 					fwrite(&value, sizeof(value), 1, context->channels[i].logfile);
 				}
 				if (context->channels[i].lr & 0x80) {
@@ -631,30 +615,7 @@
 					right += (value * YM_VOLUME_MULTIPLIER) / YM_VOLUME_DIVIDER;
 				}
 			}
-			int32_t tmp = left * context->lowpass_alpha + context->last_left * (0x10000 - context->lowpass_alpha);
-			left = tmp >> 16;
-			tmp = right * context->lowpass_alpha + context->last_right * (0x10000 - context->lowpass_alpha);
-			right = tmp >> 16;
-			while (context->buffer_fraction > BUFFER_INC_RES) {
-				context->buffer_fraction -= BUFFER_INC_RES;
-
-				int64_t tmp = context->last_left * ((context->buffer_fraction << 16) / context->buffer_inc);
-				tmp += left * (0x10000 - ((context->buffer_fraction << 16) / context->buffer_inc));
-				context->audio_buffer[context->buffer_pos] = tmp >> 16;
-				
-				tmp = context->last_right * ((context->buffer_fraction << 16) / context->buffer_inc);
-				tmp += right * (0x10000 - ((context->buffer_fraction << 16) / context->buffer_inc));
-				context->audio_buffer[context->buffer_pos+1] = tmp >> 16;
-				
-				context->buffer_pos += 2;
-				if (context->buffer_pos == context->sample_limit) {
-					if (!headless) {
-						render_wait_ym(context);
-					}
-				}
-			}
-			context->last_left = left;
-			context->last_right = right;
+			render_put_stereo_sample(context->audio, left, right);
 		}
 		
 	}
--- a/ym2612.h	Sun Dec 31 10:11:16 2017 -0800
+++ b/ym2612.h	Tue Dec 25 11:12:26 2018 -0800
@@ -9,6 +9,7 @@
 #include <stdint.h>
 #include <stdio.h>
 #include "serialize.h"
+#include "render.h"
 
 #define NUM_PART_REGS (0xB7-0x30)
 #define NUM_CHANNELS 6
@@ -62,14 +63,8 @@
 #define YM_PART2_REGS (YM_REG_END-YM_PART2_START)
 
 typedef struct {
-    int16_t     *audio_buffer;
-    int16_t     *back_buffer;
-    uint64_t    buffer_fraction;
-    uint64_t    buffer_inc;
+	audio_source *audio;
     uint32_t    clock_inc;
-    uint32_t    buffer_pos;
-	uint32_t    sample_rate;
-    uint32_t    sample_limit;
 	uint32_t    current_cycle;
 	//TODO: Condense the next two fields into one
 	uint32_t    write_cycle;
@@ -81,8 +76,6 @@
 	uint16_t    timer_a_load;
 	uint16_t    env_counter;
 	ym_supp     ch3_supp[3];
-	int16_t     last_left;
-	int16_t     last_right;
 	uint8_t     timer_b;
 	uint8_t     sub_timer_b;
 	uint8_t     timer_b_load;
@@ -131,7 +124,7 @@
 	REG_LR_AMS_PMS   = 0xB4
 };
 
-void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t sample_limit, uint32_t options, uint32_t lowpass_cutoff);
+void ym_init(ym2612_context * context, uint32_t master_clock, uint32_t clock_div, uint32_t options);
 void ym_reset(ym2612_context *context);
 void ym_free(ym2612_context *context);
 void ym_adjust_master_clock(ym2612_context * context, uint32_t master_clock);
--- a/z80_to_x86.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/z80_to_x86.c	Tue Dec 25 11:12:26 2018 -0800
@@ -2075,7 +2075,7 @@
 		break;
 	}
 	case Z80_JPCC: {
-		cycles(&opts->gen, num_cycles + 3);//T States: 4,3
+		cycles(&opts->gen, num_cycles + 6);//T States: 4,3,3
 		uint8_t cond = CC_Z;
 		switch (inst->reg)
 		{
@@ -2102,7 +2102,6 @@
 		}
 		uint8_t *no_jump_off = code->cur+1;
 		jcc(code, cond, code->cur+2);
-		cycles(&opts->gen, 5);//T States: 5
 		uint16_t dest_addr = inst->immed;
 		code_ptr call_dst = z80_get_native_address(context, dest_addr);
 			if (!call_dst) {
@@ -3672,7 +3671,19 @@
 
 void z80_options_free(z80_options *opts)
 {
+	for (uint32_t address = 0; address < opts->gen.address_mask; address += NATIVE_CHUNK_SIZE)
+	{
+		uint32_t chunk = address / NATIVE_CHUNK_SIZE;
+		if (opts->gen.native_code_map[chunk].base) {
+			free(opts->gen.native_code_map[chunk].offsets);
+		}
+	}
 	free(opts->gen.native_code_map);
+	uint32_t ram_inst_slots = ram_size(&opts->gen) / 1024;
+	for (uint32_t i = 0; i < ram_inst_slots; i++)
+	{
+		free(opts->gen.ram_inst_sizes[i]);
+	}
 	free(opts->gen.ram_inst_sizes);
 	free(opts);
 }
--- a/zdis.c	Sun Dec 31 10:11:16 2017 -0800
+++ b/zdis.c	Tue Dec 25 11:12:26 2018 -0800
@@ -112,6 +112,7 @@
 						}
 					}
 				}
+				fclose(address_log);
 				break;
 			case 's':
 				opt++;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zip.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,200 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include "util.h"
+#include "zip.h"
+#ifndef DISABLE_ZLIB
+#include "zlib/zlib.h"
+#endif
+
+static const char cdfd_magic[4] = {'P', 'K', 1, 2};
+static const char eocd_magic[4] = {'P', 'K', 5, 6};
+#define MIN_EOCD_SIZE 22
+#define MIN_CDFD_SIZE 46
+#define ZIP_MAX_EOCD_OFFSET (64*1024+MIN_EOCD_SIZE)
+
+enum {
+	ZIP_STORE = 0,
+	ZIP_DEFLATE = 8
+};
+
+zip_file *zip_open(const char *filename)
+{
+	FILE *f = fopen(filename, "rb");
+	if (!f) {
+		return NULL;
+	}
+	long fsize = file_size(f);
+	if (fsize < MIN_EOCD_SIZE) {
+		//too small to be a zip file
+		goto fail;
+	}
+	
+	long max_offset = fsize > ZIP_MAX_EOCD_OFFSET ? ZIP_MAX_EOCD_OFFSET : fsize;
+	fseek(f, -max_offset, SEEK_END);
+	uint8_t *buf = malloc(max_offset);
+	if (max_offset != fread(buf, 1, max_offset, f)) {
+		goto fail;
+	}
+	
+	long current_offset;
+	uint32_t cd_start, cd_size;
+	uint16_t cd_count;
+	for (current_offset = max_offset - MIN_EOCD_SIZE; current_offset >= 0; current_offset--)
+	{
+		if (memcmp(eocd_magic, buf + current_offset, sizeof(eocd_magic))) {
+			continue;
+		}
+		uint16_t comment_size = buf[current_offset + 20] | buf[current_offset + 21] << 8;
+		if (comment_size != (max_offset - current_offset - MIN_EOCD_SIZE)) {
+			continue;
+		}
+		cd_start = buf[current_offset + 16] | buf[current_offset + 17] << 8
+			| buf[current_offset + 18] << 16 | buf[current_offset + 19] << 24;
+		if (cd_start > (fsize - (max_offset - current_offset))) {
+			continue;
+		}
+		cd_size = buf[current_offset + 12] | buf[current_offset + 13] << 8
+			| buf[current_offset + 14] << 16 | buf[current_offset + 15] << 24;
+		if ((cd_start + cd_size) > (fsize - (max_offset - current_offset))) {
+			continue;
+		}
+		cd_count = buf[current_offset + 10] | buf[current_offset + 11] << 8;
+		break;
+	}
+	free(buf);
+	if (current_offset < 0) {
+		//failed to find EOCD
+		goto fail;
+	}
+	buf = malloc(cd_size);
+	fseek(f, cd_start, SEEK_SET);
+	if (cd_size != fread(buf, 1, cd_size, f)) {
+		goto fail_free;
+	}
+	zip_entry *entries = calloc(cd_count, sizeof(zip_entry));
+	uint32_t cd_max_last = cd_size - MIN_CDFD_SIZE;
+	zip_entry *cur_entry = entries;
+	for (uint32_t off = 0; cd_count && off <= cd_max_last; cur_entry++, cd_count--)
+	{
+		if (memcmp(buf + off, cdfd_magic, sizeof(cdfd_magic))) {
+			goto fail_entries;
+		}
+		uint32_t name_length = buf[off + 28] | buf[off + 29] << 8;
+		uint32_t extra_length = buf[off + 30] | buf[off + 31] << 8;
+		//TODO: verify name length doesn't go past end of CD
+		
+		cur_entry->name = malloc(name_length + 1);
+		memcpy(cur_entry->name, buf + off + MIN_CDFD_SIZE, name_length);
+		cur_entry->name[name_length] = 0;
+		
+		cur_entry->compressed_size = buf[off + 20] | buf[off + 21] << 8 
+			| buf[off + 22] << 16 | buf[off + 23] << 24;
+		cur_entry->size = buf[off + 24] | buf[off + 25] << 8 
+			| buf[off + 26] << 16 | buf[off + 27] << 24;
+			
+		cur_entry->local_header_off = buf[off + 42] | buf[off + 43] << 8 
+			| buf[off + 44] << 16 | buf[off + 45] << 24;
+			
+		cur_entry->compression_method = buf[off + 10] | buf[off + 11] << 8;
+		
+		off += name_length + extra_length + MIN_CDFD_SIZE;
+	}
+	
+	zip_file *z = malloc(sizeof(zip_file));
+	z->entries = entries;
+	z->file = f;
+	z->num_entries = cur_entry - entries;
+	return z;
+	
+fail_entries:
+	for (cur_entry--; cur_entry >= entries; cur_entry--)
+	{
+		free(cur_entry->name);
+	}
+	free(entries);
+fail_free:
+	free(buf);
+fail:
+	fclose(f);
+	return NULL;
+}
+
+uint8_t *zip_read(zip_file *f, uint32_t index, size_t *out_size)
+{
+	
+	fseek(f->file, f->entries[index].local_header_off + 26, SEEK_SET);
+	uint8_t tmp[4];
+	if (sizeof(tmp) != fread(tmp, 1, sizeof(tmp), f->file)) {
+		return NULL;
+	}
+	uint32_t local_variable = (tmp[0] | tmp[1] << 8) + (tmp[2] | tmp[3] << 8);
+	fseek(f->file, f->entries[index].local_header_off + local_variable + 30, SEEK_SET);
+	
+	size_t int_size;
+	if (!out_size) {
+		out_size = &int_size;
+		int_size = f->entries[index].size;
+	}
+	
+	uint8_t *buf = malloc(*out_size);
+	if (*out_size > f->entries[index].size) {
+		*out_size = f->entries[index].size;
+	}
+	switch(f->entries[index].compression_method)
+	{
+	case ZIP_STORE:
+		if (*out_size != fread(buf, 1, *out_size, f->file)) {
+			free(buf);
+			return NULL;
+		}
+		break;
+#ifndef DISABLE_ZLIB
+	case ZIP_DEFLATE: {
+		//note in unzip.c in zlib/contrib suggests a dummy byte is needed, so we allocate an extra byte here
+		uint8_t *src_buf = malloc(f->entries[index].compressed_size + 1);
+		if (f->entries[index].compressed_size != fread(src_buf, 1, f->entries[index].compressed_size, f->file)) {
+			free(src_buf);
+			return NULL;
+		}
+		uLongf destLen = *out_size;
+		z_stream stream;
+		memset(&stream, 0, sizeof(stream));
+		stream.avail_in = f->entries[index].compressed_size + 1;
+		stream.next_in = src_buf;
+		stream.next_out = buf;
+		stream.avail_out = *out_size;
+		if (Z_OK == inflateInit2(&stream, -15)) {
+			int result = inflate(&stream, Z_FINISH);
+			*out_size = stream.total_out;
+			free(src_buf);
+			inflateEnd(&stream);
+			if (result != Z_OK && result != Z_STREAM_END && result != Z_BUF_ERROR) {
+				free(buf);
+				return NULL;
+			}
+		}
+		break;
+	}
+#endif
+	default:
+		free(buf);
+		return NULL;
+	}
+	
+	return buf;
+}
+
+void zip_close(zip_file *f)
+{
+	fclose(f->file);
+	for (uint32_t i = 0; i < f->num_entries; i++)
+	{
+		free(f->entries[i].name);
+	}
+	free(f->entries);
+	free(f);
+}
+
+ 
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zip.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,25 @@
+#ifndef ZIP_H_
+#define ZIP_H_
+
+#include <stdint.h>
+#include <stdio.h>
+
+typedef struct {
+	uint64_t compressed_size;
+	uint64_t size;
+	uint64_t local_header_off;
+	char     *name;
+	uint16_t compression_method;
+} zip_entry;
+
+typedef struct {
+	zip_entry *entries;
+	FILE      *file;
+	uint32_t  num_entries;
+} zip_file;
+
+zip_file *zip_open(const char *filename);
+uint8_t *zip_read(zip_file *f, uint32_t index, size_t *out_size);
+void zip_close(zip_file *f);
+
+#endif //ZIP_H_
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/adler32.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,186 @@
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-2011, 2016 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#include "zutil.h"
+
+local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2));
+
+#define BASE 65521U     /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf,i)  {adler += (buf)[i]; sum2 += adler;}
+#define DO2(buf,i)  DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i)  DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i)  DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf)   DO8(buf,0); DO8(buf,8);
+
+/* use NO_DIVIDE if your processor does not do division in hardware --
+   try it both ways to see which is faster */
+#ifdef NO_DIVIDE
+/* note that this assumes BASE is 65521, where 65536 % 65521 == 15
+   (thank you to John Reiser for pointing this out) */
+#  define CHOP(a) \
+    do { \
+        unsigned long tmp = a >> 16; \
+        a &= 0xffffUL; \
+        a += (tmp << 4) - tmp; \
+    } while (0)
+#  define MOD28(a) \
+    do { \
+        CHOP(a); \
+        if (a >= BASE) a -= BASE; \
+    } while (0)
+#  define MOD(a) \
+    do { \
+        CHOP(a); \
+        MOD28(a); \
+    } while (0)
+#  define MOD63(a) \
+    do { /* this assumes a is not negative */ \
+        z_off64_t tmp = a >> 32; \
+        a &= 0xffffffffL; \
+        a += (tmp << 8) - (tmp << 5) + tmp; \
+        tmp = a >> 16; \
+        a &= 0xffffL; \
+        a += (tmp << 4) - tmp; \
+        tmp = a >> 16; \
+        a &= 0xffffL; \
+        a += (tmp << 4) - tmp; \
+        if (a >= BASE) a -= BASE; \
+    } while (0)
+#else
+#  define MOD(a) a %= BASE
+#  define MOD28(a) a %= BASE
+#  define MOD63(a) a %= BASE
+#endif
+
+/* ========================================================================= */
+uLong ZEXPORT adler32_z(adler, buf, len)
+    uLong adler;
+    const Bytef *buf;
+    z_size_t len;
+{
+    unsigned long sum2;
+    unsigned n;
+
+    /* split Adler-32 into component sums */
+    sum2 = (adler >> 16) & 0xffff;
+    adler &= 0xffff;
+
+    /* in case user likes doing a byte at a time, keep it fast */
+    if (len == 1) {
+        adler += buf[0];
+        if (adler >= BASE)
+            adler -= BASE;
+        sum2 += adler;
+        if (sum2 >= BASE)
+            sum2 -= BASE;
+        return adler | (sum2 << 16);
+    }
+
+    /* initial Adler-32 value (deferred check for len == 1 speed) */
+    if (buf == Z_NULL)
+        return 1L;
+
+    /* in case short lengths are provided, keep it somewhat fast */
+    if (len < 16) {
+        while (len--) {
+            adler += *buf++;
+            sum2 += adler;
+        }
+        if (adler >= BASE)
+            adler -= BASE;
+        MOD28(sum2);            /* only added so many BASE's */
+        return adler | (sum2 << 16);
+    }
+
+    /* do length NMAX blocks -- requires just one modulo operation */
+    while (len >= NMAX) {
+        len -= NMAX;
+        n = NMAX / 16;          /* NMAX is divisible by 16 */
+        do {
+            DO16(buf);          /* 16 sums unrolled */
+            buf += 16;
+        } while (--n);
+        MOD(adler);
+        MOD(sum2);
+    }
+
+    /* do remaining bytes (less than NMAX, still just one modulo) */
+    if (len) {                  /* avoid modulos if none remaining */
+        while (len >= 16) {
+            len -= 16;
+            DO16(buf);
+            buf += 16;
+        }
+        while (len--) {
+            adler += *buf++;
+            sum2 += adler;
+        }
+        MOD(adler);
+        MOD(sum2);
+    }
+
+    /* return recombined sums */
+    return adler | (sum2 << 16);
+}
+
+/* ========================================================================= */
+uLong ZEXPORT adler32(adler, buf, len)
+    uLong adler;
+    const Bytef *buf;
+    uInt len;
+{
+    return adler32_z(adler, buf, len);
+}
+
+/* ========================================================================= */
+local uLong adler32_combine_(adler1, adler2, len2)
+    uLong adler1;
+    uLong adler2;
+    z_off64_t len2;
+{
+    unsigned long sum1;
+    unsigned long sum2;
+    unsigned rem;
+
+    /* for negative len, return invalid adler32 as a clue for debugging */
+    if (len2 < 0)
+        return 0xffffffffUL;
+
+    /* the derivation of this formula is left as an exercise for the reader */
+    MOD63(len2);                /* assumes len2 >= 0 */
+    rem = (unsigned)len2;
+    sum1 = adler1 & 0xffff;
+    sum2 = rem * sum1;
+    MOD(sum2);
+    sum1 += (adler2 & 0xffff) + BASE - 1;
+    sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem;
+    if (sum1 >= BASE) sum1 -= BASE;
+    if (sum1 >= BASE) sum1 -= BASE;
+    if (sum2 >= ((unsigned long)BASE << 1)) sum2 -= ((unsigned long)BASE << 1);
+    if (sum2 >= BASE) sum2 -= BASE;
+    return sum1 | (sum2 << 16);
+}
+
+/* ========================================================================= */
+uLong ZEXPORT adler32_combine(adler1, adler2, len2)
+    uLong adler1;
+    uLong adler2;
+    z_off_t len2;
+{
+    return adler32_combine_(adler1, adler2, len2);
+}
+
+uLong ZEXPORT adler32_combine64(adler1, adler2, len2)
+    uLong adler1;
+    uLong adler2;
+    z_off64_t len2;
+{
+    return adler32_combine_(adler1, adler2, len2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/compress.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,86 @@
+/* compress.c -- compress a memory buffer
+ * Copyright (C) 1995-2005, 2014, 2016 Jean-loup Gailly, Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+/* ===========================================================================
+     Compresses the source buffer into the destination buffer. The level
+   parameter has the same meaning as in deflateInit.  sourceLen is the byte
+   length of the source buffer. Upon entry, destLen is the total size of the
+   destination buffer, which must be at least 0.1% larger than sourceLen plus
+   12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+
+     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+   Z_STREAM_ERROR if the level parameter is invalid.
+*/
+int ZEXPORT compress2 (dest, destLen, source, sourceLen, level)
+    Bytef *dest;
+    uLongf *destLen;
+    const Bytef *source;
+    uLong sourceLen;
+    int level;
+{
+    z_stream stream;
+    int err;
+    const uInt max = (uInt)-1;
+    uLong left;
+
+    left = *destLen;
+    *destLen = 0;
+
+    stream.zalloc = (alloc_func)0;
+    stream.zfree = (free_func)0;
+    stream.opaque = (voidpf)0;
+
+    err = deflateInit(&stream, level);
+    if (err != Z_OK) return err;
+
+    stream.next_out = dest;
+    stream.avail_out = 0;
+    stream.next_in = (z_const Bytef *)source;
+    stream.avail_in = 0;
+
+    do {
+        if (stream.avail_out == 0) {
+            stream.avail_out = left > (uLong)max ? max : (uInt)left;
+            left -= stream.avail_out;
+        }
+        if (stream.avail_in == 0) {
+            stream.avail_in = sourceLen > (uLong)max ? max : (uInt)sourceLen;
+            sourceLen -= stream.avail_in;
+        }
+        err = deflate(&stream, sourceLen ? Z_NO_FLUSH : Z_FINISH);
+    } while (err == Z_OK);
+
+    *destLen = stream.total_out;
+    deflateEnd(&stream);
+    return err == Z_STREAM_END ? Z_OK : err;
+}
+
+/* ===========================================================================
+ */
+int ZEXPORT compress (dest, destLen, source, sourceLen)
+    Bytef *dest;
+    uLongf *destLen;
+    const Bytef *source;
+    uLong sourceLen;
+{
+    return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION);
+}
+
+/* ===========================================================================
+     If the default memLevel or windowBits for deflateInit() is changed, then
+   this function needs to be updated.
+ */
+uLong ZEXPORT compressBound (sourceLen)
+    uLong sourceLen;
+{
+    return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
+           (sourceLen >> 25) + 13;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/crc32.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,442 @@
+/* crc32.c -- compute the CRC-32 of a data stream
+ * Copyright (C) 1995-2006, 2010, 2011, 2012, 2016 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster
+ * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing
+ * tables for updating the shift register in one step with three exclusive-ors
+ * instead of four steps with four exclusive-ors.  This results in about a
+ * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3.
+ */
+
+/* @(#) $Id$ */
+
+/*
+  Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore
+  protection on the static variables used to control the first-use generation
+  of the crc tables.  Therefore, if you #define DYNAMIC_CRC_TABLE, you should
+  first call get_crc_table() to initialize the tables before allowing more than
+  one thread to use crc32().
+
+  DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h.
+ */
+
+#ifdef MAKECRCH
+#  include <stdio.h>
+#  ifndef DYNAMIC_CRC_TABLE
+#    define DYNAMIC_CRC_TABLE
+#  endif /* !DYNAMIC_CRC_TABLE */
+#endif /* MAKECRCH */
+
+#include "zutil.h"      /* for STDC and FAR definitions */
+
+/* Definitions for doing the crc four data bytes at a time. */
+#if !defined(NOBYFOUR) && defined(Z_U4)
+#  define BYFOUR
+#endif
+#ifdef BYFOUR
+   local unsigned long crc32_little OF((unsigned long,
+                        const unsigned char FAR *, z_size_t));
+   local unsigned long crc32_big OF((unsigned long,
+                        const unsigned char FAR *, z_size_t));
+#  define TBLS 8
+#else
+#  define TBLS 1
+#endif /* BYFOUR */
+
+/* Local functions for crc concatenation */
+local unsigned long gf2_matrix_times OF((unsigned long *mat,
+                                         unsigned long vec));
+local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat));
+local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2));
+
+
+#ifdef DYNAMIC_CRC_TABLE
+
+local volatile int crc_table_empty = 1;
+local z_crc_t FAR crc_table[TBLS][256];
+local void make_crc_table OF((void));
+#ifdef MAKECRCH
+   local void write_table OF((FILE *, const z_crc_t FAR *));
+#endif /* MAKECRCH */
+/*
+  Generate tables for a byte-wise 32-bit CRC calculation on the polynomial:
+  x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
+
+  Polynomials over GF(2) are represented in binary, one bit per coefficient,
+  with the lowest powers in the most significant bit.  Then adding polynomials
+  is just exclusive-or, and multiplying a polynomial by x is a right shift by
+  one.  If we call the above polynomial p, and represent a byte as the
+  polynomial q, also with the lowest power in the most significant bit (so the
+  byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
+  where a mod b means the remainder after dividing a by b.
+
+  This calculation is done using the shift-register method of multiplying and
+  taking the remainder.  The register is initialized to zero, and for each
+  incoming bit, x^32 is added mod p to the register if the bit is a one (where
+  x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
+  x (which is shifting right by one and adding x^32 mod p if the bit shifted
+  out is a one).  We start with the highest power (least significant bit) of
+  q and repeat for all eight bits of q.
+
+  The first table is simply the CRC of all possible eight bit values.  This is
+  all the information needed to generate CRCs on data a byte at a time for all
+  combinations of CRC register values and incoming bytes.  The remaining tables
+  allow for word-at-a-time CRC calculation for both big-endian and little-
+  endian machines, where a word is four bytes.
+*/
+local void make_crc_table()
+{
+    z_crc_t c;
+    int n, k;
+    z_crc_t poly;                       /* polynomial exclusive-or pattern */
+    /* terms of polynomial defining this crc (except x^32): */
+    static volatile int first = 1;      /* flag to limit concurrent making */
+    static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
+
+    /* See if another task is already doing this (not thread-safe, but better
+       than nothing -- significantly reduces duration of vulnerability in
+       case the advice about DYNAMIC_CRC_TABLE is ignored) */
+    if (first) {
+        first = 0;
+
+        /* make exclusive-or pattern from polynomial (0xedb88320UL) */
+        poly = 0;
+        for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++)
+            poly |= (z_crc_t)1 << (31 - p[n]);
+
+        /* generate a crc for every 8-bit value */
+        for (n = 0; n < 256; n++) {
+            c = (z_crc_t)n;
+            for (k = 0; k < 8; k++)
+                c = c & 1 ? poly ^ (c >> 1) : c >> 1;
+            crc_table[0][n] = c;
+        }
+
+#ifdef BYFOUR
+        /* generate crc for each value followed by one, two, and three zeros,
+           and then the byte reversal of those as well as the first table */
+        for (n = 0; n < 256; n++) {
+            c = crc_table[0][n];
+            crc_table[4][n] = ZSWAP32(c);
+            for (k = 1; k < 4; k++) {
+                c = crc_table[0][c & 0xff] ^ (c >> 8);
+                crc_table[k][n] = c;
+                crc_table[k + 4][n] = ZSWAP32(c);
+            }
+        }
+#endif /* BYFOUR */
+
+        crc_table_empty = 0;
+    }
+    else {      /* not first */
+        /* wait for the other guy to finish (not efficient, but rare) */
+        while (crc_table_empty)
+            ;
+    }
+
+#ifdef MAKECRCH
+    /* write out CRC tables to crc32.h */
+    {
+        FILE *out;
+
+        out = fopen("crc32.h", "w");
+        if (out == NULL) return;
+        fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n");
+        fprintf(out, " * Generated automatically by crc32.c\n */\n\n");
+        fprintf(out, "local const z_crc_t FAR ");
+        fprintf(out, "crc_table[TBLS][256] =\n{\n  {\n");
+        write_table(out, crc_table[0]);
+#  ifdef BYFOUR
+        fprintf(out, "#ifdef BYFOUR\n");
+        for (k = 1; k < 8; k++) {
+            fprintf(out, "  },\n  {\n");
+            write_table(out, crc_table[k]);
+        }
+        fprintf(out, "#endif\n");
+#  endif /* BYFOUR */
+        fprintf(out, "  }\n};\n");
+        fclose(out);
+    }
+#endif /* MAKECRCH */
+}
+
+#ifdef MAKECRCH
+local void write_table(out, table)
+    FILE *out;
+    const z_crc_t FAR *table;
+{
+    int n;
+
+    for (n = 0; n < 256; n++)
+        fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : "    ",
+                (unsigned long)(table[n]),
+                n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", "));
+}
+#endif /* MAKECRCH */
+
+#else /* !DYNAMIC_CRC_TABLE */
+/* ========================================================================
+ * Tables of CRC-32s of all single-byte values, made by make_crc_table().
+ */
+#include "crc32.h"
+#endif /* DYNAMIC_CRC_TABLE */
+
+/* =========================================================================
+ * This function can be used by asm versions of crc32()
+ */
+const z_crc_t FAR * ZEXPORT get_crc_table()
+{
+#ifdef DYNAMIC_CRC_TABLE
+    if (crc_table_empty)
+        make_crc_table();
+#endif /* DYNAMIC_CRC_TABLE */
+    return (const z_crc_t FAR *)crc_table;
+}
+
+/* ========================================================================= */
+#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8)
+#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1
+
+/* ========================================================================= */
+unsigned long ZEXPORT crc32_z(crc, buf, len)
+    unsigned long crc;
+    const unsigned char FAR *buf;
+    z_size_t len;
+{
+    if (buf == Z_NULL) return 0UL;
+
+#ifdef DYNAMIC_CRC_TABLE
+    if (crc_table_empty)
+        make_crc_table();
+#endif /* DYNAMIC_CRC_TABLE */
+
+#ifdef BYFOUR
+    if (sizeof(void *) == sizeof(ptrdiff_t)) {
+        z_crc_t endian;
+
+        endian = 1;
+        if (*((unsigned char *)(&endian)))
+            return crc32_little(crc, buf, len);
+        else
+            return crc32_big(crc, buf, len);
+    }
+#endif /* BYFOUR */
+    crc = crc ^ 0xffffffffUL;
+    while (len >= 8) {
+        DO8;
+        len -= 8;
+    }
+    if (len) do {
+        DO1;
+    } while (--len);
+    return crc ^ 0xffffffffUL;
+}
+
+/* ========================================================================= */
+unsigned long ZEXPORT crc32(crc, buf, len)
+    unsigned long crc;
+    const unsigned char FAR *buf;
+    uInt len;
+{
+    return crc32_z(crc, buf, len);
+}
+
+#ifdef BYFOUR
+
+/*
+   This BYFOUR code accesses the passed unsigned char * buffer with a 32-bit
+   integer pointer type. This violates the strict aliasing rule, where a
+   compiler can assume, for optimization purposes, that two pointers to
+   fundamentally different types won't ever point to the same memory. This can
+   manifest as a problem only if one of the pointers is written to. This code
+   only reads from those pointers. So long as this code remains isolated in
+   this compilation unit, there won't be a problem. For this reason, this code
+   should not be copied and pasted into a compilation unit in which other code
+   writes to the buffer that is passed to these routines.
+ */
+
+/* ========================================================================= */
+#define DOLIT4 c ^= *buf4++; \
+        c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \
+            crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24]
+#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4
+
+/* ========================================================================= */
+local unsigned long crc32_little(crc, buf, len)
+    unsigned long crc;
+    const unsigned char FAR *buf;
+    z_size_t len;
+{
+    register z_crc_t c;
+    register const z_crc_t FAR *buf4;
+
+    c = (z_crc_t)crc;
+    c = ~c;
+    while (len && ((ptrdiff_t)buf & 3)) {
+        c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+        len--;
+    }
+
+    buf4 = (const z_crc_t FAR *)(const void FAR *)buf;
+    while (len >= 32) {
+        DOLIT32;
+        len -= 32;
+    }
+    while (len >= 4) {
+        DOLIT4;
+        len -= 4;
+    }
+    buf = (const unsigned char FAR *)buf4;
+
+    if (len) do {
+        c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+    } while (--len);
+    c = ~c;
+    return (unsigned long)c;
+}
+
+/* ========================================================================= */
+#define DOBIG4 c ^= *buf4++; \
+        c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \
+            crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24]
+#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4
+
+/* ========================================================================= */
+local unsigned long crc32_big(crc, buf, len)
+    unsigned long crc;
+    const unsigned char FAR *buf;
+    z_size_t len;
+{
+    register z_crc_t c;
+    register const z_crc_t FAR *buf4;
+
+    c = ZSWAP32((z_crc_t)crc);
+    c = ~c;
+    while (len && ((ptrdiff_t)buf & 3)) {
+        c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+        len--;
+    }
+
+    buf4 = (const z_crc_t FAR *)(const void FAR *)buf;
+    while (len >= 32) {
+        DOBIG32;
+        len -= 32;
+    }
+    while (len >= 4) {
+        DOBIG4;
+        len -= 4;
+    }
+    buf = (const unsigned char FAR *)buf4;
+
+    if (len) do {
+        c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+    } while (--len);
+    c = ~c;
+    return (unsigned long)(ZSWAP32(c));
+}
+
+#endif /* BYFOUR */
+
+#define GF2_DIM 32      /* dimension of GF(2) vectors (length of CRC) */
+
+/* ========================================================================= */
+local unsigned long gf2_matrix_times(mat, vec)
+    unsigned long *mat;
+    unsigned long vec;
+{
+    unsigned long sum;
+
+    sum = 0;
+    while (vec) {
+        if (vec & 1)
+            sum ^= *mat;
+        vec >>= 1;
+        mat++;
+    }
+    return sum;
+}
+
+/* ========================================================================= */
+local void gf2_matrix_square(square, mat)
+    unsigned long *square;
+    unsigned long *mat;
+{
+    int n;
+
+    for (n = 0; n < GF2_DIM; n++)
+        square[n] = gf2_matrix_times(mat, mat[n]);
+}
+
+/* ========================================================================= */
+local uLong crc32_combine_(crc1, crc2, len2)
+    uLong crc1;
+    uLong crc2;
+    z_off64_t len2;
+{
+    int n;
+    unsigned long row;
+    unsigned long even[GF2_DIM];    /* even-power-of-two zeros operator */
+    unsigned long odd[GF2_DIM];     /* odd-power-of-two zeros operator */
+
+    /* degenerate case (also disallow negative lengths) */
+    if (len2 <= 0)
+        return crc1;
+
+    /* put operator for one zero bit in odd */
+    odd[0] = 0xedb88320UL;          /* CRC-32 polynomial */
+    row = 1;
+    for (n = 1; n < GF2_DIM; n++) {
+        odd[n] = row;
+        row <<= 1;
+    }
+
+    /* put operator for two zero bits in even */
+    gf2_matrix_square(even, odd);
+
+    /* put operator for four zero bits in odd */
+    gf2_matrix_square(odd, even);
+
+    /* apply len2 zeros to crc1 (first square will put the operator for one
+       zero byte, eight zero bits, in even) */
+    do {
+        /* apply zeros operator for this bit of len2 */
+        gf2_matrix_square(even, odd);
+        if (len2 & 1)
+            crc1 = gf2_matrix_times(even, crc1);
+        len2 >>= 1;
+
+        /* if no more bits set, then done */
+        if (len2 == 0)
+            break;
+
+        /* another iteration of the loop with odd and even swapped */
+        gf2_matrix_square(odd, even);
+        if (len2 & 1)
+            crc1 = gf2_matrix_times(odd, crc1);
+        len2 >>= 1;
+
+        /* if no more bits set, then done */
+    } while (len2 != 0);
+
+    /* return combined crc */
+    crc1 ^= crc2;
+    return crc1;
+}
+
+/* ========================================================================= */
+uLong ZEXPORT crc32_combine(crc1, crc2, len2)
+    uLong crc1;
+    uLong crc2;
+    z_off_t len2;
+{
+    return crc32_combine_(crc1, crc2, len2);
+}
+
+uLong ZEXPORT crc32_combine64(crc1, crc2, len2)
+    uLong crc1;
+    uLong crc2;
+    z_off64_t len2;
+{
+    return crc32_combine_(crc1, crc2, len2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/crc32.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,441 @@
+/* crc32.h -- tables for rapid CRC calculation
+ * Generated automatically by crc32.c
+ */
+
+local const z_crc_t FAR crc_table[TBLS][256] =
+{
+  {
+    0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
+    0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,
+    0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,
+    0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
+    0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,
+    0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,
+    0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,
+    0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
+    0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,
+    0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,
+    0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,
+    0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
+    0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,
+    0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,
+    0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,
+    0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
+    0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,
+    0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,
+    0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,
+    0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
+    0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,
+    0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,
+    0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,
+    0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
+    0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,
+    0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,
+    0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,
+    0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
+    0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,
+    0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,
+    0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,
+    0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
+    0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,
+    0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,
+    0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,
+    0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
+    0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,
+    0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,
+    0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,
+    0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
+    0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,
+    0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,
+    0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,
+    0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
+    0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,
+    0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,
+    0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,
+    0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
+    0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,
+    0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,
+    0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,
+    0x2d02ef8dUL
+#ifdef BYFOUR
+  },
+  {
+    0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL,
+    0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL,
+    0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL,
+    0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL,
+    0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL,
+    0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL,
+    0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL,
+    0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL,
+    0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL,
+    0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL,
+    0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL,
+    0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL,
+    0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL,
+    0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL,
+    0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL,
+    0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL,
+    0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL,
+    0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL,
+    0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL,
+    0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL,
+    0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL,
+    0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL,
+    0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL,
+    0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL,
+    0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL,
+    0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL,
+    0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL,
+    0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL,
+    0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL,
+    0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL,
+    0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL,
+    0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL,
+    0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL,
+    0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL,
+    0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL,
+    0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL,
+    0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL,
+    0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL,
+    0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL,
+    0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL,
+    0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL,
+    0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL,
+    0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL,
+    0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL,
+    0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL,
+    0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL,
+    0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL,
+    0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL,
+    0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL,
+    0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL,
+    0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL,
+    0x9324fd72UL
+  },
+  {
+    0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL,
+    0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL,
+    0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL,
+    0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL,
+    0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL,
+    0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL,
+    0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL,
+    0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL,
+    0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL,
+    0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL,
+    0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL,
+    0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL,
+    0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL,
+    0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL,
+    0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL,
+    0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL,
+    0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL,
+    0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL,
+    0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL,
+    0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL,
+    0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL,
+    0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL,
+    0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL,
+    0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL,
+    0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL,
+    0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL,
+    0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL,
+    0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL,
+    0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL,
+    0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL,
+    0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL,
+    0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL,
+    0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL,
+    0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL,
+    0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL,
+    0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL,
+    0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL,
+    0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL,
+    0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL,
+    0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL,
+    0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL,
+    0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL,
+    0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL,
+    0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL,
+    0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL,
+    0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL,
+    0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL,
+    0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL,
+    0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL,
+    0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL,
+    0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL,
+    0xbe9834edUL
+  },
+  {
+    0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL,
+    0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL,
+    0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL,
+    0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL,
+    0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL,
+    0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL,
+    0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL,
+    0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL,
+    0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL,
+    0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL,
+    0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL,
+    0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL,
+    0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL,
+    0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL,
+    0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL,
+    0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL,
+    0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL,
+    0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL,
+    0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL,
+    0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL,
+    0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL,
+    0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL,
+    0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL,
+    0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL,
+    0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL,
+    0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL,
+    0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL,
+    0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL,
+    0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL,
+    0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL,
+    0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL,
+    0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL,
+    0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL,
+    0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL,
+    0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL,
+    0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL,
+    0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL,
+    0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL,
+    0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL,
+    0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL,
+    0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL,
+    0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL,
+    0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL,
+    0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL,
+    0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL,
+    0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL,
+    0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL,
+    0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL,
+    0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL,
+    0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL,
+    0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL,
+    0xde0506f1UL
+  },
+  {
+    0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL,
+    0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL,
+    0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL,
+    0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL,
+    0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL,
+    0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL,
+    0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL,
+    0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL,
+    0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL,
+    0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL,
+    0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL,
+    0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL,
+    0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL,
+    0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL,
+    0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL,
+    0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL,
+    0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL,
+    0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL,
+    0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL,
+    0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL,
+    0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL,
+    0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL,
+    0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL,
+    0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL,
+    0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL,
+    0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL,
+    0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL,
+    0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL,
+    0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL,
+    0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL,
+    0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL,
+    0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL,
+    0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL,
+    0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL,
+    0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL,
+    0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL,
+    0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL,
+    0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL,
+    0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL,
+    0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL,
+    0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL,
+    0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL,
+    0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL,
+    0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL,
+    0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL,
+    0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL,
+    0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL,
+    0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL,
+    0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL,
+    0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL,
+    0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL,
+    0x8def022dUL
+  },
+  {
+    0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL,
+    0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL,
+    0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL,
+    0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL,
+    0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL,
+    0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL,
+    0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL,
+    0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL,
+    0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL,
+    0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL,
+    0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL,
+    0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL,
+    0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL,
+    0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL,
+    0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL,
+    0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL,
+    0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL,
+    0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL,
+    0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL,
+    0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL,
+    0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL,
+    0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL,
+    0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL,
+    0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL,
+    0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL,
+    0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL,
+    0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL,
+    0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL,
+    0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL,
+    0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL,
+    0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL,
+    0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL,
+    0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL,
+    0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL,
+    0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL,
+    0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL,
+    0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL,
+    0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL,
+    0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL,
+    0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL,
+    0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL,
+    0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL,
+    0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL,
+    0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL,
+    0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL,
+    0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL,
+    0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL,
+    0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL,
+    0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL,
+    0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL,
+    0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL,
+    0x72fd2493UL
+  },
+  {
+    0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL,
+    0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL,
+    0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL,
+    0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL,
+    0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL,
+    0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL,
+    0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL,
+    0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL,
+    0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL,
+    0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL,
+    0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL,
+    0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL,
+    0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL,
+    0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL,
+    0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL,
+    0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL,
+    0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL,
+    0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL,
+    0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL,
+    0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL,
+    0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL,
+    0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL,
+    0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL,
+    0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL,
+    0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL,
+    0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL,
+    0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL,
+    0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL,
+    0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL,
+    0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL,
+    0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL,
+    0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL,
+    0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL,
+    0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL,
+    0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL,
+    0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL,
+    0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL,
+    0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL,
+    0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL,
+    0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL,
+    0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL,
+    0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL,
+    0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL,
+    0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL,
+    0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL,
+    0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL,
+    0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL,
+    0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL,
+    0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL,
+    0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL,
+    0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL,
+    0xed3498beUL
+  },
+  {
+    0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL,
+    0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL,
+    0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL,
+    0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL,
+    0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL,
+    0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL,
+    0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL,
+    0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL,
+    0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL,
+    0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL,
+    0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL,
+    0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL,
+    0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL,
+    0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL,
+    0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL,
+    0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL,
+    0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL,
+    0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL,
+    0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL,
+    0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL,
+    0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL,
+    0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL,
+    0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL,
+    0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL,
+    0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL,
+    0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL,
+    0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL,
+    0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL,
+    0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL,
+    0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL,
+    0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL,
+    0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL,
+    0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL,
+    0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL,
+    0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL,
+    0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL,
+    0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL,
+    0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL,
+    0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL,
+    0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL,
+    0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL,
+    0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL,
+    0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL,
+    0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL,
+    0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL,
+    0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL,
+    0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL,
+    0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL,
+    0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL,
+    0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL,
+    0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL,
+    0xf10605deUL
+#endif
+  }
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/deflate.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,2163 @@
+/* deflate.c -- compress data using the deflation algorithm
+ * Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ *  ALGORITHM
+ *
+ *      The "deflation" process depends on being able to identify portions
+ *      of the input text which are identical to earlier input (within a
+ *      sliding window trailing behind the input currently being processed).
+ *
+ *      The most straightforward technique turns out to be the fastest for
+ *      most input files: try all possible matches and select the longest.
+ *      The key feature of this algorithm is that insertions into the string
+ *      dictionary are very simple and thus fast, and deletions are avoided
+ *      completely. Insertions are performed at each input character, whereas
+ *      string matches are performed only when the previous match ends. So it
+ *      is preferable to spend more time in matches to allow very fast string
+ *      insertions and avoid deletions. The matching algorithm for small
+ *      strings is inspired from that of Rabin & Karp. A brute force approach
+ *      is used to find longer strings when a small match has been found.
+ *      A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
+ *      (by Leonid Broukhis).
+ *         A previous version of this file used a more sophisticated algorithm
+ *      (by Fiala and Greene) which is guaranteed to run in linear amortized
+ *      time, but has a larger average cost, uses more memory and is patented.
+ *      However the F&G algorithm may be faster for some highly redundant
+ *      files if the parameter max_chain_length (described below) is too large.
+ *
+ *  ACKNOWLEDGEMENTS
+ *
+ *      The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
+ *      I found it in 'freeze' written by Leonid Broukhis.
+ *      Thanks to many people for bug reports and testing.
+ *
+ *  REFERENCES
+ *
+ *      Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
+ *      Available in http://tools.ietf.org/html/rfc1951
+ *
+ *      A description of the Rabin and Karp algorithm is given in the book
+ *         "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
+ *
+ *      Fiala,E.R., and Greene,D.H.
+ *         Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
+ *
+ */
+
+/* @(#) $Id$ */
+
+#include "deflate.h"
+
+const char deflate_copyright[] =
+   " deflate 1.2.11 Copyright 1995-2017 Jean-loup Gailly and Mark Adler ";
+/*
+  If you use the zlib library in a product, an acknowledgment is welcome
+  in the documentation of your product. If for some reason you cannot
+  include such an acknowledgment, I would appreciate that you keep this
+  copyright string in the executable of your product.
+ */
+
+/* ===========================================================================
+ *  Function prototypes.
+ */
+typedef enum {
+    need_more,      /* block not completed, need more input or more output */
+    block_done,     /* block flush performed */
+    finish_started, /* finish started, need only more output at next deflate */
+    finish_done     /* finish done, accept no more input or output */
+} block_state;
+
+typedef block_state (*compress_func) OF((deflate_state *s, int flush));
+/* Compression function. Returns the block state after the call. */
+
+local int deflateStateCheck      OF((z_streamp strm));
+local void slide_hash     OF((deflate_state *s));
+local void fill_window    OF((deflate_state *s));
+local block_state deflate_stored OF((deflate_state *s, int flush));
+local block_state deflate_fast   OF((deflate_state *s, int flush));
+#ifndef FASTEST
+local block_state deflate_slow   OF((deflate_state *s, int flush));
+#endif
+local block_state deflate_rle    OF((deflate_state *s, int flush));
+local block_state deflate_huff   OF((deflate_state *s, int flush));
+local void lm_init        OF((deflate_state *s));
+local void putShortMSB    OF((deflate_state *s, uInt b));
+local void flush_pending  OF((z_streamp strm));
+local unsigned read_buf   OF((z_streamp strm, Bytef *buf, unsigned size));
+#ifdef ASMV
+#  pragma message("Assembler code may have bugs -- use at your own risk")
+      void match_init OF((void)); /* asm code initialization */
+      uInt longest_match  OF((deflate_state *s, IPos cur_match));
+#else
+local uInt longest_match  OF((deflate_state *s, IPos cur_match));
+#endif
+
+#ifdef ZLIB_DEBUG
+local  void check_match OF((deflate_state *s, IPos start, IPos match,
+                            int length));
+#endif
+
+/* ===========================================================================
+ * Local data
+ */
+
+#define NIL 0
+/* Tail of hash chains */
+
+#ifndef TOO_FAR
+#  define TOO_FAR 4096
+#endif
+/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+typedef struct config_s {
+   ush good_length; /* reduce lazy search above this match length */
+   ush max_lazy;    /* do not perform lazy search above this match length */
+   ush nice_length; /* quit search above this match length */
+   ush max_chain;
+   compress_func func;
+} config;
+
+#ifdef FASTEST
+local const config configuration_table[2] = {
+/*      good lazy nice chain */
+/* 0 */ {0,    0,  0,    0, deflate_stored},  /* store only */
+/* 1 */ {4,    4,  8,    4, deflate_fast}}; /* max speed, no lazy matches */
+#else
+local const config configuration_table[10] = {
+/*      good lazy nice chain */
+/* 0 */ {0,    0,  0,    0, deflate_stored},  /* store only */
+/* 1 */ {4,    4,  8,    4, deflate_fast}, /* max speed, no lazy matches */
+/* 2 */ {4,    5, 16,    8, deflate_fast},
+/* 3 */ {4,    6, 32,   32, deflate_fast},
+
+/* 4 */ {4,    4, 16,   16, deflate_slow},  /* lazy matches */
+/* 5 */ {8,   16, 32,   32, deflate_slow},
+/* 6 */ {8,   16, 128, 128, deflate_slow},
+/* 7 */ {8,   32, 128, 256, deflate_slow},
+/* 8 */ {32, 128, 258, 1024, deflate_slow},
+/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */
+#endif
+
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
+ */
+
+/* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */
+#define RANK(f) (((f) * 2) - ((f) > 4 ? 9 : 0))
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * IN  assertion: all calls to UPDATE_HASH are made with consecutive input
+ *    characters, so that a running hash key can be computed from the previous
+ *    key instead of complete recalculation each time.
+ */
+#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
+
+
+/* ===========================================================================
+ * Insert string str in the dictionary and set match_head to the previous head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * If this file is compiled with -DFASTEST, the compression level is forced
+ * to 1, and no hash chains are maintained.
+ * IN  assertion: all calls to INSERT_STRING are made with consecutive input
+ *    characters and the first MIN_MATCH bytes of str are valid (except for
+ *    the last MIN_MATCH-1 bytes of the input file).
+ */
+#ifdef FASTEST
+#define INSERT_STRING(s, str, match_head) \
+   (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+    match_head = s->head[s->ins_h], \
+    s->head[s->ins_h] = (Pos)(str))
+#else
+#define INSERT_STRING(s, str, match_head) \
+   (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+    match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \
+    s->head[s->ins_h] = (Pos)(str))
+#endif
+
+/* ===========================================================================
+ * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
+ * prev[] will be initialized on the fly.
+ */
+#define CLEAR_HASH(s) \
+    s->head[s->hash_size-1] = NIL; \
+    zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
+
+/* ===========================================================================
+ * Slide the hash table when sliding the window down (could be avoided with 32
+ * bit values at the expense of memory usage). We slide even when level == 0 to
+ * keep the hash table consistent if we switch back to level > 0 later.
+ */
+local void slide_hash(s)
+    deflate_state *s;
+{
+    unsigned n, m;
+    Posf *p;
+    uInt wsize = s->w_size;
+
+    n = s->hash_size;
+    p = &s->head[n];
+    do {
+        m = *--p;
+        *p = (Pos)(m >= wsize ? m - wsize : NIL);
+    } while (--n);
+    n = wsize;
+#ifndef FASTEST
+    p = &s->prev[n];
+    do {
+        m = *--p;
+        *p = (Pos)(m >= wsize ? m - wsize : NIL);
+        /* If n is not on any hash chain, prev[n] is garbage but
+         * its value will never be used.
+         */
+    } while (--n);
+#endif
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateInit_(strm, level, version, stream_size)
+    z_streamp strm;
+    int level;
+    const char *version;
+    int stream_size;
+{
+    return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
+                         Z_DEFAULT_STRATEGY, version, stream_size);
+    /* To do: ignore strm->next_in if we use it as window */
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
+                  version, stream_size)
+    z_streamp strm;
+    int  level;
+    int  method;
+    int  windowBits;
+    int  memLevel;
+    int  strategy;
+    const char *version;
+    int stream_size;
+{
+    deflate_state *s;
+    int wrap = 1;
+    static const char my_version[] = ZLIB_VERSION;
+
+    ushf *overlay;
+    /* We overlay pending_buf and d_buf+l_buf. This works since the average
+     * output size for (length,distance) codes is <= 24 bits.
+     */
+
+    if (version == Z_NULL || version[0] != my_version[0] ||
+        stream_size != sizeof(z_stream)) {
+        return Z_VERSION_ERROR;
+    }
+    if (strm == Z_NULL) return Z_STREAM_ERROR;
+
+    strm->msg = Z_NULL;
+    if (strm->zalloc == (alloc_func)0) {
+#ifdef Z_SOLO
+        return Z_STREAM_ERROR;
+#else
+        strm->zalloc = zcalloc;
+        strm->opaque = (voidpf)0;
+#endif
+    }
+    if (strm->zfree == (free_func)0)
+#ifdef Z_SOLO
+        return Z_STREAM_ERROR;
+#else
+        strm->zfree = zcfree;
+#endif
+
+#ifdef FASTEST
+    if (level != 0) level = 1;
+#else
+    if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#endif
+
+    if (windowBits < 0) { /* suppress zlib wrapper */
+        wrap = 0;
+        windowBits = -windowBits;
+    }
+#ifdef GZIP
+    else if (windowBits > 15) {
+        wrap = 2;       /* write gzip wrapper instead */
+        windowBits -= 16;
+    }
+#endif
+    if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED ||
+        windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
+        strategy < 0 || strategy > Z_FIXED || (windowBits == 8 && wrap != 1)) {
+        return Z_STREAM_ERROR;
+    }
+    if (windowBits == 8) windowBits = 9;  /* until 256-byte window bug fixed */
+    s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
+    if (s == Z_NULL) return Z_MEM_ERROR;
+    strm->state = (struct internal_state FAR *)s;
+    s->strm = strm;
+    s->status = INIT_STATE;     /* to pass state test in deflateReset() */
+
+    s->wrap = wrap;
+    s->gzhead = Z_NULL;
+    s->w_bits = (uInt)windowBits;
+    s->w_size = 1 << s->w_bits;
+    s->w_mask = s->w_size - 1;
+
+    s->hash_bits = (uInt)memLevel + 7;
+    s->hash_size = 1 << s->hash_bits;
+    s->hash_mask = s->hash_size - 1;
+    s->hash_shift =  ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
+
+    s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
+    s->prev   = (Posf *)  ZALLOC(strm, s->w_size, sizeof(Pos));
+    s->head   = (Posf *)  ZALLOC(strm, s->hash_size, sizeof(Pos));
+
+    s->high_water = 0;      /* nothing written to s->window yet */
+
+    s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
+
+    overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
+    s->pending_buf = (uchf *) overlay;
+    s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);
+
+    if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
+        s->pending_buf == Z_NULL) {
+        s->status = FINISH_STATE;
+        strm->msg = ERR_MSG(Z_MEM_ERROR);
+        deflateEnd (strm);
+        return Z_MEM_ERROR;
+    }
+    s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
+    s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
+
+    s->level = level;
+    s->strategy = strategy;
+    s->method = (Byte)method;
+
+    return deflateReset(strm);
+}
+
+/* =========================================================================
+ * Check for a valid deflate stream state. Return 0 if ok, 1 if not.
+ */
+local int deflateStateCheck (strm)
+    z_streamp strm;
+{
+    deflate_state *s;
+    if (strm == Z_NULL ||
+        strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0)
+        return 1;
+    s = strm->state;
+    if (s == Z_NULL || s->strm != strm || (s->status != INIT_STATE &&
+#ifdef GZIP
+                                           s->status != GZIP_STATE &&
+#endif
+                                           s->status != EXTRA_STATE &&
+                                           s->status != NAME_STATE &&
+                                           s->status != COMMENT_STATE &&
+                                           s->status != HCRC_STATE &&
+                                           s->status != BUSY_STATE &&
+                                           s->status != FINISH_STATE))
+        return 1;
+    return 0;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
+    z_streamp strm;
+    const Bytef *dictionary;
+    uInt  dictLength;
+{
+    deflate_state *s;
+    uInt str, n;
+    int wrap;
+    unsigned avail;
+    z_const unsigned char *next;
+
+    if (deflateStateCheck(strm) || dictionary == Z_NULL)
+        return Z_STREAM_ERROR;
+    s = strm->state;
+    wrap = s->wrap;
+    if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead)
+        return Z_STREAM_ERROR;
+
+    /* when using zlib wrappers, compute Adler-32 for provided dictionary */
+    if (wrap == 1)
+        strm->adler = adler32(strm->adler, dictionary, dictLength);
+    s->wrap = 0;                    /* avoid computing Adler-32 in read_buf */
+
+    /* if dictionary would fill window, just replace the history */
+    if (dictLength >= s->w_size) {
+        if (wrap == 0) {            /* already empty otherwise */
+            CLEAR_HASH(s);
+            s->strstart = 0;
+            s->block_start = 0L;
+            s->insert = 0;
+        }
+        dictionary += dictLength - s->w_size;  /* use the tail */
+        dictLength = s->w_size;
+    }
+
+    /* insert dictionary into window and hash */
+    avail = strm->avail_in;
+    next = strm->next_in;
+    strm->avail_in = dictLength;
+    strm->next_in = (z_const Bytef *)dictionary;
+    fill_window(s);
+    while (s->lookahead >= MIN_MATCH) {
+        str = s->strstart;
+        n = s->lookahead - (MIN_MATCH-1);
+        do {
+            UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]);
+#ifndef FASTEST
+            s->prev[str & s->w_mask] = s->head[s->ins_h];
+#endif
+            s->head[s->ins_h] = (Pos)str;
+            str++;
+        } while (--n);
+        s->strstart = str;
+        s->lookahead = MIN_MATCH-1;
+        fill_window(s);
+    }
+    s->strstart += s->lookahead;
+    s->block_start = (long)s->strstart;
+    s->insert = s->lookahead;
+    s->lookahead = 0;
+    s->match_length = s->prev_length = MIN_MATCH-1;
+    s->match_available = 0;
+    strm->next_in = next;
+    strm->avail_in = avail;
+    s->wrap = wrap;
+    return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateGetDictionary (strm, dictionary, dictLength)
+    z_streamp strm;
+    Bytef *dictionary;
+    uInt  *dictLength;
+{
+    deflate_state *s;
+    uInt len;
+
+    if (deflateStateCheck(strm))
+        return Z_STREAM_ERROR;
+    s = strm->state;
+    len = s->strstart + s->lookahead;
+    if (len > s->w_size)
+        len = s->w_size;
+    if (dictionary != Z_NULL && len)
+        zmemcpy(dictionary, s->window + s->strstart + s->lookahead - len, len);
+    if (dictLength != Z_NULL)
+        *dictLength = len;
+    return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateResetKeep (strm)
+    z_streamp strm;
+{
+    deflate_state *s;
+
+    if (deflateStateCheck(strm)) {
+        return Z_STREAM_ERROR;
+    }
+
+    strm->total_in = strm->total_out = 0;
+    strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
+    strm->data_type = Z_UNKNOWN;
+
+    s = (deflate_state *)strm->state;
+    s->pending = 0;
+    s->pending_out = s->pending_buf;
+
+    if (s->wrap < 0) {
+        s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */
+    }
+    s->status =
+#ifdef GZIP
+        s->wrap == 2 ? GZIP_STATE :
+#endif
+        s->wrap ? INIT_STATE : BUSY_STATE;
+    strm->adler =
+#ifdef GZIP
+        s->wrap == 2 ? crc32(0L, Z_NULL, 0) :
+#endif
+        adler32(0L, Z_NULL, 0);
+    s->last_flush = Z_NO_FLUSH;
+
+    _tr_init(s);
+
+    return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateReset (strm)
+    z_streamp strm;
+{
+    int ret;
+
+    ret = deflateResetKeep(strm);
+    if (ret == Z_OK)
+        lm_init(strm->state);
+    return ret;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateSetHeader (strm, head)
+    z_streamp strm;
+    gz_headerp head;
+{
+    if (deflateStateCheck(strm) || strm->state->wrap != 2)
+        return Z_STREAM_ERROR;
+    strm->state->gzhead = head;
+    return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflatePending (strm, pending, bits)
+    unsigned *pending;
+    int *bits;
+    z_streamp strm;
+{
+    if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
+    if (pending != Z_NULL)
+        *pending = strm->state->pending;
+    if (bits != Z_NULL)
+        *bits = strm->state->bi_valid;
+    return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflatePrime (strm, bits, value)
+    z_streamp strm;
+    int bits;
+    int value;
+{
+    deflate_state *s;
+    int put;
+
+    if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
+    s = strm->state;
+    if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3))
+        return Z_BUF_ERROR;
+    do {
+        put = Buf_size - s->bi_valid;
+        if (put > bits)
+            put = bits;
+        s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid);
+        s->bi_valid += put;
+        _tr_flush_bits(s);
+        value >>= put;
+        bits -= put;
+    } while (bits);
+    return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateParams(strm, level, strategy)
+    z_streamp strm;
+    int level;
+    int strategy;
+{
+    deflate_state *s;
+    compress_func func;
+
+    if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
+    s = strm->state;
+
+#ifdef FASTEST
+    if (level != 0) level = 1;
+#else
+    if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#endif
+    if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) {
+        return Z_STREAM_ERROR;
+    }
+    func = configuration_table[s->level].func;
+
+    if ((strategy != s->strategy || func != configuration_table[level].func) &&
+        s->high_water) {
+        /* Flush the last buffer: */
+        int err = deflate(strm, Z_BLOCK);
+        if (err == Z_STREAM_ERROR)
+            return err;
+        if (strm->avail_out == 0)
+            return Z_BUF_ERROR;
+    }
+    if (s->level != level) {
+        if (s->level == 0 && s->matches != 0) {
+            if (s->matches == 1)
+                slide_hash(s);
+            else
+                CLEAR_HASH(s);
+            s->matches = 0;
+        }
+        s->level = level;
+        s->max_lazy_match   = configuration_table[level].max_lazy;
+        s->good_match       = configuration_table[level].good_length;
+        s->nice_match       = configuration_table[level].nice_length;
+        s->max_chain_length = configuration_table[level].max_chain;
+    }
+    s->strategy = strategy;
+    return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain)
+    z_streamp strm;
+    int good_length;
+    int max_lazy;
+    int nice_length;
+    int max_chain;
+{
+    deflate_state *s;
+
+    if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
+    s = strm->state;
+    s->good_match = (uInt)good_length;
+    s->max_lazy_match = (uInt)max_lazy;
+    s->nice_match = nice_length;
+    s->max_chain_length = (uInt)max_chain;
+    return Z_OK;
+}
+
+/* =========================================================================
+ * For the default windowBits of 15 and memLevel of 8, this function returns
+ * a close to exact, as well as small, upper bound on the compressed size.
+ * They are coded as constants here for a reason--if the #define's are
+ * changed, then this function needs to be changed as well.  The return
+ * value for 15 and 8 only works for those exact settings.
+ *
+ * For any setting other than those defaults for windowBits and memLevel,
+ * the value returned is a conservative worst case for the maximum expansion
+ * resulting from using fixed blocks instead of stored blocks, which deflate
+ * can emit on compressed data for some combinations of the parameters.
+ *
+ * This function could be more sophisticated to provide closer upper bounds for
+ * every combination of windowBits and memLevel.  But even the conservative
+ * upper bound of about 14% expansion does not seem onerous for output buffer
+ * allocation.
+ */
+uLong ZEXPORT deflateBound(strm, sourceLen)
+    z_streamp strm;
+    uLong sourceLen;
+{
+    deflate_state *s;
+    uLong complen, wraplen;
+
+    /* conservative upper bound for compressed data */
+    complen = sourceLen +
+              ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5;
+
+    /* if can't get parameters, return conservative bound plus zlib wrapper */
+    if (deflateStateCheck(strm))
+        return complen + 6;
+
+    /* compute wrapper length */
+    s = strm->state;
+    switch (s->wrap) {
+    case 0:                                 /* raw deflate */
+        wraplen = 0;
+        break;
+    case 1:                                 /* zlib wrapper */
+        wraplen = 6 + (s->strstart ? 4 : 0);
+        break;
+#ifdef GZIP
+    case 2:                                 /* gzip wrapper */
+        wraplen = 18;
+        if (s->gzhead != Z_NULL) {          /* user-supplied gzip header */
+            Bytef *str;
+            if (s->gzhead->extra != Z_NULL)
+                wraplen += 2 + s->gzhead->extra_len;
+            str = s->gzhead->name;
+            if (str != Z_NULL)
+                do {
+                    wraplen++;
+                } while (*str++);
+            str = s->gzhead->comment;
+            if (str != Z_NULL)
+                do {
+                    wraplen++;
+                } while (*str++);
+            if (s->gzhead->hcrc)
+                wraplen += 2;
+        }
+        break;
+#endif
+    default:                                /* for compiler happiness */
+        wraplen = 6;
+    }
+
+    /* if not default parameters, return conservative bound */
+    if (s->w_bits != 15 || s->hash_bits != 8 + 7)
+        return complen + wraplen;
+
+    /* default settings: return tight bound for that case */
+    return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
+           (sourceLen >> 25) + 13 - 6 + wraplen;
+}
+
+/* =========================================================================
+ * Put a short in the pending buffer. The 16-bit value is put in MSB order.
+ * IN assertion: the stream state is correct and there is enough room in
+ * pending_buf.
+ */
+local void putShortMSB (s, b)
+    deflate_state *s;
+    uInt b;
+{
+    put_byte(s, (Byte)(b >> 8));
+    put_byte(s, (Byte)(b & 0xff));
+}
+
+/* =========================================================================
+ * Flush as much pending output as possible. All deflate() output, except for
+ * some deflate_stored() output, goes through this function so some
+ * applications may wish to modify it to avoid allocating a large
+ * strm->next_out buffer and copying into it. (See also read_buf()).
+ */
+local void flush_pending(strm)
+    z_streamp strm;
+{
+    unsigned len;
+    deflate_state *s = strm->state;
+
+    _tr_flush_bits(s);
+    len = s->pending;
+    if (len > strm->avail_out) len = strm->avail_out;
+    if (len == 0) return;
+
+    zmemcpy(strm->next_out, s->pending_out, len);
+    strm->next_out  += len;
+    s->pending_out  += len;
+    strm->total_out += len;
+    strm->avail_out -= len;
+    s->pending      -= len;
+    if (s->pending == 0) {
+        s->pending_out = s->pending_buf;
+    }
+}
+
+/* ===========================================================================
+ * Update the header CRC with the bytes s->pending_buf[beg..s->pending - 1].
+ */
+#define HCRC_UPDATE(beg) \
+    do { \
+        if (s->gzhead->hcrc && s->pending > (beg)) \
+            strm->adler = crc32(strm->adler, s->pending_buf + (beg), \
+                                s->pending - (beg)); \
+    } while (0)
+
+/* ========================================================================= */
+int ZEXPORT deflate (strm, flush)
+    z_streamp strm;
+    int flush;
+{
+    int old_flush; /* value of flush param for previous deflate call */
+    deflate_state *s;
+
+    if (deflateStateCheck(strm) || flush > Z_BLOCK || flush < 0) {
+        return Z_STREAM_ERROR;
+    }
+    s = strm->state;
+
+    if (strm->next_out == Z_NULL ||
+        (strm->avail_in != 0 && strm->next_in == Z_NULL) ||
+        (s->status == FINISH_STATE && flush != Z_FINISH)) {
+        ERR_RETURN(strm, Z_STREAM_ERROR);
+    }
+    if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
+
+    old_flush = s->last_flush;
+    s->last_flush = flush;
+
+    /* Flush as much pending output as possible */
+    if (s->pending != 0) {
+        flush_pending(strm);
+        if (strm->avail_out == 0) {
+            /* Since avail_out is 0, deflate will be called again with
+             * more output space, but possibly with both pending and
+             * avail_in equal to zero. There won't be anything to do,
+             * but this is not an error situation so make sure we
+             * return OK instead of BUF_ERROR at next call of deflate:
+             */
+            s->last_flush = -1;
+            return Z_OK;
+        }
+
+    /* Make sure there is something to do and avoid duplicate consecutive
+     * flushes. For repeated and useless calls with Z_FINISH, we keep
+     * returning Z_STREAM_END instead of Z_BUF_ERROR.
+     */
+    } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) &&
+               flush != Z_FINISH) {
+        ERR_RETURN(strm, Z_BUF_ERROR);
+    }
+
+    /* User must not provide more input after the first FINISH: */
+    if (s->status == FINISH_STATE && strm->avail_in != 0) {
+        ERR_RETURN(strm, Z_BUF_ERROR);
+    }
+
+    /* Write the header */
+    if (s->status == INIT_STATE) {
+        /* zlib header */
+        uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
+        uInt level_flags;
+
+        if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2)
+            level_flags = 0;
+        else if (s->level < 6)
+            level_flags = 1;
+        else if (s->level == 6)
+            level_flags = 2;
+        else
+            level_flags = 3;
+        header |= (level_flags << 6);
+        if (s->strstart != 0) header |= PRESET_DICT;
+        header += 31 - (header % 31);
+
+        putShortMSB(s, header);
+
+        /* Save the adler32 of the preset dictionary: */
+        if (s->strstart != 0) {
+            putShortMSB(s, (uInt)(strm->adler >> 16));
+            putShortMSB(s, (uInt)(strm->adler & 0xffff));
+        }
+        strm->adler = adler32(0L, Z_NULL, 0);
+        s->status = BUSY_STATE;
+
+        /* Compression must start with an empty pending buffer */
+        flush_pending(strm);
+        if (s->pending != 0) {
+            s->last_flush = -1;
+            return Z_OK;
+        }
+    }
+#ifdef GZIP
+    if (s->status == GZIP_STATE) {
+        /* gzip header */
+        strm->adler = crc32(0L, Z_NULL, 0);
+        put_byte(s, 31);
+        put_byte(s, 139);
+        put_byte(s, 8);
+        if (s->gzhead == Z_NULL) {
+            put_byte(s, 0);
+            put_byte(s, 0);
+            put_byte(s, 0);
+            put_byte(s, 0);
+            put_byte(s, 0);
+            put_byte(s, s->level == 9 ? 2 :
+                     (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
+                      4 : 0));
+            put_byte(s, OS_CODE);
+            s->status = BUSY_STATE;
+
+            /* Compression must start with an empty pending buffer */
+            flush_pending(strm);
+            if (s->pending != 0) {
+                s->last_flush = -1;
+                return Z_OK;
+            }
+        }
+        else {
+            put_byte(s, (s->gzhead->text ? 1 : 0) +
+                     (s->gzhead->hcrc ? 2 : 0) +
+                     (s->gzhead->extra == Z_NULL ? 0 : 4) +
+                     (s->gzhead->name == Z_NULL ? 0 : 8) +
+                     (s->gzhead->comment == Z_NULL ? 0 : 16)
+                     );
+            put_byte(s, (Byte)(s->gzhead->time & 0xff));
+            put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff));
+            put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff));
+            put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff));
+            put_byte(s, s->level == 9 ? 2 :
+                     (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
+                      4 : 0));
+            put_byte(s, s->gzhead->os & 0xff);
+            if (s->gzhead->extra != Z_NULL) {
+                put_byte(s, s->gzhead->extra_len & 0xff);
+                put_byte(s, (s->gzhead->extra_len >> 8) & 0xff);
+            }
+            if (s->gzhead->hcrc)
+                strm->adler = crc32(strm->adler, s->pending_buf,
+                                    s->pending);
+            s->gzindex = 0;
+            s->status = EXTRA_STATE;
+        }
+    }
+    if (s->status == EXTRA_STATE) {
+        if (s->gzhead->extra != Z_NULL) {
+            ulg beg = s->pending;   /* start of bytes to update crc */
+            uInt left = (s->gzhead->extra_len & 0xffff) - s->gzindex;
+            while (s->pending + left > s->pending_buf_size) {
+                uInt copy = s->pending_buf_size - s->pending;
+                zmemcpy(s->pending_buf + s->pending,
+                        s->gzhead->extra + s->gzindex, copy);
+                s->pending = s->pending_buf_size;
+                HCRC_UPDATE(beg);
+                s->gzindex += copy;
+                flush_pending(strm);
+                if (s->pending != 0) {
+                    s->last_flush = -1;
+                    return Z_OK;
+                }
+                beg = 0;
+                left -= copy;
+            }
+            zmemcpy(s->pending_buf + s->pending,
+                    s->gzhead->extra + s->gzindex, left);
+            s->pending += left;
+            HCRC_UPDATE(beg);
+            s->gzindex = 0;
+        }
+        s->status = NAME_STATE;
+    }
+    if (s->status == NAME_STATE) {
+        if (s->gzhead->name != Z_NULL) {
+            ulg beg = s->pending;   /* start of bytes to update crc */
+            int val;
+            do {
+                if (s->pending == s->pending_buf_size) {
+                    HCRC_UPDATE(beg);
+                    flush_pending(strm);
+                    if (s->pending != 0) {
+                        s->last_flush = -1;
+                        return Z_OK;
+                    }
+                    beg = 0;
+                }
+                val = s->gzhead->name[s->gzindex++];
+                put_byte(s, val);
+            } while (val != 0);
+            HCRC_UPDATE(beg);
+            s->gzindex = 0;
+        }
+        s->status = COMMENT_STATE;
+    }
+    if (s->status == COMMENT_STATE) {
+        if (s->gzhead->comment != Z_NULL) {
+            ulg beg = s->pending;   /* start of bytes to update crc */
+            int val;
+            do {
+                if (s->pending == s->pending_buf_size) {
+                    HCRC_UPDATE(beg);
+                    flush_pending(strm);
+                    if (s->pending != 0) {
+                        s->last_flush = -1;
+                        return Z_OK;
+                    }
+                    beg = 0;
+                }
+                val = s->gzhead->comment[s->gzindex++];
+                put_byte(s, val);
+            } while (val != 0);
+            HCRC_UPDATE(beg);
+        }
+        s->status = HCRC_STATE;
+    }
+    if (s->status == HCRC_STATE) {
+        if (s->gzhead->hcrc) {
+            if (s->pending + 2 > s->pending_buf_size) {
+                flush_pending(strm);
+                if (s->pending != 0) {
+                    s->last_flush = -1;
+                    return Z_OK;
+                }
+            }
+            put_byte(s, (Byte)(strm->adler & 0xff));
+            put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
+            strm->adler = crc32(0L, Z_NULL, 0);
+        }
+        s->status = BUSY_STATE;
+
+        /* Compression must start with an empty pending buffer */
+        flush_pending(strm);
+        if (s->pending != 0) {
+            s->last_flush = -1;
+            return Z_OK;
+        }
+    }
+#endif
+
+    /* Start a new block or continue the current one.
+     */
+    if (strm->avail_in != 0 || s->lookahead != 0 ||
+        (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) {
+        block_state bstate;
+
+        bstate = s->level == 0 ? deflate_stored(s, flush) :
+                 s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) :
+                 s->strategy == Z_RLE ? deflate_rle(s, flush) :
+                 (*(configuration_table[s->level].func))(s, flush);
+
+        if (bstate == finish_started || bstate == finish_done) {
+            s->status = FINISH_STATE;
+        }
+        if (bstate == need_more || bstate == finish_started) {
+            if (strm->avail_out == 0) {
+                s->last_flush = -1; /* avoid BUF_ERROR next call, see above */
+            }
+            return Z_OK;
+            /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
+             * of deflate should use the same flush parameter to make sure
+             * that the flush is complete. So we don't have to output an
+             * empty block here, this will be done at next call. This also
+             * ensures that for a very small output buffer, we emit at most
+             * one empty block.
+             */
+        }
+        if (bstate == block_done) {
+            if (flush == Z_PARTIAL_FLUSH) {
+                _tr_align(s);
+            } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */
+                _tr_stored_block(s, (char*)0, 0L, 0);
+                /* For a full flush, this empty block will be recognized
+                 * as a special marker by inflate_sync().
+                 */
+                if (flush == Z_FULL_FLUSH) {
+                    CLEAR_HASH(s);             /* forget history */
+                    if (s->lookahead == 0) {
+                        s->strstart = 0;
+                        s->block_start = 0L;
+                        s->insert = 0;
+                    }
+                }
+            }
+            flush_pending(strm);
+            if (strm->avail_out == 0) {
+              s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */
+              return Z_OK;
+            }
+        }
+    }
+
+    if (flush != Z_FINISH) return Z_OK;
+    if (s->wrap <= 0) return Z_STREAM_END;
+
+    /* Write the trailer */
+#ifdef GZIP
+    if (s->wrap == 2) {
+        put_byte(s, (Byte)(strm->adler & 0xff));
+        put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
+        put_byte(s, (Byte)((strm->adler >> 16) & 0xff));
+        put_byte(s, (Byte)((strm->adler >> 24) & 0xff));
+        put_byte(s, (Byte)(strm->total_in & 0xff));
+        put_byte(s, (Byte)((strm->total_in >> 8) & 0xff));
+        put_byte(s, (Byte)((strm->total_in >> 16) & 0xff));
+        put_byte(s, (Byte)((strm->total_in >> 24) & 0xff));
+    }
+    else
+#endif
+    {
+        putShortMSB(s, (uInt)(strm->adler >> 16));
+        putShortMSB(s, (uInt)(strm->adler & 0xffff));
+    }
+    flush_pending(strm);
+    /* If avail_out is zero, the application will call deflate again
+     * to flush the rest.
+     */
+    if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */
+    return s->pending != 0 ? Z_OK : Z_STREAM_END;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateEnd (strm)
+    z_streamp strm;
+{
+    int status;
+
+    if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
+
+    status = strm->state->status;
+
+    /* Deallocate in reverse order of allocations: */
+    TRY_FREE(strm, strm->state->pending_buf);
+    TRY_FREE(strm, strm->state->head);
+    TRY_FREE(strm, strm->state->prev);
+    TRY_FREE(strm, strm->state->window);
+
+    ZFREE(strm, strm->state);
+    strm->state = Z_NULL;
+
+    return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
+}
+
+/* =========================================================================
+ * Copy the source state to the destination state.
+ * To simplify the source, this is not supported for 16-bit MSDOS (which
+ * doesn't have enough memory anyway to duplicate compression states).
+ */
+int ZEXPORT deflateCopy (dest, source)
+    z_streamp dest;
+    z_streamp source;
+{
+#ifdef MAXSEG_64K
+    return Z_STREAM_ERROR;
+#else
+    deflate_state *ds;
+    deflate_state *ss;
+    ushf *overlay;
+
+
+    if (deflateStateCheck(source) || dest == Z_NULL) {
+        return Z_STREAM_ERROR;
+    }
+
+    ss = source->state;
+
+    zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream));
+
+    ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
+    if (ds == Z_NULL) return Z_MEM_ERROR;
+    dest->state = (struct internal_state FAR *) ds;
+    zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state));
+    ds->strm = dest;
+
+    ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
+    ds->prev   = (Posf *)  ZALLOC(dest, ds->w_size, sizeof(Pos));
+    ds->head   = (Posf *)  ZALLOC(dest, ds->hash_size, sizeof(Pos));
+    overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
+    ds->pending_buf = (uchf *) overlay;
+
+    if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
+        ds->pending_buf == Z_NULL) {
+        deflateEnd (dest);
+        return Z_MEM_ERROR;
+    }
+    /* following zmemcpy do not work for 16-bit MSDOS */
+    zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
+    zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos));
+    zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos));
+    zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
+
+    ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
+    ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);
+    ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
+
+    ds->l_desc.dyn_tree = ds->dyn_ltree;
+    ds->d_desc.dyn_tree = ds->dyn_dtree;
+    ds->bl_desc.dyn_tree = ds->bl_tree;
+
+    return Z_OK;
+#endif /* MAXSEG_64K */
+}
+
+/* ===========================================================================
+ * Read a new buffer from the current input stream, update the adler32
+ * and total number of bytes read.  All deflate() input goes through
+ * this function so some applications may wish to modify it to avoid
+ * allocating a large strm->next_in buffer and copying from it.
+ * (See also flush_pending()).
+ */
+local unsigned read_buf(strm, buf, size)
+    z_streamp strm;
+    Bytef *buf;
+    unsigned size;
+{
+    unsigned len = strm->avail_in;
+
+    if (len > size) len = size;
+    if (len == 0) return 0;
+
+    strm->avail_in  -= len;
+
+    zmemcpy(buf, strm->next_in, len);
+    if (strm->state->wrap == 1) {
+        strm->adler = adler32(strm->adler, buf, len);
+    }
+#ifdef GZIP
+    else if (strm->state->wrap == 2) {
+        strm->adler = crc32(strm->adler, buf, len);
+    }
+#endif
+    strm->next_in  += len;
+    strm->total_in += len;
+
+    return len;
+}
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new zlib stream
+ */
+local void lm_init (s)
+    deflate_state *s;
+{
+    s->window_size = (ulg)2L*s->w_size;
+
+    CLEAR_HASH(s);
+
+    /* Set the default configuration parameters:
+     */
+    s->max_lazy_match   = configuration_table[s->level].max_lazy;
+    s->good_match       = configuration_table[s->level].good_length;
+    s->nice_match       = configuration_table[s->level].nice_length;
+    s->max_chain_length = configuration_table[s->level].max_chain;
+
+    s->strstart = 0;
+    s->block_start = 0L;
+    s->lookahead = 0;
+    s->insert = 0;
+    s->match_length = s->prev_length = MIN_MATCH-1;
+    s->match_available = 0;
+    s->ins_h = 0;
+#ifndef FASTEST
+#ifdef ASMV
+    match_init(); /* initialize the asm code */
+#endif
+#endif
+}
+
+#ifndef FASTEST
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ *   string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ * OUT assertion: the match length is not greater than s->lookahead.
+ */
+#ifndef ASMV
+/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
+ * match.S. The code will be functionally equivalent.
+ */
+local uInt longest_match(s, cur_match)
+    deflate_state *s;
+    IPos cur_match;                             /* current match */
+{
+    unsigned chain_length = s->max_chain_length;/* max hash chain length */
+    register Bytef *scan = s->window + s->strstart; /* current string */
+    register Bytef *match;                      /* matched string */
+    register int len;                           /* length of current match */
+    int best_len = (int)s->prev_length;         /* best match length so far */
+    int nice_match = s->nice_match;             /* stop if match long enough */
+    IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
+        s->strstart - (IPos)MAX_DIST(s) : NIL;
+    /* Stop when cur_match becomes <= limit. To simplify the code,
+     * we prevent matches with the string of window index 0.
+     */
+    Posf *prev = s->prev;
+    uInt wmask = s->w_mask;
+
+#ifdef UNALIGNED_OK
+    /* Compare two bytes at a time. Note: this is not always beneficial.
+     * Try with and without -DUNALIGNED_OK to check.
+     */
+    register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
+    register ush scan_start = *(ushf*)scan;
+    register ush scan_end   = *(ushf*)(scan+best_len-1);
+#else
+    register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+    register Byte scan_end1  = scan[best_len-1];
+    register Byte scan_end   = scan[best_len];
+#endif
+
+    /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+     * It is easy to get rid of this optimization if necessary.
+     */
+    Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+    /* Do not waste too much time if we already have a good match: */
+    if (s->prev_length >= s->good_match) {
+        chain_length >>= 2;
+    }
+    /* Do not look for matches beyond the end of the input. This is necessary
+     * to make deflate deterministic.
+     */
+    if ((uInt)nice_match > s->lookahead) nice_match = (int)s->lookahead;
+
+    Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+    do {
+        Assert(cur_match < s->strstart, "no future");
+        match = s->window + cur_match;
+
+        /* Skip to next match if the match length cannot increase
+         * or if the match length is less than 2.  Note that the checks below
+         * for insufficient lookahead only occur occasionally for performance
+         * reasons.  Therefore uninitialized memory will be accessed, and
+         * conditional jumps will be made that depend on those values.
+         * However the length of the match is limited to the lookahead, so
+         * the output of deflate is not affected by the uninitialized values.
+         */
+#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
+        /* This code assumes sizeof(unsigned short) == 2. Do not use
+         * UNALIGNED_OK if your compiler uses a different size.
+         */
+        if (*(ushf*)(match+best_len-1) != scan_end ||
+            *(ushf*)match != scan_start) continue;
+
+        /* It is not necessary to compare scan[2] and match[2] since they are
+         * always equal when the other bytes match, given that the hash keys
+         * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
+         * strstart+3, +5, ... up to strstart+257. We check for insufficient
+         * lookahead only every 4th comparison; the 128th check will be made
+         * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
+         * necessary to put more guard bytes at the end of the window, or
+         * to check more often for insufficient lookahead.
+         */
+        Assert(scan[2] == match[2], "scan[2]?");
+        scan++, match++;
+        do {
+        } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+                 scan < strend);
+        /* The funny "do {}" generates better code on most compilers */
+
+        /* Here, scan <= window+strstart+257 */
+        Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+        if (*scan == *match) scan++;
+
+        len = (MAX_MATCH - 1) - (int)(strend-scan);
+        scan = strend - (MAX_MATCH-1);
+
+#else /* UNALIGNED_OK */
+
+        if (match[best_len]   != scan_end  ||
+            match[best_len-1] != scan_end1 ||
+            *match            != *scan     ||
+            *++match          != scan[1])      continue;
+
+        /* The check at best_len-1 can be removed because it will be made
+         * again later. (This heuristic is not always a win.)
+         * It is not necessary to compare scan[2] and match[2] since they
+         * are always equal when the other bytes match, given that
+         * the hash keys are equal and that HASH_BITS >= 8.
+         */
+        scan += 2, match++;
+        Assert(*scan == *match, "match[2]?");
+
+        /* We check for insufficient lookahead only every 8th comparison;
+         * the 256th check will be made at strstart+258.
+         */
+        do {
+        } while (*++scan == *++match && *++scan == *++match &&
+                 *++scan == *++match && *++scan == *++match &&
+                 *++scan == *++match && *++scan == *++match &&
+                 *++scan == *++match && *++scan == *++match &&
+                 scan < strend);
+
+        Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+        len = MAX_MATCH - (int)(strend - scan);
+        scan = strend - MAX_MATCH;
+
+#endif /* UNALIGNED_OK */
+
+        if (len > best_len) {
+            s->match_start = cur_match;
+            best_len = len;
+            if (len >= nice_match) break;
+#ifdef UNALIGNED_OK
+            scan_end = *(ushf*)(scan+best_len-1);
+#else
+            scan_end1  = scan[best_len-1];
+            scan_end   = scan[best_len];
+#endif
+        }
+    } while ((cur_match = prev[cur_match & wmask]) > limit
+             && --chain_length != 0);
+
+    if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
+    return s->lookahead;
+}
+#endif /* ASMV */
+
+#else /* FASTEST */
+
+/* ---------------------------------------------------------------------------
+ * Optimized version for FASTEST only
+ */
+local uInt longest_match(s, cur_match)
+    deflate_state *s;
+    IPos cur_match;                             /* current match */
+{
+    register Bytef *scan = s->window + s->strstart; /* current string */
+    register Bytef *match;                       /* matched string */
+    register int len;                           /* length of current match */
+    register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+
+    /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+     * It is easy to get rid of this optimization if necessary.
+     */
+    Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+    Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+    Assert(cur_match < s->strstart, "no future");
+
+    match = s->window + cur_match;
+
+    /* Return failure if the match length is less than 2:
+     */
+    if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1;
+
+    /* The check at best_len-1 can be removed because it will be made
+     * again later. (This heuristic is not always a win.)
+     * It is not necessary to compare scan[2] and match[2] since they
+     * are always equal when the other bytes match, given that
+     * the hash keys are equal and that HASH_BITS >= 8.
+     */
+    scan += 2, match += 2;
+    Assert(*scan == *match, "match[2]?");
+
+    /* We check for insufficient lookahead only every 8th comparison;
+     * the 256th check will be made at strstart+258.
+     */
+    do {
+    } while (*++scan == *++match && *++scan == *++match &&
+             *++scan == *++match && *++scan == *++match &&
+             *++scan == *++match && *++scan == *++match &&
+             *++scan == *++match && *++scan == *++match &&
+             scan < strend);
+
+    Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+    len = MAX_MATCH - (int)(strend - scan);
+
+    if (len < MIN_MATCH) return MIN_MATCH - 1;
+
+    s->match_start = cur_match;
+    return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead;
+}
+
+#endif /* FASTEST */
+
+#ifdef ZLIB_DEBUG
+
+#define EQUAL 0
+/* result of memcmp for equal strings */
+
+/* ===========================================================================
+ * Check that the match at match_start is indeed a match.
+ */
+local void check_match(s, start, match, length)
+    deflate_state *s;
+    IPos start, match;
+    int length;
+{
+    /* check that the match is indeed a match */
+    if (zmemcmp(s->window + match,
+                s->window + start, length) != EQUAL) {
+        fprintf(stderr, " start %u, match %u, length %d\n",
+                start, match, length);
+        do {
+            fprintf(stderr, "%c%c", s->window[match++], s->window[start++]);
+        } while (--length != 0);
+        z_error("invalid match");
+    }
+    if (z_verbose > 1) {
+        fprintf(stderr,"\\[%d,%d]", start-match, length);
+        do { putc(s->window[start++], stderr); } while (--length != 0);
+    }
+}
+#else
+#  define check_match(s, start, match, length)
+#endif /* ZLIB_DEBUG */
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * IN assertion: lookahead < MIN_LOOKAHEAD
+ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+ *    At least one byte has been read, or avail_in == 0; reads are
+ *    performed for at least two bytes (required for the zip translate_eol
+ *    option -- not supported here).
+ */
+local void fill_window(s)
+    deflate_state *s;
+{
+    unsigned n;
+    unsigned more;    /* Amount of free space at the end of the window. */
+    uInt wsize = s->w_size;
+
+    Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead");
+
+    do {
+        more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
+
+        /* Deal with !@#$% 64K limit: */
+        if (sizeof(int) <= 2) {
+            if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
+                more = wsize;
+
+            } else if (more == (unsigned)(-1)) {
+                /* Very unlikely, but possible on 16 bit machine if
+                 * strstart == 0 && lookahead == 1 (input done a byte at time)
+                 */
+                more--;
+            }
+        }
+
+        /* If the window is almost full and there is insufficient lookahead,
+         * move the upper half to the lower one to make room in the upper half.
+         */
+        if (s->strstart >= wsize+MAX_DIST(s)) {
+
+            zmemcpy(s->window, s->window+wsize, (unsigned)wsize - more);
+            s->match_start -= wsize;
+            s->strstart    -= wsize; /* we now have strstart >= MAX_DIST */
+            s->block_start -= (long) wsize;
+            slide_hash(s);
+            more += wsize;
+        }
+        if (s->strm->avail_in == 0) break;
+
+        /* If there was no sliding:
+         *    strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+         *    more == window_size - lookahead - strstart
+         * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+         * => more >= window_size - 2*WSIZE + 2
+         * In the BIG_MEM or MMAP case (not yet supported),
+         *   window_size == input_size + MIN_LOOKAHEAD  &&
+         *   strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
+         * Otherwise, window_size == 2*WSIZE so more >= 2.
+         * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+         */
+        Assert(more >= 2, "more < 2");
+
+        n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
+        s->lookahead += n;
+
+        /* Initialize the hash value now that we have some input: */
+        if (s->lookahead + s->insert >= MIN_MATCH) {
+            uInt str = s->strstart - s->insert;
+            s->ins_h = s->window[str];
+            UPDATE_HASH(s, s->ins_h, s->window[str + 1]);
+#if MIN_MATCH != 3
+            Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+            while (s->insert) {
+                UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]);
+#ifndef FASTEST
+                s->prev[str & s->w_mask] = s->head[s->ins_h];
+#endif
+                s->head[s->ins_h] = (Pos)str;
+                str++;
+                s->insert--;
+                if (s->lookahead + s->insert < MIN_MATCH)
+                    break;
+            }
+        }
+        /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
+         * but this is not important since only literal bytes will be emitted.
+         */
+
+    } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
+
+    /* If the WIN_INIT bytes after the end of the current data have never been
+     * written, then zero those bytes in order to avoid memory check reports of
+     * the use of uninitialized (or uninitialised as Julian writes) bytes by
+     * the longest match routines.  Update the high water mark for the next
+     * time through here.  WIN_INIT is set to MAX_MATCH since the longest match
+     * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
+     */
+    if (s->high_water < s->window_size) {
+        ulg curr = s->strstart + (ulg)(s->lookahead);
+        ulg init;
+
+        if (s->high_water < curr) {
+            /* Previous high water mark below current data -- zero WIN_INIT
+             * bytes or up to end of window, whichever is less.
+             */
+            init = s->window_size - curr;
+            if (init > WIN_INIT)
+                init = WIN_INIT;
+            zmemzero(s->window + curr, (unsigned)init);
+            s->high_water = curr + init;
+        }
+        else if (s->high_water < (ulg)curr + WIN_INIT) {
+            /* High water mark at or above current data, but below current data
+             * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
+             * to end of window, whichever is less.
+             */
+            init = (ulg)curr + WIN_INIT - s->high_water;
+            if (init > s->window_size - s->high_water)
+                init = s->window_size - s->high_water;
+            zmemzero(s->window + s->high_water, (unsigned)init);
+            s->high_water += init;
+        }
+    }
+
+    Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
+           "not enough room for search");
+}
+
+/* ===========================================================================
+ * Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match.
+ */
+#define FLUSH_BLOCK_ONLY(s, last) { \
+   _tr_flush_block(s, (s->block_start >= 0L ? \
+                   (charf *)&s->window[(unsigned)s->block_start] : \
+                   (charf *)Z_NULL), \
+                (ulg)((long)s->strstart - s->block_start), \
+                (last)); \
+   s->block_start = s->strstart; \
+   flush_pending(s->strm); \
+   Tracev((stderr,"[FLUSH]")); \
+}
+
+/* Same but force premature exit if necessary. */
+#define FLUSH_BLOCK(s, last) { \
+   FLUSH_BLOCK_ONLY(s, last); \
+   if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \
+}
+
+/* Maximum stored block length in deflate format (not including header). */
+#define MAX_STORED 65535
+
+/* Minimum of a and b. */
+#define MIN(a, b) ((a) > (b) ? (b) : (a))
+
+/* ===========================================================================
+ * Copy without compression as much as possible from the input stream, return
+ * the current block state.
+ *
+ * In case deflateParams() is used to later switch to a non-zero compression
+ * level, s->matches (otherwise unused when storing) keeps track of the number
+ * of hash table slides to perform. If s->matches is 1, then one hash table
+ * slide will be done when switching. If s->matches is 2, the maximum value
+ * allowed here, then the hash table will be cleared, since two or more slides
+ * is the same as a clear.
+ *
+ * deflate_stored() is written to minimize the number of times an input byte is
+ * copied. It is most efficient with large input and output buffers, which
+ * maximizes the opportunites to have a single copy from next_in to next_out.
+ */
+local block_state deflate_stored(s, flush)
+    deflate_state *s;
+    int flush;
+{
+    /* Smallest worthy block size when not flushing or finishing. By default
+     * this is 32K. This can be as small as 507 bytes for memLevel == 1. For
+     * large input and output buffers, the stored block size will be larger.
+     */
+    unsigned min_block = MIN(s->pending_buf_size - 5, s->w_size);
+
+    /* Copy as many min_block or larger stored blocks directly to next_out as
+     * possible. If flushing, copy the remaining available input to next_out as
+     * stored blocks, if there is enough space.
+     */
+    unsigned len, left, have, last = 0;
+    unsigned used = s->strm->avail_in;
+    do {
+        /* Set len to the maximum size block that we can copy directly with the
+         * available input data and output space. Set left to how much of that
+         * would be copied from what's left in the window.
+         */
+        len = MAX_STORED;       /* maximum deflate stored block length */
+        have = (s->bi_valid + 42) >> 3;         /* number of header bytes */
+        if (s->strm->avail_out < have)          /* need room for header */
+            break;
+            /* maximum stored block length that will fit in avail_out: */
+        have = s->strm->avail_out - have;
+        left = s->strstart - s->block_start;    /* bytes left in window */
+        if (len > (ulg)left + s->strm->avail_in)
+            len = left + s->strm->avail_in;     /* limit len to the input */
+        if (len > have)
+            len = have;                         /* limit len to the output */
+
+        /* If the stored block would be less than min_block in length, or if
+         * unable to copy all of the available input when flushing, then try
+         * copying to the window and the pending buffer instead. Also don't
+         * write an empty block when flushing -- deflate() does that.
+         */
+        if (len < min_block && ((len == 0 && flush != Z_FINISH) ||
+                                flush == Z_NO_FLUSH ||
+                                len != left + s->strm->avail_in))
+            break;
+
+        /* Make a dummy stored block in pending to get the header bytes,
+         * including any pending bits. This also updates the debugging counts.
+         */
+        last = flush == Z_FINISH && len == left + s->strm->avail_in ? 1 : 0;
+        _tr_stored_block(s, (char *)0, 0L, last);
+
+        /* Replace the lengths in the dummy stored block with len. */
+        s->pending_buf[s->pending - 4] = len;
+        s->pending_buf[s->pending - 3] = len >> 8;
+        s->pending_buf[s->pending - 2] = ~len;
+        s->pending_buf[s->pending - 1] = ~len >> 8;
+
+        /* Write the stored block header bytes. */
+        flush_pending(s->strm);
+
+#ifdef ZLIB_DEBUG
+        /* Update debugging counts for the data about to be copied. */
+        s->compressed_len += len << 3;
+        s->bits_sent += len << 3;
+#endif
+
+        /* Copy uncompressed bytes from the window to next_out. */
+        if (left) {
+            if (left > len)
+                left = len;
+            zmemcpy(s->strm->next_out, s->window + s->block_start, left);
+            s->strm->next_out += left;
+            s->strm->avail_out -= left;
+            s->strm->total_out += left;
+            s->block_start += left;
+            len -= left;
+        }
+
+        /* Copy uncompressed bytes directly from next_in to next_out, updating
+         * the check value.
+         */
+        if (len) {
+            read_buf(s->strm, s->strm->next_out, len);
+            s->strm->next_out += len;
+            s->strm->avail_out -= len;
+            s->strm->total_out += len;
+        }
+    } while (last == 0);
+
+    /* Update the sliding window with the last s->w_size bytes of the copied
+     * data, or append all of the copied data to the existing window if less
+     * than s->w_size bytes were copied. Also update the number of bytes to
+     * insert in the hash tables, in the event that deflateParams() switches to
+     * a non-zero compression level.
+     */
+    used -= s->strm->avail_in;      /* number of input bytes directly copied */
+    if (used) {
+        /* If any input was used, then no unused input remains in the window,
+         * therefore s->block_start == s->strstart.
+         */
+        if (used >= s->w_size) {    /* supplant the previous history */
+            s->matches = 2;         /* clear hash */
+            zmemcpy(s->window, s->strm->next_in - s->w_size, s->w_size);
+            s->strstart = s->w_size;
+        }
+        else {
+            if (s->window_size - s->strstart <= used) {
+                /* Slide the window down. */
+                s->strstart -= s->w_size;
+                zmemcpy(s->window, s->window + s->w_size, s->strstart);
+                if (s->matches < 2)
+                    s->matches++;   /* add a pending slide_hash() */
+            }
+            zmemcpy(s->window + s->strstart, s->strm->next_in - used, used);
+            s->strstart += used;
+        }
+        s->block_start = s->strstart;
+        s->insert += MIN(used, s->w_size - s->insert);
+    }
+    if (s->high_water < s->strstart)
+        s->high_water = s->strstart;
+
+    /* If the last block was written to next_out, then done. */
+    if (last)
+        return finish_done;
+
+    /* If flushing and all input has been consumed, then done. */
+    if (flush != Z_NO_FLUSH && flush != Z_FINISH &&
+        s->strm->avail_in == 0 && (long)s->strstart == s->block_start)
+        return block_done;
+
+    /* Fill the window with any remaining input. */
+    have = s->window_size - s->strstart - 1;
+    if (s->strm->avail_in > have && s->block_start >= (long)s->w_size) {
+        /* Slide the window down. */
+        s->block_start -= s->w_size;
+        s->strstart -= s->w_size;
+        zmemcpy(s->window, s->window + s->w_size, s->strstart);
+        if (s->matches < 2)
+            s->matches++;           /* add a pending slide_hash() */
+        have += s->w_size;          /* more space now */
+    }
+    if (have > s->strm->avail_in)
+        have = s->strm->avail_in;
+    if (have) {
+        read_buf(s->strm, s->window + s->strstart, have);
+        s->strstart += have;
+    }
+    if (s->high_water < s->strstart)
+        s->high_water = s->strstart;
+
+    /* There was not enough avail_out to write a complete worthy or flushed
+     * stored block to next_out. Write a stored block to pending instead, if we
+     * have enough input for a worthy block, or if flushing and there is enough
+     * room for the remaining input as a stored block in the pending buffer.
+     */
+    have = (s->bi_valid + 42) >> 3;         /* number of header bytes */
+        /* maximum stored block length that will fit in pending: */
+    have = MIN(s->pending_buf_size - have, MAX_STORED);
+    min_block = MIN(have, s->w_size);
+    left = s->strstart - s->block_start;
+    if (left >= min_block ||
+        ((left || flush == Z_FINISH) && flush != Z_NO_FLUSH &&
+         s->strm->avail_in == 0 && left <= have)) {
+        len = MIN(left, have);
+        last = flush == Z_FINISH && s->strm->avail_in == 0 &&
+               len == left ? 1 : 0;
+        _tr_stored_block(s, (charf *)s->window + s->block_start, len, last);
+        s->block_start += len;
+        flush_pending(s->strm);
+    }
+
+    /* We've done all we can with the available input and output. */
+    return last ? finish_started : need_more;
+}
+
+/* ===========================================================================
+ * Compress as much as possible from the input stream, return the current
+ * block state.
+ * This function does not perform lazy evaluation of matches and inserts
+ * new strings in the dictionary only for unmatched strings or for short
+ * matches. It is used only for the fast compression options.
+ */
+local block_state deflate_fast(s, flush)
+    deflate_state *s;
+    int flush;
+{
+    IPos hash_head;       /* head of the hash chain */
+    int bflush;           /* set if current block must be flushed */
+
+    for (;;) {
+        /* Make sure that we always have enough lookahead, except
+         * at the end of the input file. We need MAX_MATCH bytes
+         * for the next match, plus MIN_MATCH bytes to insert the
+         * string following the next match.
+         */
+        if (s->lookahead < MIN_LOOKAHEAD) {
+            fill_window(s);
+            if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+                return need_more;
+            }
+            if (s->lookahead == 0) break; /* flush the current block */
+        }
+
+        /* Insert the string window[strstart .. strstart+2] in the
+         * dictionary, and set hash_head to the head of the hash chain:
+         */
+        hash_head = NIL;
+        if (s->lookahead >= MIN_MATCH) {
+            INSERT_STRING(s, s->strstart, hash_head);
+        }
+
+        /* Find the longest match, discarding those <= prev_length.
+         * At this point we have always match_length < MIN_MATCH
+         */
+        if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
+            /* To simplify the code, we prevent matches with the string
+             * of window index 0 (in particular we have to avoid a match
+             * of the string with itself at the start of the input file).
+             */
+            s->match_length = longest_match (s, hash_head);
+            /* longest_match() sets match_start */
+        }
+        if (s->match_length >= MIN_MATCH) {
+            check_match(s, s->strstart, s->match_start, s->match_length);
+
+            _tr_tally_dist(s, s->strstart - s->match_start,
+                           s->match_length - MIN_MATCH, bflush);
+
+            s->lookahead -= s->match_length;
+
+            /* Insert new strings in the hash table only if the match length
+             * is not too large. This saves time but degrades compression.
+             */
+#ifndef FASTEST
+            if (s->match_length <= s->max_insert_length &&
+                s->lookahead >= MIN_MATCH) {
+                s->match_length--; /* string at strstart already in table */
+                do {
+                    s->strstart++;
+                    INSERT_STRING(s, s->strstart, hash_head);
+                    /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+                     * always MIN_MATCH bytes ahead.
+                     */
+                } while (--s->match_length != 0);
+                s->strstart++;
+            } else
+#endif
+            {
+                s->strstart += s->match_length;
+                s->match_length = 0;
+                s->ins_h = s->window[s->strstart];
+                UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+                Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+                /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
+                 * matter since it will be recomputed at next deflate call.
+                 */
+            }
+        } else {
+            /* No match, output a literal byte */
+            Tracevv((stderr,"%c", s->window[s->strstart]));
+            _tr_tally_lit (s, s->window[s->strstart], bflush);
+            s->lookahead--;
+            s->strstart++;
+        }
+        if (bflush) FLUSH_BLOCK(s, 0);
+    }
+    s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1;
+    if (flush == Z_FINISH) {
+        FLUSH_BLOCK(s, 1);
+        return finish_done;
+    }
+    if (s->last_lit)
+        FLUSH_BLOCK(s, 0);
+    return block_done;
+}
+
+#ifndef FASTEST
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+local block_state deflate_slow(s, flush)
+    deflate_state *s;
+    int flush;
+{
+    IPos hash_head;          /* head of hash chain */
+    int bflush;              /* set if current block must be flushed */
+
+    /* Process the input block. */
+    for (;;) {
+        /* Make sure that we always have enough lookahead, except
+         * at the end of the input file. We need MAX_MATCH bytes
+         * for the next match, plus MIN_MATCH bytes to insert the
+         * string following the next match.
+         */
+        if (s->lookahead < MIN_LOOKAHEAD) {
+            fill_window(s);
+            if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+                return need_more;
+            }
+            if (s->lookahead == 0) break; /* flush the current block */
+        }
+
+        /* Insert the string window[strstart .. strstart+2] in the
+         * dictionary, and set hash_head to the head of the hash chain:
+         */
+        hash_head = NIL;
+        if (s->lookahead >= MIN_MATCH) {
+            INSERT_STRING(s, s->strstart, hash_head);
+        }
+
+        /* Find the longest match, discarding those <= prev_length.
+         */
+        s->prev_length = s->match_length, s->prev_match = s->match_start;
+        s->match_length = MIN_MATCH-1;
+
+        if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
+            s->strstart - hash_head <= MAX_DIST(s)) {
+            /* To simplify the code, we prevent matches with the string
+             * of window index 0 (in particular we have to avoid a match
+             * of the string with itself at the start of the input file).
+             */
+            s->match_length = longest_match (s, hash_head);
+            /* longest_match() sets match_start */
+
+            if (s->match_length <= 5 && (s->strategy == Z_FILTERED
+#if TOO_FAR <= 32767
+                || (s->match_length == MIN_MATCH &&
+                    s->strstart - s->match_start > TOO_FAR)
+#endif
+                )) {
+
+                /* If prev_match is also MIN_MATCH, match_start is garbage
+                 * but we will ignore the current match anyway.
+                 */
+                s->match_length = MIN_MATCH-1;
+            }
+        }
+        /* If there was a match at the previous step and the current
+         * match is not better, output the previous match:
+         */
+        if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
+            uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
+            /* Do not insert strings in hash table beyond this. */
+
+            check_match(s, s->strstart-1, s->prev_match, s->prev_length);
+
+            _tr_tally_dist(s, s->strstart -1 - s->prev_match,
+                           s->prev_length - MIN_MATCH, bflush);
+
+            /* Insert in hash table all strings up to the end of the match.
+             * strstart-1 and strstart are already inserted. If there is not
+             * enough lookahead, the last two strings are not inserted in
+             * the hash table.
+             */
+            s->lookahead -= s->prev_length-1;
+            s->prev_length -= 2;
+            do {
+                if (++s->strstart <= max_insert) {
+                    INSERT_STRING(s, s->strstart, hash_head);
+                }
+            } while (--s->prev_length != 0);
+            s->match_available = 0;
+            s->match_length = MIN_MATCH-1;
+            s->strstart++;
+
+            if (bflush) FLUSH_BLOCK(s, 0);
+
+        } else if (s->match_available) {
+            /* If there was no match at the previous position, output a
+             * single literal. If there was a match but the current match
+             * is longer, truncate the previous match to a single literal.
+             */
+            Tracevv((stderr,"%c", s->window[s->strstart-1]));
+            _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+            if (bflush) {
+                FLUSH_BLOCK_ONLY(s, 0);
+            }
+            s->strstart++;
+            s->lookahead--;
+            if (s->strm->avail_out == 0) return need_more;
+        } else {
+            /* There is no previous match to compare with, wait for
+             * the next step to decide.
+             */
+            s->match_available = 1;
+            s->strstart++;
+            s->lookahead--;
+        }
+    }
+    Assert (flush != Z_NO_FLUSH, "no flush?");
+    if (s->match_available) {
+        Tracevv((stderr,"%c", s->window[s->strstart-1]));
+        _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+        s->match_available = 0;
+    }
+    s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1;
+    if (flush == Z_FINISH) {
+        FLUSH_BLOCK(s, 1);
+        return finish_done;
+    }
+    if (s->last_lit)
+        FLUSH_BLOCK(s, 0);
+    return block_done;
+}
+#endif /* FASTEST */
+
+/* ===========================================================================
+ * For Z_RLE, simply look for runs of bytes, generate matches only of distance
+ * one.  Do not maintain a hash table.  (It will be regenerated if this run of
+ * deflate switches away from Z_RLE.)
+ */
+local block_state deflate_rle(s, flush)
+    deflate_state *s;
+    int flush;
+{
+    int bflush;             /* set if current block must be flushed */
+    uInt prev;              /* byte at distance one to match */
+    Bytef *scan, *strend;   /* scan goes up to strend for length of run */
+
+    for (;;) {
+        /* Make sure that we always have enough lookahead, except
+         * at the end of the input file. We need MAX_MATCH bytes
+         * for the longest run, plus one for the unrolled loop.
+         */
+        if (s->lookahead <= MAX_MATCH) {
+            fill_window(s);
+            if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) {
+                return need_more;
+            }
+            if (s->lookahead == 0) break; /* flush the current block */
+        }
+
+        /* See how many times the previous byte repeats */
+        s->match_length = 0;
+        if (s->lookahead >= MIN_MATCH && s->strstart > 0) {
+            scan = s->window + s->strstart - 1;
+            prev = *scan;
+            if (prev == *++scan && prev == *++scan && prev == *++scan) {
+                strend = s->window + s->strstart + MAX_MATCH;
+                do {
+                } while (prev == *++scan && prev == *++scan &&
+                         prev == *++scan && prev == *++scan &&
+                         prev == *++scan && prev == *++scan &&
+                         prev == *++scan && prev == *++scan &&
+                         scan < strend);
+                s->match_length = MAX_MATCH - (uInt)(strend - scan);
+                if (s->match_length > s->lookahead)
+                    s->match_length = s->lookahead;
+            }
+            Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan");
+        }
+
+        /* Emit match if have run of MIN_MATCH or longer, else emit literal */
+        if (s->match_length >= MIN_MATCH) {
+            check_match(s, s->strstart, s->strstart - 1, s->match_length);
+
+            _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush);
+
+            s->lookahead -= s->match_length;
+            s->strstart += s->match_length;
+            s->match_length = 0;
+        } else {
+            /* No match, output a literal byte */
+            Tracevv((stderr,"%c", s->window[s->strstart]));
+            _tr_tally_lit (s, s->window[s->strstart], bflush);
+            s->lookahead--;
+            s->strstart++;
+        }
+        if (bflush) FLUSH_BLOCK(s, 0);
+    }
+    s->insert = 0;
+    if (flush == Z_FINISH) {
+        FLUSH_BLOCK(s, 1);
+        return finish_done;
+    }
+    if (s->last_lit)
+        FLUSH_BLOCK(s, 0);
+    return block_done;
+}
+
+/* ===========================================================================
+ * For Z_HUFFMAN_ONLY, do not look for matches.  Do not maintain a hash table.
+ * (It will be regenerated if this run of deflate switches away from Huffman.)
+ */
+local block_state deflate_huff(s, flush)
+    deflate_state *s;
+    int flush;
+{
+    int bflush;             /* set if current block must be flushed */
+
+    for (;;) {
+        /* Make sure that we have a literal to write. */
+        if (s->lookahead == 0) {
+            fill_window(s);
+            if (s->lookahead == 0) {
+                if (flush == Z_NO_FLUSH)
+                    return need_more;
+                break;      /* flush the current block */
+            }
+        }
+
+        /* Output a literal byte */
+        s->match_length = 0;
+        Tracevv((stderr,"%c", s->window[s->strstart]));
+        _tr_tally_lit (s, s->window[s->strstart], bflush);
+        s->lookahead--;
+        s->strstart++;
+        if (bflush) FLUSH_BLOCK(s, 0);
+    }
+    s->insert = 0;
+    if (flush == Z_FINISH) {
+        FLUSH_BLOCK(s, 1);
+        return finish_done;
+    }
+    if (s->last_lit)
+        FLUSH_BLOCK(s, 0);
+    return block_done;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/deflate.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,349 @@
+/* deflate.h -- internal compression state
+ * Copyright (C) 1995-2016 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id$ */
+
+#ifndef DEFLATE_H
+#define DEFLATE_H
+
+#include "zutil.h"
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+   trailer creation by deflate().  NO_GZIP would be used to avoid linking in
+   the crc code when it is not needed.  For shared libraries, gzip encoding
+   should be left enabled. */
+#ifndef NO_GZIP
+#  define GZIP
+#endif
+
+/* ===========================================================================
+ * Internal compression state.
+ */
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS  256
+/* number of literal bytes 0..255 */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES   30
+/* number of distance codes */
+
+#define BL_CODES  19
+/* number of codes used to transfer the bit lengths */
+
+#define HEAP_SIZE (2*L_CODES+1)
+/* maximum heap size */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define Buf_size 16
+/* size of bit buffer in bi_buf */
+
+#define INIT_STATE    42    /* zlib header -> BUSY_STATE */
+#ifdef GZIP
+#  define GZIP_STATE  57    /* gzip header -> BUSY_STATE | EXTRA_STATE */
+#endif
+#define EXTRA_STATE   69    /* gzip extra block -> NAME_STATE */
+#define NAME_STATE    73    /* gzip file name -> COMMENT_STATE */
+#define COMMENT_STATE 91    /* gzip comment -> HCRC_STATE */
+#define HCRC_STATE   103    /* gzip header CRC -> BUSY_STATE */
+#define BUSY_STATE   113    /* deflate -> FINISH_STATE */
+#define FINISH_STATE 666    /* stream complete */
+/* Stream status */
+
+
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data_s {
+    union {
+        ush  freq;       /* frequency count */
+        ush  code;       /* bit string */
+    } fc;
+    union {
+        ush  dad;        /* father node in Huffman tree */
+        ush  len;        /* length of bit string */
+    } dl;
+} FAR ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad  dl.dad
+#define Len  dl.len
+
+typedef struct static_tree_desc_s  static_tree_desc;
+
+typedef struct tree_desc_s {
+    ct_data *dyn_tree;           /* the dynamic tree */
+    int     max_code;            /* largest code with non zero frequency */
+    const static_tree_desc *stat_desc;  /* the corresponding static tree */
+} FAR tree_desc;
+
+typedef ush Pos;
+typedef Pos FAR Posf;
+typedef unsigned IPos;
+
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+typedef struct internal_state {
+    z_streamp strm;      /* pointer back to this zlib stream */
+    int   status;        /* as the name implies */
+    Bytef *pending_buf;  /* output still pending */
+    ulg   pending_buf_size; /* size of pending_buf */
+    Bytef *pending_out;  /* next pending byte to output to the stream */
+    ulg   pending;       /* nb of bytes in the pending buffer */
+    int   wrap;          /* bit 0 true for zlib, bit 1 true for gzip */
+    gz_headerp  gzhead;  /* gzip header information to write */
+    ulg   gzindex;       /* where in extra, name, or comment */
+    Byte  method;        /* can only be DEFLATED */
+    int   last_flush;    /* value of flush param for previous deflate call */
+
+                /* used by deflate.c: */
+
+    uInt  w_size;        /* LZ77 window size (32K by default) */
+    uInt  w_bits;        /* log2(w_size)  (8..16) */
+    uInt  w_mask;        /* w_size - 1 */
+
+    Bytef *window;
+    /* Sliding window. Input bytes are read into the second half of the window,
+     * and move to the first half later to keep a dictionary of at least wSize
+     * bytes. With this organization, matches are limited to a distance of
+     * wSize-MAX_MATCH bytes, but this ensures that IO is always
+     * performed with a length multiple of the block size. Also, it limits
+     * the window size to 64K, which is quite useful on MSDOS.
+     * To do: use the user input buffer as sliding window.
+     */
+
+    ulg window_size;
+    /* Actual size of window: 2*wSize, except when the user input buffer
+     * is directly used as sliding window.
+     */
+
+    Posf *prev;
+    /* Link to older string with same hash index. To limit the size of this
+     * array to 64K, this link is maintained only for the last 32K strings.
+     * An index in this array is thus a window index modulo 32K.
+     */
+
+    Posf *head; /* Heads of the hash chains or NIL. */
+
+    uInt  ins_h;          /* hash index of string to be inserted */
+    uInt  hash_size;      /* number of elements in hash table */
+    uInt  hash_bits;      /* log2(hash_size) */
+    uInt  hash_mask;      /* hash_size-1 */
+
+    uInt  hash_shift;
+    /* Number of bits by which ins_h must be shifted at each input
+     * step. It must be such that after MIN_MATCH steps, the oldest
+     * byte no longer takes part in the hash key, that is:
+     *   hash_shift * MIN_MATCH >= hash_bits
+     */
+
+    long block_start;
+    /* Window position at the beginning of the current output block. Gets
+     * negative when the window is moved backwards.
+     */
+
+    uInt match_length;           /* length of best match */
+    IPos prev_match;             /* previous match */
+    int match_available;         /* set if previous match exists */
+    uInt strstart;               /* start of string to insert */
+    uInt match_start;            /* start of matching string */
+    uInt lookahead;              /* number of valid bytes ahead in window */
+
+    uInt prev_length;
+    /* Length of the best match at previous step. Matches not greater than this
+     * are discarded. This is used in the lazy match evaluation.
+     */
+
+    uInt max_chain_length;
+    /* To speed up deflation, hash chains are never searched beyond this
+     * length.  A higher limit improves compression ratio but degrades the
+     * speed.
+     */
+
+    uInt max_lazy_match;
+    /* Attempt to find a better match only when the current match is strictly
+     * smaller than this value. This mechanism is used only for compression
+     * levels >= 4.
+     */
+#   define max_insert_length  max_lazy_match
+    /* Insert new strings in the hash table only if the match length is not
+     * greater than this length. This saves time but degrades compression.
+     * max_insert_length is used only for compression levels <= 3.
+     */
+
+    int level;    /* compression level (1..9) */
+    int strategy; /* favor or force Huffman coding*/
+
+    uInt good_match;
+    /* Use a faster search when the previous match is longer than this */
+
+    int nice_match; /* Stop searching when current match exceeds this */
+
+                /* used by trees.c: */
+    /* Didn't use ct_data typedef below to suppress compiler warning */
+    struct ct_data_s dyn_ltree[HEAP_SIZE];   /* literal and length tree */
+    struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
+    struct ct_data_s bl_tree[2*BL_CODES+1];  /* Huffman tree for bit lengths */
+
+    struct tree_desc_s l_desc;               /* desc. for literal tree */
+    struct tree_desc_s d_desc;               /* desc. for distance tree */
+    struct tree_desc_s bl_desc;              /* desc. for bit length tree */
+
+    ush bl_count[MAX_BITS+1];
+    /* number of codes at each bit length for an optimal tree */
+
+    int heap[2*L_CODES+1];      /* heap used to build the Huffman trees */
+    int heap_len;               /* number of elements in the heap */
+    int heap_max;               /* element of largest frequency */
+    /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+     * The same heap array is used to build all trees.
+     */
+
+    uch depth[2*L_CODES+1];
+    /* Depth of each subtree used as tie breaker for trees of equal frequency
+     */
+
+    uchf *l_buf;          /* buffer for literals or lengths */
+
+    uInt  lit_bufsize;
+    /* Size of match buffer for literals/lengths.  There are 4 reasons for
+     * limiting lit_bufsize to 64K:
+     *   - frequencies can be kept in 16 bit counters
+     *   - if compression is not successful for the first block, all input
+     *     data is still in the window so we can still emit a stored block even
+     *     when input comes from standard input.  (This can also be done for
+     *     all blocks if lit_bufsize is not greater than 32K.)
+     *   - if compression is not successful for a file smaller than 64K, we can
+     *     even emit a stored file instead of a stored block (saving 5 bytes).
+     *     This is applicable only for zip (not gzip or zlib).
+     *   - creating new Huffman trees less frequently may not provide fast
+     *     adaptation to changes in the input data statistics. (Take for
+     *     example a binary file with poorly compressible code followed by
+     *     a highly compressible string table.) Smaller buffer sizes give
+     *     fast adaptation but have of course the overhead of transmitting
+     *     trees more frequently.
+     *   - I can't count above 4
+     */
+
+    uInt last_lit;      /* running index in l_buf */
+
+    ushf *d_buf;
+    /* Buffer for distances. To simplify the code, d_buf and l_buf have
+     * the same number of elements. To use different lengths, an extra flag
+     * array would be necessary.
+     */
+
+    ulg opt_len;        /* bit length of current block with optimal trees */
+    ulg static_len;     /* bit length of current block with static trees */
+    uInt matches;       /* number of string matches in current block */
+    uInt insert;        /* bytes at end of window left to insert */
+
+#ifdef ZLIB_DEBUG
+    ulg compressed_len; /* total bit length of compressed file mod 2^32 */
+    ulg bits_sent;      /* bit length of compressed data sent mod 2^32 */
+#endif
+
+    ush bi_buf;
+    /* Output buffer. bits are inserted starting at the bottom (least
+     * significant bits).
+     */
+    int bi_valid;
+    /* Number of valid bits in bi_buf.  All bits above the last valid bit
+     * are always zero.
+     */
+
+    ulg high_water;
+    /* High water mark offset in window for initialized bytes -- bytes above
+     * this are set to zero in order to avoid memory check warnings when
+     * longest match routines access bytes past the input.  This is then
+     * updated to the new high water mark.
+     */
+
+} FAR deflate_state;
+
+/* Output a byte on the stream.
+ * IN assertion: there is enough room in pending_buf.
+ */
+#define put_byte(s, c) {s->pending_buf[s->pending++] = (Bytef)(c);}
+
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST(s)  ((s)->w_size-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+#define WIN_INIT MAX_MATCH
+/* Number of bytes after end of data in window to initialize in order to avoid
+   memory checker errors from longest match routines */
+
+        /* in trees.c */
+void ZLIB_INTERNAL _tr_init OF((deflate_state *s));
+int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
+void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf,
+                        ulg stored_len, int last));
+void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s));
+void ZLIB_INTERNAL _tr_align OF((deflate_state *s));
+void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf,
+                        ulg stored_len, int last));
+
+#define d_code(dist) \
+   ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. _dist_code[256] and _dist_code[257] are never
+ * used.
+ */
+
+#ifndef ZLIB_DEBUG
+/* Inline versions of _tr_tally for speed: */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+  extern uch ZLIB_INTERNAL _length_code[];
+  extern uch ZLIB_INTERNAL _dist_code[];
+#else
+  extern const uch ZLIB_INTERNAL _length_code[];
+  extern const uch ZLIB_INTERNAL _dist_code[];
+#endif
+
+# define _tr_tally_lit(s, c, flush) \
+  { uch cc = (c); \
+    s->d_buf[s->last_lit] = 0; \
+    s->l_buf[s->last_lit++] = cc; \
+    s->dyn_ltree[cc].Freq++; \
+    flush = (s->last_lit == s->lit_bufsize-1); \
+   }
+# define _tr_tally_dist(s, distance, length, flush) \
+  { uch len = (uch)(length); \
+    ush dist = (ush)(distance); \
+    s->d_buf[s->last_lit] = dist; \
+    s->l_buf[s->last_lit++] = len; \
+    dist--; \
+    s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
+    s->dyn_dtree[d_code(dist)].Freq++; \
+    flush = (s->last_lit == s->lit_bufsize-1); \
+  }
+#else
+# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
+# define _tr_tally_dist(s, distance, length, flush) \
+              flush = _tr_tally(s, distance, length)
+#endif
+
+#endif /* DEFLATE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/gzclose.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,25 @@
+/* gzclose.c -- zlib gzclose() function
+ * Copyright (C) 2004, 2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "gzguts.h"
+
+/* gzclose() is in a separate file so that it is linked in only if it is used.
+   That way the other gzclose functions can be used instead to avoid linking in
+   unneeded compression or decompression routines. */
+int ZEXPORT gzclose(file)
+    gzFile file;
+{
+#ifndef NO_GZCOMPRESS
+    gz_statep state;
+
+    if (file == NULL)
+        return Z_STREAM_ERROR;
+    state = (gz_statep)file;
+
+    return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file);
+#else
+    return gzclose_r(file);
+#endif
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/gzguts.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,218 @@
+/* gzguts.h -- zlib internal header definitions for gz* operations
+ * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#ifdef _LARGEFILE64_SOURCE
+#  ifndef _LARGEFILE_SOURCE
+#    define _LARGEFILE_SOURCE 1
+#  endif
+#  ifdef _FILE_OFFSET_BITS
+#    undef _FILE_OFFSET_BITS
+#  endif
+#endif
+
+#ifdef HAVE_HIDDEN
+#  define ZLIB_INTERNAL __attribute__((visibility ("hidden")))
+#else
+#  define ZLIB_INTERNAL
+#endif
+
+#include <stdio.h>
+#include "zlib.h"
+#ifdef STDC
+#  include <string.h>
+#  include <stdlib.h>
+#  include <limits.h>
+#endif
+
+#ifndef _POSIX_SOURCE
+#  define _POSIX_SOURCE
+#endif
+#include <fcntl.h>
+
+#ifdef _WIN32
+#  include <stddef.h>
+#endif
+
+#if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32)
+#  include <io.h>
+#endif
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+#  define WIDECHAR
+#endif
+
+#ifdef WINAPI_FAMILY
+#  define open _open
+#  define read _read
+#  define write _write
+#  define close _close
+#endif
+
+#ifdef NO_DEFLATE       /* for compatibility with old definition */
+#  define NO_GZCOMPRESS
+#endif
+
+#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550)
+#  ifndef HAVE_VSNPRINTF
+#    define HAVE_VSNPRINTF
+#  endif
+#endif
+
+#if defined(__CYGWIN__)
+#  ifndef HAVE_VSNPRINTF
+#    define HAVE_VSNPRINTF
+#  endif
+#endif
+
+#if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410)
+#  ifndef HAVE_VSNPRINTF
+#    define HAVE_VSNPRINTF
+#  endif
+#endif
+
+#ifndef HAVE_VSNPRINTF
+#  ifdef MSDOS
+/* vsnprintf may exist on some MS-DOS compilers (DJGPP?),
+   but for now we just assume it doesn't. */
+#    define NO_vsnprintf
+#  endif
+#  ifdef __TURBOC__
+#    define NO_vsnprintf
+#  endif
+#  ifdef WIN32
+/* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */
+#    if !defined(vsnprintf) && !defined(NO_vsnprintf)
+#      if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 )
+#         define vsnprintf _vsnprintf
+#      endif
+#    endif
+#  endif
+#  ifdef __SASC
+#    define NO_vsnprintf
+#  endif
+#  ifdef VMS
+#    define NO_vsnprintf
+#  endif
+#  ifdef __OS400__
+#    define NO_vsnprintf
+#  endif
+#  ifdef __MVS__
+#    define NO_vsnprintf
+#  endif
+#endif
+
+/* unlike snprintf (which is required in C99), _snprintf does not guarantee
+   null termination of the result -- however this is only used in gzlib.c where
+   the result is assured to fit in the space provided */
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#  define snprintf _snprintf
+#endif
+
+#ifndef local
+#  define local static
+#endif
+/* since "static" is used to mean two completely different things in C, we
+   define "local" for the non-static meaning of "static", for readability
+   (compile with -Dlocal if your debugger can't find static symbols) */
+
+/* gz* functions always use library allocation functions */
+#ifndef STDC
+  extern voidp  malloc OF((uInt size));
+  extern void   free   OF((voidpf ptr));
+#endif
+
+/* get errno and strerror definition */
+#if defined UNDER_CE
+#  include <windows.h>
+#  define zstrerror() gz_strwinerror((DWORD)GetLastError())
+#else
+#  ifndef NO_STRERROR
+#    include <errno.h>
+#    define zstrerror() strerror(errno)
+#  else
+#    define zstrerror() "stdio error (consult errno)"
+#  endif
+#endif
+
+/* provide prototypes for these when building zlib without LFS */
+#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0
+    ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
+    ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
+    ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
+    ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));
+#endif
+
+/* default memLevel */
+#if MAX_MEM_LEVEL >= 8
+#  define DEF_MEM_LEVEL 8
+#else
+#  define DEF_MEM_LEVEL  MAX_MEM_LEVEL
+#endif
+
+/* default i/o buffer size -- double this for output when reading (this and
+   twice this must be able to fit in an unsigned type) */
+#define GZBUFSIZE 8192
+
+/* gzip modes, also provide a little integrity check on the passed structure */
+#define GZ_NONE 0
+#define GZ_READ 7247
+#define GZ_WRITE 31153
+#define GZ_APPEND 1     /* mode set to GZ_WRITE after the file is opened */
+
+/* values for gz_state how */
+#define LOOK 0      /* look for a gzip header */
+#define COPY 1      /* copy input directly */
+#define GZIP 2      /* decompress a gzip stream */
+
+/* internal gzip file state data structure */
+typedef struct {
+        /* exposed contents for gzgetc() macro */
+    struct gzFile_s x;      /* "x" for exposed */
+                            /* x.have: number of bytes available at x.next */
+                            /* x.next: next output data to deliver or write */
+                            /* x.pos: current position in uncompressed data */
+        /* used for both reading and writing */
+    int mode;               /* see gzip modes above */
+    int fd;                 /* file descriptor */
+    char *path;             /* path or fd for error messages */
+    unsigned size;          /* buffer size, zero if not allocated yet */
+    unsigned want;          /* requested buffer size, default is GZBUFSIZE */
+    unsigned char *in;      /* input buffer (double-sized when writing) */
+    unsigned char *out;     /* output buffer (double-sized when reading) */
+    int direct;             /* 0 if processing gzip, 1 if transparent */
+        /* just for reading */
+    int how;                /* 0: get header, 1: copy, 2: decompress */
+    z_off64_t start;        /* where the gzip data started, for rewinding */
+    int eof;                /* true if end of input file reached */
+    int past;               /* true if read requested past end */
+        /* just for writing */
+    int level;              /* compression level */
+    int strategy;           /* compression strategy */
+        /* seek request */
+    z_off64_t skip;         /* amount to skip (already rewound if backwards) */
+    int seek;               /* true if seek request pending */
+        /* error information */
+    int err;                /* error code */
+    char *msg;              /* error message */
+        /* zlib inflate or deflate stream */
+    z_stream strm;          /* stream structure in-place (not a pointer) */
+} gz_state;
+typedef gz_state FAR *gz_statep;
+
+/* shared functions */
+void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *));
+#if defined UNDER_CE
+char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error));
+#endif
+
+/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t
+   value -- needed when comparing unsigned to z_off64_t, which is signed
+   (possible z_off64_t types off_t, off64_t, and long are all signed) */
+#ifdef INT_MAX
+#  define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX)
+#else
+unsigned ZLIB_INTERNAL gz_intmax OF((void));
+#  define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax())
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/gzlib.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,637 @@
+/* gzlib.c -- zlib functions common to reading and writing gzip files
+ * Copyright (C) 2004-2017 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "gzguts.h"
+
+#if defined(_WIN32) && !defined(__BORLANDC__) && !defined(__MINGW32__)
+#  define LSEEK _lseeki64
+#else
+#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0
+#  define LSEEK lseek64
+#else
+#  define LSEEK lseek
+#endif
+#endif
+
+/* Local functions */
+local void gz_reset OF((gz_statep));
+local gzFile gz_open OF((const void *, int, const char *));
+
+#if defined UNDER_CE
+
+/* Map the Windows error number in ERROR to a locale-dependent error message
+   string and return a pointer to it.  Typically, the values for ERROR come
+   from GetLastError.
+
+   The string pointed to shall not be modified by the application, but may be
+   overwritten by a subsequent call to gz_strwinerror
+
+   The gz_strwinerror function does not change the current setting of
+   GetLastError. */
+char ZLIB_INTERNAL *gz_strwinerror (error)
+     DWORD error;
+{
+    static char buf[1024];
+
+    wchar_t *msgbuf;
+    DWORD lasterr = GetLastError();
+    DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
+        | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+        NULL,
+        error,
+        0, /* Default language */
+        (LPVOID)&msgbuf,
+        0,
+        NULL);
+    if (chars != 0) {
+        /* If there is an \r\n appended, zap it.  */
+        if (chars >= 2
+            && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') {
+            chars -= 2;
+            msgbuf[chars] = 0;
+        }
+
+        if (chars > sizeof (buf) - 1) {
+            chars = sizeof (buf) - 1;
+            msgbuf[chars] = 0;
+        }
+
+        wcstombs(buf, msgbuf, chars + 1);
+        LocalFree(msgbuf);
+    }
+    else {
+        sprintf(buf, "unknown win32 error (%ld)", error);
+    }
+
+    SetLastError(lasterr);
+    return buf;
+}
+
+#endif /* UNDER_CE */
+
+/* Reset gzip file state */
+local void gz_reset(state)
+    gz_statep state;
+{
+    state->x.have = 0;              /* no output data available */
+    if (state->mode == GZ_READ) {   /* for reading ... */
+        state->eof = 0;             /* not at end of file */
+        state->past = 0;            /* have not read past end yet */
+        state->how = LOOK;          /* look for gzip header */
+    }
+    state->seek = 0;                /* no seek request pending */
+    gz_error(state, Z_OK, NULL);    /* clear error */
+    state->x.pos = 0;               /* no uncompressed data yet */
+    state->strm.avail_in = 0;       /* no input data yet */
+}
+
+/* Open a gzip file either by name or file descriptor. */
+local gzFile gz_open(path, fd, mode)
+    const void *path;
+    int fd;
+    const char *mode;
+{
+    gz_statep state;
+    z_size_t len;
+    int oflag;
+#ifdef O_CLOEXEC
+    int cloexec = 0;
+#endif
+#ifdef O_EXCL
+    int exclusive = 0;
+#endif
+
+    /* check input */
+    if (path == NULL)
+        return NULL;
+
+    /* allocate gzFile structure to return */
+    state = (gz_statep)malloc(sizeof(gz_state));
+    if (state == NULL)
+        return NULL;
+    state->size = 0;            /* no buffers allocated yet */
+    state->want = GZBUFSIZE;    /* requested buffer size */
+    state->msg = NULL;          /* no error message yet */
+
+    /* interpret mode */
+    state->mode = GZ_NONE;
+    state->level = Z_DEFAULT_COMPRESSION;
+    state->strategy = Z_DEFAULT_STRATEGY;
+    state->direct = 0;
+    while (*mode) {
+        if (*mode >= '0' && *mode <= '9')
+            state->level = *mode - '0';
+        else
+            switch (*mode) {
+            case 'r':
+                state->mode = GZ_READ;
+                break;
+#ifndef NO_GZCOMPRESS
+            case 'w':
+                state->mode = GZ_WRITE;
+                break;
+            case 'a':
+                state->mode = GZ_APPEND;
+                break;
+#endif
+            case '+':       /* can't read and write at the same time */
+                free(state);
+                return NULL;
+            case 'b':       /* ignore -- will request binary anyway */
+                break;
+#ifdef O_CLOEXEC
+            case 'e':
+                cloexec = 1;
+                break;
+#endif
+#ifdef O_EXCL
+            case 'x':
+                exclusive = 1;
+                break;
+#endif
+            case 'f':
+                state->strategy = Z_FILTERED;
+                break;
+            case 'h':
+                state->strategy = Z_HUFFMAN_ONLY;
+                break;
+            case 'R':
+                state->strategy = Z_RLE;
+                break;
+            case 'F':
+                state->strategy = Z_FIXED;
+                break;
+            case 'T':
+                state->direct = 1;
+                break;
+            default:        /* could consider as an error, but just ignore */
+                ;
+            }
+        mode++;
+    }
+
+    /* must provide an "r", "w", or "a" */
+    if (state->mode == GZ_NONE) {
+        free(state);
+        return NULL;
+    }
+
+    /* can't force transparent read */
+    if (state->mode == GZ_READ) {
+        if (state->direct) {
+            free(state);
+            return NULL;
+        }
+        state->direct = 1;      /* for empty file */
+    }
+
+    /* save the path name for error messages */
+#ifdef WIDECHAR
+    if (fd == -2) {
+        len = wcstombs(NULL, path, 0);
+        if (len == (z_size_t)-1)
+            len = 0;
+    }
+    else
+#endif
+        len = strlen((const char *)path);
+    state->path = (char *)malloc(len + 1);
+    if (state->path == NULL) {
+        free(state);
+        return NULL;
+    }
+#ifdef WIDECHAR
+    if (fd == -2)
+        if (len)
+            wcstombs(state->path, path, len + 1);
+        else
+            *(state->path) = 0;
+    else
+#endif
+#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
+        (void)snprintf(state->path, len + 1, "%s", (const char *)path);
+#else
+        strcpy(state->path, path);
+#endif
+
+    /* compute the flags for open() */
+    oflag =
+#ifdef O_LARGEFILE
+        O_LARGEFILE |
+#endif
+#ifdef O_BINARY
+        O_BINARY |
+#endif
+#ifdef O_CLOEXEC
+        (cloexec ? O_CLOEXEC : 0) |
+#endif
+        (state->mode == GZ_READ ?
+         O_RDONLY :
+         (O_WRONLY | O_CREAT |
+#ifdef O_EXCL
+          (exclusive ? O_EXCL : 0) |
+#endif
+          (state->mode == GZ_WRITE ?
+           O_TRUNC :
+           O_APPEND)));
+
+    /* open the file with the appropriate flags (or just use fd) */
+    state->fd = fd > -1 ? fd : (
+#ifdef WIDECHAR
+        fd == -2 ? _wopen(path, oflag, 0666) :
+#endif
+        open((const char *)path, oflag, 0666));
+    if (state->fd == -1) {
+        free(state->path);
+        free(state);
+        return NULL;
+    }
+    if (state->mode == GZ_APPEND) {
+        LSEEK(state->fd, 0, SEEK_END);  /* so gzoffset() is correct */
+        state->mode = GZ_WRITE;         /* simplify later checks */
+    }
+
+    /* save the current position for rewinding (only if reading) */
+    if (state->mode == GZ_READ) {
+        state->start = LSEEK(state->fd, 0, SEEK_CUR);
+        if (state->start == -1) state->start = 0;
+    }
+
+    /* initialize stream */
+    gz_reset(state);
+
+    /* return stream */
+    return (gzFile)state;
+}
+
+/* -- see zlib.h -- */
+gzFile ZEXPORT gzopen(path, mode)
+    const char *path;
+    const char *mode;
+{
+    return gz_open(path, -1, mode);
+}
+
+/* -- see zlib.h -- */
+gzFile ZEXPORT gzopen64(path, mode)
+    const char *path;
+    const char *mode;
+{
+    return gz_open(path, -1, mode);
+}
+
+/* -- see zlib.h -- */
+gzFile ZEXPORT gzdopen(fd, mode)
+    int fd;
+    const char *mode;
+{
+    char *path;         /* identifier for error messages */
+    gzFile gz;
+
+    if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL)
+        return NULL;
+#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
+    (void)snprintf(path, 7 + 3 * sizeof(int), "<fd:%d>", fd);
+#else
+    sprintf(path, "<fd:%d>", fd);   /* for debugging */
+#endif
+    gz = gz_open(path, fd, mode);
+    free(path);
+    return gz;
+}
+
+/* -- see zlib.h -- */
+#ifdef WIDECHAR
+gzFile ZEXPORT gzopen_w(path, mode)
+    const wchar_t *path;
+    const char *mode;
+{
+    return gz_open(path, -2, mode);
+}
+#endif
+
+/* -- see zlib.h -- */
+int ZEXPORT gzbuffer(file, size)
+    gzFile file;
+    unsigned size;
+{
+    gz_statep state;
+
+    /* get internal structure and check integrity */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
+        return -1;
+
+    /* make sure we haven't already allocated memory */
+    if (state->size != 0)
+        return -1;
+
+    /* check and set requested size */
+    if ((size << 1) < size)
+        return -1;              /* need to be able to double it */
+    if (size < 2)
+        size = 2;               /* need two bytes to check magic header */
+    state->want = size;
+    return 0;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzrewind(file)
+    gzFile file;
+{
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+
+    /* check that we're reading and that there's no error */
+    if (state->mode != GZ_READ ||
+            (state->err != Z_OK && state->err != Z_BUF_ERROR))
+        return -1;
+
+    /* back up and start over */
+    if (LSEEK(state->fd, state->start, SEEK_SET) == -1)
+        return -1;
+    gz_reset(state);
+    return 0;
+}
+
+/* -- see zlib.h -- */
+z_off64_t ZEXPORT gzseek64(file, offset, whence)
+    gzFile file;
+    z_off64_t offset;
+    int whence;
+{
+    unsigned n;
+    z_off64_t ret;
+    gz_statep state;
+
+    /* get internal structure and check integrity */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
+        return -1;
+
+    /* check that there's no error */
+    if (state->err != Z_OK && state->err != Z_BUF_ERROR)
+        return -1;
+
+    /* can only seek from start or relative to current position */
+    if (whence != SEEK_SET && whence != SEEK_CUR)
+        return -1;
+
+    /* normalize offset to a SEEK_CUR specification */
+    if (whence == SEEK_SET)
+        offset -= state->x.pos;
+    else if (state->seek)
+        offset += state->skip;
+    state->seek = 0;
+
+    /* if within raw area while reading, just go there */
+    if (state->mode == GZ_READ && state->how == COPY &&
+            state->x.pos + offset >= 0) {
+        ret = LSEEK(state->fd, offset - state->x.have, SEEK_CUR);
+        if (ret == -1)
+            return -1;
+        state->x.have = 0;
+        state->eof = 0;
+        state->past = 0;
+        state->seek = 0;
+        gz_error(state, Z_OK, NULL);
+        state->strm.avail_in = 0;
+        state->x.pos += offset;
+        return state->x.pos;
+    }
+
+    /* calculate skip amount, rewinding if needed for back seek when reading */
+    if (offset < 0) {
+        if (state->mode != GZ_READ)         /* writing -- can't go backwards */
+            return -1;
+        offset += state->x.pos;
+        if (offset < 0)                     /* before start of file! */
+            return -1;
+        if (gzrewind(file) == -1)           /* rewind, then skip to offset */
+            return -1;
+    }
+
+    /* if reading, skip what's in output buffer (one less gzgetc() check) */
+    if (state->mode == GZ_READ) {
+        n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > offset ?
+            (unsigned)offset : state->x.have;
+        state->x.have -= n;
+        state->x.next += n;
+        state->x.pos += n;
+        offset -= n;
+    }
+
+    /* request skip (if not zero) */
+    if (offset) {
+        state->seek = 1;
+        state->skip = offset;
+    }
+    return state->x.pos + offset;
+}
+
+/* -- see zlib.h -- */
+z_off_t ZEXPORT gzseek(file, offset, whence)
+    gzFile file;
+    z_off_t offset;
+    int whence;
+{
+    z_off64_t ret;
+
+    ret = gzseek64(file, (z_off64_t)offset, whence);
+    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
+}
+
+/* -- see zlib.h -- */
+z_off64_t ZEXPORT gztell64(file)
+    gzFile file;
+{
+    gz_statep state;
+
+    /* get internal structure and check integrity */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
+        return -1;
+
+    /* return position */
+    return state->x.pos + (state->seek ? state->skip : 0);
+}
+
+/* -- see zlib.h -- */
+z_off_t ZEXPORT gztell(file)
+    gzFile file;
+{
+    z_off64_t ret;
+
+    ret = gztell64(file);
+    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
+}
+
+/* -- see zlib.h -- */
+z_off64_t ZEXPORT gzoffset64(file)
+    gzFile file;
+{
+    z_off64_t offset;
+    gz_statep state;
+
+    /* get internal structure and check integrity */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
+        return -1;
+
+    /* compute and return effective offset in file */
+    offset = LSEEK(state->fd, 0, SEEK_CUR);
+    if (offset == -1)
+        return -1;
+    if (state->mode == GZ_READ)             /* reading */
+        offset -= state->strm.avail_in;     /* don't count buffered input */
+    return offset;
+}
+
+/* -- see zlib.h -- */
+z_off_t ZEXPORT gzoffset(file)
+    gzFile file;
+{
+    z_off64_t ret;
+
+    ret = gzoffset64(file);
+    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzeof(file)
+    gzFile file;
+{
+    gz_statep state;
+
+    /* get internal structure and check integrity */
+    if (file == NULL)
+        return 0;
+    state = (gz_statep)file;
+    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
+        return 0;
+
+    /* return end-of-file state */
+    return state->mode == GZ_READ ? state->past : 0;
+}
+
+/* -- see zlib.h -- */
+const char * ZEXPORT gzerror(file, errnum)
+    gzFile file;
+    int *errnum;
+{
+    gz_statep state;
+
+    /* get internal structure and check integrity */
+    if (file == NULL)
+        return NULL;
+    state = (gz_statep)file;
+    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
+        return NULL;
+
+    /* return error information */
+    if (errnum != NULL)
+        *errnum = state->err;
+    return state->err == Z_MEM_ERROR ? "out of memory" :
+                                       (state->msg == NULL ? "" : state->msg);
+}
+
+/* -- see zlib.h -- */
+void ZEXPORT gzclearerr(file)
+    gzFile file;
+{
+    gz_statep state;
+
+    /* get internal structure and check integrity */
+    if (file == NULL)
+        return;
+    state = (gz_statep)file;
+    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
+        return;
+
+    /* clear error and end-of-file */
+    if (state->mode == GZ_READ) {
+        state->eof = 0;
+        state->past = 0;
+    }
+    gz_error(state, Z_OK, NULL);
+}
+
+/* Create an error message in allocated memory and set state->err and
+   state->msg accordingly.  Free any previous error message already there.  Do
+   not try to free or allocate space if the error is Z_MEM_ERROR (out of
+   memory).  Simply save the error message as a static string.  If there is an
+   allocation failure constructing the error message, then convert the error to
+   out of memory. */
+void ZLIB_INTERNAL gz_error(state, err, msg)
+    gz_statep state;
+    int err;
+    const char *msg;
+{
+    /* free previously allocated message and clear */
+    if (state->msg != NULL) {
+        if (state->err != Z_MEM_ERROR)
+            free(state->msg);
+        state->msg = NULL;
+    }
+
+    /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */
+    if (err != Z_OK && err != Z_BUF_ERROR)
+        state->x.have = 0;
+
+    /* set error code, and if no message, then done */
+    state->err = err;
+    if (msg == NULL)
+        return;
+
+    /* for an out of memory error, return literal string when requested */
+    if (err == Z_MEM_ERROR)
+        return;
+
+    /* construct error message with path */
+    if ((state->msg = (char *)malloc(strlen(state->path) + strlen(msg) + 3)) ==
+            NULL) {
+        state->err = Z_MEM_ERROR;
+        return;
+    }
+#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
+    (void)snprintf(state->msg, strlen(state->path) + strlen(msg) + 3,
+                   "%s%s%s", state->path, ": ", msg);
+#else
+    strcpy(state->msg, state->path);
+    strcat(state->msg, ": ");
+    strcat(state->msg, msg);
+#endif
+}
+
+#ifndef INT_MAX
+/* portably return maximum value for an int (when limits.h presumed not
+   available) -- we need to do this to cover cases where 2's complement not
+   used, since C standard permits 1's complement and sign-bit representations,
+   otherwise we could just use ((unsigned)-1) >> 1 */
+unsigned ZLIB_INTERNAL gz_intmax()
+{
+    unsigned p, q;
+
+    p = 1;
+    do {
+        q = p;
+        p <<= 1;
+        p++;
+    } while (p > q);
+    return q >> 1;
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/gzread.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,654 @@
+/* gzread.c -- zlib functions for reading gzip files
+ * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "gzguts.h"
+
+/* Local functions */
+local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *));
+local int gz_avail OF((gz_statep));
+local int gz_look OF((gz_statep));
+local int gz_decomp OF((gz_statep));
+local int gz_fetch OF((gz_statep));
+local int gz_skip OF((gz_statep, z_off64_t));
+local z_size_t gz_read OF((gz_statep, voidp, z_size_t));
+
+/* Use read() to load a buffer -- return -1 on error, otherwise 0.  Read from
+   state->fd, and update state->eof, state->err, and state->msg as appropriate.
+   This function needs to loop on read(), since read() is not guaranteed to
+   read the number of bytes requested, depending on the type of descriptor. */
+local int gz_load(state, buf, len, have)
+    gz_statep state;
+    unsigned char *buf;
+    unsigned len;
+    unsigned *have;
+{
+    int ret;
+    unsigned get, max = ((unsigned)-1 >> 2) + 1;
+
+    *have = 0;
+    do {
+        get = len - *have;
+        if (get > max)
+            get = max;
+        ret = read(state->fd, buf + *have, get);
+        if (ret <= 0)
+            break;
+        *have += (unsigned)ret;
+    } while (*have < len);
+    if (ret < 0) {
+        gz_error(state, Z_ERRNO, zstrerror());
+        return -1;
+    }
+    if (ret == 0)
+        state->eof = 1;
+    return 0;
+}
+
+/* Load up input buffer and set eof flag if last data loaded -- return -1 on
+   error, 0 otherwise.  Note that the eof flag is set when the end of the input
+   file is reached, even though there may be unused data in the buffer.  Once
+   that data has been used, no more attempts will be made to read the file.
+   If strm->avail_in != 0, then the current data is moved to the beginning of
+   the input buffer, and then the remainder of the buffer is loaded with the
+   available data from the input file. */
+local int gz_avail(state)
+    gz_statep state;
+{
+    unsigned got;
+    z_streamp strm = &(state->strm);
+
+    if (state->err != Z_OK && state->err != Z_BUF_ERROR)
+        return -1;
+    if (state->eof == 0) {
+        if (strm->avail_in) {       /* copy what's there to the start */
+            unsigned char *p = state->in;
+            unsigned const char *q = strm->next_in;
+            unsigned n = strm->avail_in;
+            do {
+                *p++ = *q++;
+            } while (--n);
+        }
+        if (gz_load(state, state->in + strm->avail_in,
+                    state->size - strm->avail_in, &got) == -1)
+            return -1;
+        strm->avail_in += got;
+        strm->next_in = state->in;
+    }
+    return 0;
+}
+
+/* Look for gzip header, set up for inflate or copy.  state->x.have must be 0.
+   If this is the first time in, allocate required memory.  state->how will be
+   left unchanged if there is no more input data available, will be set to COPY
+   if there is no gzip header and direct copying will be performed, or it will
+   be set to GZIP for decompression.  If direct copying, then leftover input
+   data from the input buffer will be copied to the output buffer.  In that
+   case, all further file reads will be directly to either the output buffer or
+   a user buffer.  If decompressing, the inflate state will be initialized.
+   gz_look() will return 0 on success or -1 on failure. */
+local int gz_look(state)
+    gz_statep state;
+{
+    z_streamp strm = &(state->strm);
+
+    /* allocate read buffers and inflate memory */
+    if (state->size == 0) {
+        /* allocate buffers */
+        state->in = (unsigned char *)malloc(state->want);
+        state->out = (unsigned char *)malloc(state->want << 1);
+        if (state->in == NULL || state->out == NULL) {
+            free(state->out);
+            free(state->in);
+            gz_error(state, Z_MEM_ERROR, "out of memory");
+            return -1;
+        }
+        state->size = state->want;
+
+        /* allocate inflate memory */
+        state->strm.zalloc = Z_NULL;
+        state->strm.zfree = Z_NULL;
+        state->strm.opaque = Z_NULL;
+        state->strm.avail_in = 0;
+        state->strm.next_in = Z_NULL;
+        if (inflateInit2(&(state->strm), 15 + 16) != Z_OK) {    /* gunzip */
+            free(state->out);
+            free(state->in);
+            state->size = 0;
+            gz_error(state, Z_MEM_ERROR, "out of memory");
+            return -1;
+        }
+    }
+
+    /* get at least the magic bytes in the input buffer */
+    if (strm->avail_in < 2) {
+        if (gz_avail(state) == -1)
+            return -1;
+        if (strm->avail_in == 0)
+            return 0;
+    }
+
+    /* look for gzip magic bytes -- if there, do gzip decoding (note: there is
+       a logical dilemma here when considering the case of a partially written
+       gzip file, to wit, if a single 31 byte is written, then we cannot tell
+       whether this is a single-byte file, or just a partially written gzip
+       file -- for here we assume that if a gzip file is being written, then
+       the header will be written in a single operation, so that reading a
+       single byte is sufficient indication that it is not a gzip file) */
+    if (strm->avail_in > 1 &&
+            strm->next_in[0] == 31 && strm->next_in[1] == 139) {
+        inflateReset(strm);
+        state->how = GZIP;
+        state->direct = 0;
+        return 0;
+    }
+
+    /* no gzip header -- if we were decoding gzip before, then this is trailing
+       garbage.  Ignore the trailing garbage and finish. */
+    if (state->direct == 0) {
+        strm->avail_in = 0;
+        state->eof = 1;
+        state->x.have = 0;
+        return 0;
+    }
+
+    /* doing raw i/o, copy any leftover input to output -- this assumes that
+       the output buffer is larger than the input buffer, which also assures
+       space for gzungetc() */
+    state->x.next = state->out;
+    if (strm->avail_in) {
+        memcpy(state->x.next, strm->next_in, strm->avail_in);
+        state->x.have = strm->avail_in;
+        strm->avail_in = 0;
+    }
+    state->how = COPY;
+    state->direct = 1;
+    return 0;
+}
+
+/* Decompress from input to the provided next_out and avail_out in the state.
+   On return, state->x.have and state->x.next point to the just decompressed
+   data.  If the gzip stream completes, state->how is reset to LOOK to look for
+   the next gzip stream or raw data, once state->x.have is depleted.  Returns 0
+   on success, -1 on failure. */
+local int gz_decomp(state)
+    gz_statep state;
+{
+    int ret = Z_OK;
+    unsigned had;
+    z_streamp strm = &(state->strm);
+
+    /* fill output buffer up to end of deflate stream */
+    had = strm->avail_out;
+    do {
+        /* get more input for inflate() */
+        if (strm->avail_in == 0 && gz_avail(state) == -1)
+            return -1;
+        if (strm->avail_in == 0) {
+            gz_error(state, Z_BUF_ERROR, "unexpected end of file");
+            break;
+        }
+
+        /* decompress and handle errors */
+        ret = inflate(strm, Z_NO_FLUSH);
+        if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) {
+            gz_error(state, Z_STREAM_ERROR,
+                     "internal error: inflate stream corrupt");
+            return -1;
+        }
+        if (ret == Z_MEM_ERROR) {
+            gz_error(state, Z_MEM_ERROR, "out of memory");
+            return -1;
+        }
+        if (ret == Z_DATA_ERROR) {              /* deflate stream invalid */
+            gz_error(state, Z_DATA_ERROR,
+                     strm->msg == NULL ? "compressed data error" : strm->msg);
+            return -1;
+        }
+    } while (strm->avail_out && ret != Z_STREAM_END);
+
+    /* update available output */
+    state->x.have = had - strm->avail_out;
+    state->x.next = strm->next_out - state->x.have;
+
+    /* if the gzip stream completed successfully, look for another */
+    if (ret == Z_STREAM_END)
+        state->how = LOOK;
+
+    /* good decompression */
+    return 0;
+}
+
+/* Fetch data and put it in the output buffer.  Assumes state->x.have is 0.
+   Data is either copied from the input file or decompressed from the input
+   file depending on state->how.  If state->how is LOOK, then a gzip header is
+   looked for to determine whether to copy or decompress.  Returns -1 on error,
+   otherwise 0.  gz_fetch() will leave state->how as COPY or GZIP unless the
+   end of the input file has been reached and all data has been processed.  */
+local int gz_fetch(state)
+    gz_statep state;
+{
+    z_streamp strm = &(state->strm);
+
+    do {
+        switch(state->how) {
+        case LOOK:      /* -> LOOK, COPY (only if never GZIP), or GZIP */
+            if (gz_look(state) == -1)
+                return -1;
+            if (state->how == LOOK)
+                return 0;
+            break;
+        case COPY:      /* -> COPY */
+            if (gz_load(state, state->out, state->size << 1, &(state->x.have))
+                    == -1)
+                return -1;
+            state->x.next = state->out;
+            return 0;
+        case GZIP:      /* -> GZIP or LOOK (if end of gzip stream) */
+            strm->avail_out = state->size << 1;
+            strm->next_out = state->out;
+            if (gz_decomp(state) == -1)
+                return -1;
+        }
+    } while (state->x.have == 0 && (!state->eof || strm->avail_in));
+    return 0;
+}
+
+/* Skip len uncompressed bytes of output.  Return -1 on error, 0 on success. */
+local int gz_skip(state, len)
+    gz_statep state;
+    z_off64_t len;
+{
+    unsigned n;
+
+    /* skip over len bytes or reach end-of-file, whichever comes first */
+    while (len)
+        /* skip over whatever is in output buffer */
+        if (state->x.have) {
+            n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > len ?
+                (unsigned)len : state->x.have;
+            state->x.have -= n;
+            state->x.next += n;
+            state->x.pos += n;
+            len -= n;
+        }
+
+        /* output buffer empty -- return if we're at the end of the input */
+        else if (state->eof && state->strm.avail_in == 0)
+            break;
+
+        /* need more data to skip -- load up output buffer */
+        else {
+            /* get more output, looking for header if required */
+            if (gz_fetch(state) == -1)
+                return -1;
+        }
+    return 0;
+}
+
+/* Read len bytes into buf from file, or less than len up to the end of the
+   input.  Return the number of bytes read.  If zero is returned, either the
+   end of file was reached, or there was an error.  state->err must be
+   consulted in that case to determine which. */
+local z_size_t gz_read(state, buf, len)
+    gz_statep state;
+    voidp buf;
+    z_size_t len;
+{
+    z_size_t got;
+    unsigned n;
+
+    /* if len is zero, avoid unnecessary operations */
+    if (len == 0)
+        return 0;
+
+    /* process a skip request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_skip(state, state->skip) == -1)
+            return 0;
+    }
+
+    /* get len bytes to buf, or less than len if at the end */
+    got = 0;
+    do {
+        /* set n to the maximum amount of len that fits in an unsigned int */
+        n = -1;
+        if (n > len)
+            n = len;
+
+        /* first just try copying data from the output buffer */
+        if (state->x.have) {
+            if (state->x.have < n)
+                n = state->x.have;
+            memcpy(buf, state->x.next, n);
+            state->x.next += n;
+            state->x.have -= n;
+        }
+
+        /* output buffer empty -- return if we're at the end of the input */
+        else if (state->eof && state->strm.avail_in == 0) {
+            state->past = 1;        /* tried to read past end */
+            break;
+        }
+
+        /* need output data -- for small len or new stream load up our output
+           buffer */
+        else if (state->how == LOOK || n < (state->size << 1)) {
+            /* get more output, looking for header if required */
+            if (gz_fetch(state) == -1)
+                return 0;
+            continue;       /* no progress yet -- go back to copy above */
+            /* the copy above assures that we will leave with space in the
+               output buffer, allowing at least one gzungetc() to succeed */
+        }
+
+        /* large len -- read directly into user buffer */
+        else if (state->how == COPY) {      /* read directly */
+            if (gz_load(state, (unsigned char *)buf, n, &n) == -1)
+                return 0;
+        }
+
+        /* large len -- decompress directly into user buffer */
+        else {  /* state->how == GZIP */
+            state->strm.avail_out = n;
+            state->strm.next_out = (unsigned char *)buf;
+            if (gz_decomp(state) == -1)
+                return 0;
+            n = state->x.have;
+            state->x.have = 0;
+        }
+
+        /* update progress */
+        len -= n;
+        buf = (char *)buf + n;
+        got += n;
+        state->x.pos += n;
+    } while (len);
+
+    /* return number of bytes read into user buffer */
+    return got;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzread(file, buf, len)
+    gzFile file;
+    voidp buf;
+    unsigned len;
+{
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+
+    /* check that we're reading and that there's no (serious) error */
+    if (state->mode != GZ_READ ||
+            (state->err != Z_OK && state->err != Z_BUF_ERROR))
+        return -1;
+
+    /* since an int is returned, make sure len fits in one, otherwise return
+       with an error (this avoids a flaw in the interface) */
+    if ((int)len < 0) {
+        gz_error(state, Z_STREAM_ERROR, "request does not fit in an int");
+        return -1;
+    }
+
+    /* read len or fewer bytes to buf */
+    len = gz_read(state, buf, len);
+
+    /* check for an error */
+    if (len == 0 && state->err != Z_OK && state->err != Z_BUF_ERROR)
+        return -1;
+
+    /* return the number of bytes read (this is assured to fit in an int) */
+    return (int)len;
+}
+
+/* -- see zlib.h -- */
+z_size_t ZEXPORT gzfread(buf, size, nitems, file)
+    voidp buf;
+    z_size_t size;
+    z_size_t nitems;
+    gzFile file;
+{
+    z_size_t len;
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return 0;
+    state = (gz_statep)file;
+
+    /* check that we're reading and that there's no (serious) error */
+    if (state->mode != GZ_READ ||
+            (state->err != Z_OK && state->err != Z_BUF_ERROR))
+        return 0;
+
+    /* compute bytes to read -- error on overflow */
+    len = nitems * size;
+    if (size && len / size != nitems) {
+        gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t");
+        return 0;
+    }
+
+    /* read len or fewer bytes to buf, return the number of full items read */
+    return len ? gz_read(state, buf, len) / size : 0;
+}
+
+/* -- see zlib.h -- */
+#ifdef Z_PREFIX_SET
+#  undef z_gzgetc
+#else
+#  undef gzgetc
+#endif
+int ZEXPORT gzgetc(file)
+    gzFile file;
+{
+    int ret;
+    unsigned char buf[1];
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+
+    /* check that we're reading and that there's no (serious) error */
+    if (state->mode != GZ_READ ||
+        (state->err != Z_OK && state->err != Z_BUF_ERROR))
+        return -1;
+
+    /* try output buffer (no need to check for skip request) */
+    if (state->x.have) {
+        state->x.have--;
+        state->x.pos++;
+        return *(state->x.next)++;
+    }
+
+    /* nothing there -- try gz_read() */
+    ret = gz_read(state, buf, 1);
+    return ret < 1 ? -1 : buf[0];
+}
+
+int ZEXPORT gzgetc_(file)
+gzFile file;
+{
+    return gzgetc(file);
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzungetc(c, file)
+    int c;
+    gzFile file;
+{
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+
+    /* check that we're reading and that there's no (serious) error */
+    if (state->mode != GZ_READ ||
+        (state->err != Z_OK && state->err != Z_BUF_ERROR))
+        return -1;
+
+    /* process a skip request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_skip(state, state->skip) == -1)
+            return -1;
+    }
+
+    /* can't push EOF */
+    if (c < 0)
+        return -1;
+
+    /* if output buffer empty, put byte at end (allows more pushing) */
+    if (state->x.have == 0) {
+        state->x.have = 1;
+        state->x.next = state->out + (state->size << 1) - 1;
+        state->x.next[0] = (unsigned char)c;
+        state->x.pos--;
+        state->past = 0;
+        return c;
+    }
+
+    /* if no room, give up (must have already done a gzungetc()) */
+    if (state->x.have == (state->size << 1)) {
+        gz_error(state, Z_DATA_ERROR, "out of room to push characters");
+        return -1;
+    }
+
+    /* slide output data if needed and insert byte before existing data */
+    if (state->x.next == state->out) {
+        unsigned char *src = state->out + state->x.have;
+        unsigned char *dest = state->out + (state->size << 1);
+        while (src > state->out)
+            *--dest = *--src;
+        state->x.next = dest;
+    }
+    state->x.have++;
+    state->x.next--;
+    state->x.next[0] = (unsigned char)c;
+    state->x.pos--;
+    state->past = 0;
+    return c;
+}
+
+/* -- see zlib.h -- */
+char * ZEXPORT gzgets(file, buf, len)
+    gzFile file;
+    char *buf;
+    int len;
+{
+    unsigned left, n;
+    char *str;
+    unsigned char *eol;
+    gz_statep state;
+
+    /* check parameters and get internal structure */
+    if (file == NULL || buf == NULL || len < 1)
+        return NULL;
+    state = (gz_statep)file;
+
+    /* check that we're reading and that there's no (serious) error */
+    if (state->mode != GZ_READ ||
+        (state->err != Z_OK && state->err != Z_BUF_ERROR))
+        return NULL;
+
+    /* process a skip request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_skip(state, state->skip) == -1)
+            return NULL;
+    }
+
+    /* copy output bytes up to new line or len - 1, whichever comes first --
+       append a terminating zero to the string (we don't check for a zero in
+       the contents, let the user worry about that) */
+    str = buf;
+    left = (unsigned)len - 1;
+    if (left) do {
+        /* assure that something is in the output buffer */
+        if (state->x.have == 0 && gz_fetch(state) == -1)
+            return NULL;                /* error */
+        if (state->x.have == 0) {       /* end of file */
+            state->past = 1;            /* read past end */
+            break;                      /* return what we have */
+        }
+
+        /* look for end-of-line in current output buffer */
+        n = state->x.have > left ? left : state->x.have;
+        eol = (unsigned char *)memchr(state->x.next, '\n', n);
+        if (eol != NULL)
+            n = (unsigned)(eol - state->x.next) + 1;
+
+        /* copy through end-of-line, or remainder if not found */
+        memcpy(buf, state->x.next, n);
+        state->x.have -= n;
+        state->x.next += n;
+        state->x.pos += n;
+        left -= n;
+        buf += n;
+    } while (left && eol == NULL);
+
+    /* return terminated string, or if nothing, end of file */
+    if (buf == str)
+        return NULL;
+    buf[0] = 0;
+    return str;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzdirect(file)
+    gzFile file;
+{
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return 0;
+    state = (gz_statep)file;
+
+    /* if the state is not known, but we can find out, then do so (this is
+       mainly for right after a gzopen() or gzdopen()) */
+    if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0)
+        (void)gz_look(state);
+
+    /* return 1 if transparent, 0 if processing a gzip stream */
+    return state->direct;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzclose_r(file)
+    gzFile file;
+{
+    int ret, err;
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return Z_STREAM_ERROR;
+    state = (gz_statep)file;
+
+    /* check that we're reading */
+    if (state->mode != GZ_READ)
+        return Z_STREAM_ERROR;
+
+    /* free memory and close file */
+    if (state->size) {
+        inflateEnd(&(state->strm));
+        free(state->out);
+        free(state->in);
+    }
+    err = state->err == Z_BUF_ERROR ? Z_BUF_ERROR : Z_OK;
+    gz_error(state, Z_OK, NULL);
+    free(state->path);
+    ret = close(state->fd);
+    free(state);
+    return ret ? Z_ERRNO : err;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/gzwrite.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,665 @@
+/* gzwrite.c -- zlib functions for writing gzip files
+ * Copyright (C) 2004-2017 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "gzguts.h"
+
+/* Local functions */
+local int gz_init OF((gz_statep));
+local int gz_comp OF((gz_statep, int));
+local int gz_zero OF((gz_statep, z_off64_t));
+local z_size_t gz_write OF((gz_statep, voidpc, z_size_t));
+
+/* Initialize state for writing a gzip file.  Mark initialization by setting
+   state->size to non-zero.  Return -1 on a memory allocation failure, or 0 on
+   success. */
+local int gz_init(state)
+    gz_statep state;
+{
+    int ret;
+    z_streamp strm = &(state->strm);
+
+    /* allocate input buffer (double size for gzprintf) */
+    state->in = (unsigned char *)malloc(state->want << 1);
+    if (state->in == NULL) {
+        gz_error(state, Z_MEM_ERROR, "out of memory");
+        return -1;
+    }
+
+    /* only need output buffer and deflate state if compressing */
+    if (!state->direct) {
+        /* allocate output buffer */
+        state->out = (unsigned char *)malloc(state->want);
+        if (state->out == NULL) {
+            free(state->in);
+            gz_error(state, Z_MEM_ERROR, "out of memory");
+            return -1;
+        }
+
+        /* allocate deflate memory, set up for gzip compression */
+        strm->zalloc = Z_NULL;
+        strm->zfree = Z_NULL;
+        strm->opaque = Z_NULL;
+        ret = deflateInit2(strm, state->level, Z_DEFLATED,
+                           MAX_WBITS + 16, DEF_MEM_LEVEL, state->strategy);
+        if (ret != Z_OK) {
+            free(state->out);
+            free(state->in);
+            gz_error(state, Z_MEM_ERROR, "out of memory");
+            return -1;
+        }
+        strm->next_in = NULL;
+    }
+
+    /* mark state as initialized */
+    state->size = state->want;
+
+    /* initialize write buffer if compressing */
+    if (!state->direct) {
+        strm->avail_out = state->size;
+        strm->next_out = state->out;
+        state->x.next = strm->next_out;
+    }
+    return 0;
+}
+
+/* Compress whatever is at avail_in and next_in and write to the output file.
+   Return -1 if there is an error writing to the output file or if gz_init()
+   fails to allocate memory, otherwise 0.  flush is assumed to be a valid
+   deflate() flush value.  If flush is Z_FINISH, then the deflate() state is
+   reset to start a new gzip stream.  If gz->direct is true, then simply write
+   to the output file without compressing, and ignore flush. */
+local int gz_comp(state, flush)
+    gz_statep state;
+    int flush;
+{
+    int ret, writ;
+    unsigned have, put, max = ((unsigned)-1 >> 2) + 1;
+    z_streamp strm = &(state->strm);
+
+    /* allocate memory if this is the first time through */
+    if (state->size == 0 && gz_init(state) == -1)
+        return -1;
+
+    /* write directly if requested */
+    if (state->direct) {
+        while (strm->avail_in) {
+            put = strm->avail_in > max ? max : strm->avail_in;
+            writ = write(state->fd, strm->next_in, put);
+            if (writ < 0) {
+                gz_error(state, Z_ERRNO, zstrerror());
+                return -1;
+            }
+            strm->avail_in -= (unsigned)writ;
+            strm->next_in += writ;
+        }
+        return 0;
+    }
+
+    /* run deflate() on provided input until it produces no more output */
+    ret = Z_OK;
+    do {
+        /* write out current buffer contents if full, or if flushing, but if
+           doing Z_FINISH then don't write until we get to Z_STREAM_END */
+        if (strm->avail_out == 0 || (flush != Z_NO_FLUSH &&
+            (flush != Z_FINISH || ret == Z_STREAM_END))) {
+            while (strm->next_out > state->x.next) {
+                put = strm->next_out - state->x.next > (int)max ? max :
+                      (unsigned)(strm->next_out - state->x.next);
+                writ = write(state->fd, state->x.next, put);
+                if (writ < 0) {
+                    gz_error(state, Z_ERRNO, zstrerror());
+                    return -1;
+                }
+                state->x.next += writ;
+            }
+            if (strm->avail_out == 0) {
+                strm->avail_out = state->size;
+                strm->next_out = state->out;
+                state->x.next = state->out;
+            }
+        }
+
+        /* compress */
+        have = strm->avail_out;
+        ret = deflate(strm, flush);
+        if (ret == Z_STREAM_ERROR) {
+            gz_error(state, Z_STREAM_ERROR,
+                      "internal error: deflate stream corrupt");
+            return -1;
+        }
+        have -= strm->avail_out;
+    } while (have);
+
+    /* if that completed a deflate stream, allow another to start */
+    if (flush == Z_FINISH)
+        deflateReset(strm);
+
+    /* all done, no errors */
+    return 0;
+}
+
+/* Compress len zeros to output.  Return -1 on a write error or memory
+   allocation failure by gz_comp(), or 0 on success. */
+local int gz_zero(state, len)
+    gz_statep state;
+    z_off64_t len;
+{
+    int first;
+    unsigned n;
+    z_streamp strm = &(state->strm);
+
+    /* consume whatever's left in the input buffer */
+    if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
+        return -1;
+
+    /* compress len zeros (len guaranteed > 0) */
+    first = 1;
+    while (len) {
+        n = GT_OFF(state->size) || (z_off64_t)state->size > len ?
+            (unsigned)len : state->size;
+        if (first) {
+            memset(state->in, 0, n);
+            first = 0;
+        }
+        strm->avail_in = n;
+        strm->next_in = state->in;
+        state->x.pos += n;
+        if (gz_comp(state, Z_NO_FLUSH) == -1)
+            return -1;
+        len -= n;
+    }
+    return 0;
+}
+
+/* Write len bytes from buf to file.  Return the number of bytes written.  If
+   the returned value is less than len, then there was an error. */
+local z_size_t gz_write(state, buf, len)
+    gz_statep state;
+    voidpc buf;
+    z_size_t len;
+{
+    z_size_t put = len;
+
+    /* if len is zero, avoid unnecessary operations */
+    if (len == 0)
+        return 0;
+
+    /* allocate memory if this is the first time through */
+    if (state->size == 0 && gz_init(state) == -1)
+        return 0;
+
+    /* check for seek request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_zero(state, state->skip) == -1)
+            return 0;
+    }
+
+    /* for small len, copy to input buffer, otherwise compress directly */
+    if (len < state->size) {
+        /* copy to input buffer, compress when full */
+        do {
+            unsigned have, copy;
+
+            if (state->strm.avail_in == 0)
+                state->strm.next_in = state->in;
+            have = (unsigned)((state->strm.next_in + state->strm.avail_in) -
+                              state->in);
+            copy = state->size - have;
+            if (copy > len)
+                copy = len;
+            memcpy(state->in + have, buf, copy);
+            state->strm.avail_in += copy;
+            state->x.pos += copy;
+            buf = (const char *)buf + copy;
+            len -= copy;
+            if (len && gz_comp(state, Z_NO_FLUSH) == -1)
+                return 0;
+        } while (len);
+    }
+    else {
+        /* consume whatever's left in the input buffer */
+        if (state->strm.avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
+            return 0;
+
+        /* directly compress user buffer to file */
+        state->strm.next_in = (z_const Bytef *)buf;
+        do {
+            unsigned n = (unsigned)-1;
+            if (n > len)
+                n = len;
+            state->strm.avail_in = n;
+            state->x.pos += n;
+            if (gz_comp(state, Z_NO_FLUSH) == -1)
+                return 0;
+            len -= n;
+        } while (len);
+    }
+
+    /* input was all buffered or compressed */
+    return put;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzwrite(file, buf, len)
+    gzFile file;
+    voidpc buf;
+    unsigned len;
+{
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return 0;
+    state = (gz_statep)file;
+
+    /* check that we're writing and that there's no error */
+    if (state->mode != GZ_WRITE || state->err != Z_OK)
+        return 0;
+
+    /* since an int is returned, make sure len fits in one, otherwise return
+       with an error (this avoids a flaw in the interface) */
+    if ((int)len < 0) {
+        gz_error(state, Z_DATA_ERROR, "requested length does not fit in int");
+        return 0;
+    }
+
+    /* write len bytes from buf (the return value will fit in an int) */
+    return (int)gz_write(state, buf, len);
+}
+
+/* -- see zlib.h -- */
+z_size_t ZEXPORT gzfwrite(buf, size, nitems, file)
+    voidpc buf;
+    z_size_t size;
+    z_size_t nitems;
+    gzFile file;
+{
+    z_size_t len;
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return 0;
+    state = (gz_statep)file;
+
+    /* check that we're writing and that there's no error */
+    if (state->mode != GZ_WRITE || state->err != Z_OK)
+        return 0;
+
+    /* compute bytes to read -- error on overflow */
+    len = nitems * size;
+    if (size && len / size != nitems) {
+        gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t");
+        return 0;
+    }
+
+    /* write len bytes to buf, return the number of full items written */
+    return len ? gz_write(state, buf, len) / size : 0;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzputc(file, c)
+    gzFile file;
+    int c;
+{
+    unsigned have;
+    unsigned char buf[1];
+    gz_statep state;
+    z_streamp strm;
+
+    /* get internal structure */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+    strm = &(state->strm);
+
+    /* check that we're writing and that there's no error */
+    if (state->mode != GZ_WRITE || state->err != Z_OK)
+        return -1;
+
+    /* check for seek request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_zero(state, state->skip) == -1)
+            return -1;
+    }
+
+    /* try writing to input buffer for speed (state->size == 0 if buffer not
+       initialized) */
+    if (state->size) {
+        if (strm->avail_in == 0)
+            strm->next_in = state->in;
+        have = (unsigned)((strm->next_in + strm->avail_in) - state->in);
+        if (have < state->size) {
+            state->in[have] = (unsigned char)c;
+            strm->avail_in++;
+            state->x.pos++;
+            return c & 0xff;
+        }
+    }
+
+    /* no room in buffer or not initialized, use gz_write() */
+    buf[0] = (unsigned char)c;
+    if (gz_write(state, buf, 1) != 1)
+        return -1;
+    return c & 0xff;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzputs(file, str)
+    gzFile file;
+    const char *str;
+{
+    int ret;
+    z_size_t len;
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+
+    /* check that we're writing and that there's no error */
+    if (state->mode != GZ_WRITE || state->err != Z_OK)
+        return -1;
+
+    /* write string */
+    len = strlen(str);
+    ret = gz_write(state, str, len);
+    return ret == 0 && len != 0 ? -1 : ret;
+}
+
+#if defined(STDC) || defined(Z_HAVE_STDARG_H)
+#include <stdarg.h>
+
+/* -- see zlib.h -- */
+int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va)
+{
+    int len;
+    unsigned left;
+    char *next;
+    gz_statep state;
+    z_streamp strm;
+
+    /* get internal structure */
+    if (file == NULL)
+        return Z_STREAM_ERROR;
+    state = (gz_statep)file;
+    strm = &(state->strm);
+
+    /* check that we're writing and that there's no error */
+    if (state->mode != GZ_WRITE || state->err != Z_OK)
+        return Z_STREAM_ERROR;
+
+    /* make sure we have some buffer space */
+    if (state->size == 0 && gz_init(state) == -1)
+        return state->err;
+
+    /* check for seek request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_zero(state, state->skip) == -1)
+            return state->err;
+    }
+
+    /* do the printf() into the input buffer, put length in len -- the input
+       buffer is double-sized just for this function, so there is guaranteed to
+       be state->size bytes available after the current contents */
+    if (strm->avail_in == 0)
+        strm->next_in = state->in;
+    next = (char *)(state->in + (strm->next_in - state->in) + strm->avail_in);
+    next[state->size - 1] = 0;
+#ifdef NO_vsnprintf
+#  ifdef HAS_vsprintf_void
+    (void)vsprintf(next, format, va);
+    for (len = 0; len < state->size; len++)
+        if (next[len] == 0) break;
+#  else
+    len = vsprintf(next, format, va);
+#  endif
+#else
+#  ifdef HAS_vsnprintf_void
+    (void)vsnprintf(next, state->size, format, va);
+    len = strlen(next);
+#  else
+    len = vsnprintf(next, state->size, format, va);
+#  endif
+#endif
+
+    /* check that printf() results fit in buffer */
+    if (len == 0 || (unsigned)len >= state->size || next[state->size - 1] != 0)
+        return 0;
+
+    /* update buffer and position, compress first half if past that */
+    strm->avail_in += (unsigned)len;
+    state->x.pos += len;
+    if (strm->avail_in >= state->size) {
+        left = strm->avail_in - state->size;
+        strm->avail_in = state->size;
+        if (gz_comp(state, Z_NO_FLUSH) == -1)
+            return state->err;
+        memcpy(state->in, state->in + state->size, left);
+        strm->next_in = state->in;
+        strm->avail_in = left;
+    }
+    return len;
+}
+
+int ZEXPORTVA gzprintf(gzFile file, const char *format, ...)
+{
+    va_list va;
+    int ret;
+
+    va_start(va, format);
+    ret = gzvprintf(file, format, va);
+    va_end(va);
+    return ret;
+}
+
+#else /* !STDC && !Z_HAVE_STDARG_H */
+
+/* -- see zlib.h -- */
+int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+                       a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+    gzFile file;
+    const char *format;
+    int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+        a11, a12, a13, a14, a15, a16, a17, a18, a19, a20;
+{
+    unsigned len, left;
+    char *next;
+    gz_statep state;
+    z_streamp strm;
+
+    /* get internal structure */
+    if (file == NULL)
+        return Z_STREAM_ERROR;
+    state = (gz_statep)file;
+    strm = &(state->strm);
+
+    /* check that can really pass pointer in ints */
+    if (sizeof(int) != sizeof(void *))
+        return Z_STREAM_ERROR;
+
+    /* check that we're writing and that there's no error */
+    if (state->mode != GZ_WRITE || state->err != Z_OK)
+        return Z_STREAM_ERROR;
+
+    /* make sure we have some buffer space */
+    if (state->size == 0 && gz_init(state) == -1)
+        return state->error;
+
+    /* check for seek request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_zero(state, state->skip) == -1)
+            return state->error;
+    }
+
+    /* do the printf() into the input buffer, put length in len -- the input
+       buffer is double-sized just for this function, so there is guaranteed to
+       be state->size bytes available after the current contents */
+    if (strm->avail_in == 0)
+        strm->next_in = state->in;
+    next = (char *)(strm->next_in + strm->avail_in);
+    next[state->size - 1] = 0;
+#ifdef NO_snprintf
+#  ifdef HAS_sprintf_void
+    sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
+            a13, a14, a15, a16, a17, a18, a19, a20);
+    for (len = 0; len < size; len++)
+        if (next[len] == 0)
+            break;
+#  else
+    len = sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
+                  a12, a13, a14, a15, a16, a17, a18, a19, a20);
+#  endif
+#else
+#  ifdef HAS_snprintf_void
+    snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, a9,
+             a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+    len = strlen(next);
+#  else
+    len = snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8,
+                   a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+#  endif
+#endif
+
+    /* check that printf() results fit in buffer */
+    if (len == 0 || len >= state->size || next[state->size - 1] != 0)
+        return 0;
+
+    /* update buffer and position, compress first half if past that */
+    strm->avail_in += len;
+    state->x.pos += len;
+    if (strm->avail_in >= state->size) {
+        left = strm->avail_in - state->size;
+        strm->avail_in = state->size;
+        if (gz_comp(state, Z_NO_FLUSH) == -1)
+            return state->err;
+        memcpy(state->in, state->in + state->size, left);
+        strm->next_in = state->in;
+        strm->avail_in = left;
+    }
+    return (int)len;
+}
+
+#endif
+
+/* -- see zlib.h -- */
+int ZEXPORT gzflush(file, flush)
+    gzFile file;
+    int flush;
+{
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return Z_STREAM_ERROR;
+    state = (gz_statep)file;
+
+    /* check that we're writing and that there's no error */
+    if (state->mode != GZ_WRITE || state->err != Z_OK)
+        return Z_STREAM_ERROR;
+
+    /* check flush parameter */
+    if (flush < 0 || flush > Z_FINISH)
+        return Z_STREAM_ERROR;
+
+    /* check for seek request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_zero(state, state->skip) == -1)
+            return state->err;
+    }
+
+    /* compress remaining data with requested flush */
+    (void)gz_comp(state, flush);
+    return state->err;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzsetparams(file, level, strategy)
+    gzFile file;
+    int level;
+    int strategy;
+{
+    gz_statep state;
+    z_streamp strm;
+
+    /* get internal structure */
+    if (file == NULL)
+        return Z_STREAM_ERROR;
+    state = (gz_statep)file;
+    strm = &(state->strm);
+
+    /* check that we're writing and that there's no error */
+    if (state->mode != GZ_WRITE || state->err != Z_OK)
+        return Z_STREAM_ERROR;
+
+    /* if no change is requested, then do nothing */
+    if (level == state->level && strategy == state->strategy)
+        return Z_OK;
+
+    /* check for seek request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_zero(state, state->skip) == -1)
+            return state->err;
+    }
+
+    /* change compression parameters for subsequent input */
+    if (state->size) {
+        /* flush previous input with previous parameters before changing */
+        if (strm->avail_in && gz_comp(state, Z_BLOCK) == -1)
+            return state->err;
+        deflateParams(strm, level, strategy);
+    }
+    state->level = level;
+    state->strategy = strategy;
+    return Z_OK;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzclose_w(file)
+    gzFile file;
+{
+    int ret = Z_OK;
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return Z_STREAM_ERROR;
+    state = (gz_statep)file;
+
+    /* check that we're writing */
+    if (state->mode != GZ_WRITE)
+        return Z_STREAM_ERROR;
+
+    /* check for seek request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_zero(state, state->skip) == -1)
+            ret = state->err;
+    }
+
+    /* flush, free memory, and close file */
+    if (gz_comp(state, Z_FINISH) == -1)
+        ret = state->err;
+    if (state->size) {
+        if (!state->direct) {
+            (void)deflateEnd(&(state->strm));
+            free(state->out);
+        }
+        free(state->in);
+    }
+    gz_error(state, Z_OK, NULL);
+    free(state->path);
+    if (close(state->fd) == -1)
+        ret = Z_ERRNO;
+    free(state);
+    return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/infback.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,640 @@
+/* infback.c -- inflate using a call-back interface
+ * Copyright (C) 1995-2016 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+   This code is largely copied from inflate.c.  Normally either infback.o or
+   inflate.o would be linked into an application--not both.  The interface
+   with inffast.c is retained so that optimized assembler-coded versions of
+   inflate_fast() can be used with either inflate.c or infback.c.
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+/* function prototypes */
+local void fixedtables OF((struct inflate_state FAR *state));
+
+/*
+   strm provides memory allocation functions in zalloc and zfree, or
+   Z_NULL to use the library memory allocation functions.
+
+   windowBits is in the range 8..15, and window is a user-supplied
+   window and output buffer that is 2**windowBits bytes.
+ */
+int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size)
+z_streamp strm;
+int windowBits;
+unsigned char FAR *window;
+const char *version;
+int stream_size;
+{
+    struct inflate_state FAR *state;
+
+    if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+        stream_size != (int)(sizeof(z_stream)))
+        return Z_VERSION_ERROR;
+    if (strm == Z_NULL || window == Z_NULL ||
+        windowBits < 8 || windowBits > 15)
+        return Z_STREAM_ERROR;
+    strm->msg = Z_NULL;                 /* in case we return an error */
+    if (strm->zalloc == (alloc_func)0) {
+#ifdef Z_SOLO
+        return Z_STREAM_ERROR;
+#else
+        strm->zalloc = zcalloc;
+        strm->opaque = (voidpf)0;
+#endif
+    }
+    if (strm->zfree == (free_func)0)
+#ifdef Z_SOLO
+        return Z_STREAM_ERROR;
+#else
+    strm->zfree = zcfree;
+#endif
+    state = (struct inflate_state FAR *)ZALLOC(strm, 1,
+                                               sizeof(struct inflate_state));
+    if (state == Z_NULL) return Z_MEM_ERROR;
+    Tracev((stderr, "inflate: allocated\n"));
+    strm->state = (struct internal_state FAR *)state;
+    state->dmax = 32768U;
+    state->wbits = (uInt)windowBits;
+    state->wsize = 1U << windowBits;
+    state->window = window;
+    state->wnext = 0;
+    state->whave = 0;
+    return Z_OK;
+}
+
+/*
+   Return state with length and distance decoding tables and index sizes set to
+   fixed code decoding.  Normally this returns fixed tables from inffixed.h.
+   If BUILDFIXED is defined, then instead this routine builds the tables the
+   first time it's called, and returns those tables the first time and
+   thereafter.  This reduces the size of the code by about 2K bytes, in
+   exchange for a little execution time.  However, BUILDFIXED should not be
+   used for threaded applications, since the rewriting of the tables and virgin
+   may not be thread-safe.
+ */
+local void fixedtables(state)
+struct inflate_state FAR *state;
+{
+#ifdef BUILDFIXED
+    static int virgin = 1;
+    static code *lenfix, *distfix;
+    static code fixed[544];
+
+    /* build fixed huffman tables if first call (may not be thread safe) */
+    if (virgin) {
+        unsigned sym, bits;
+        static code *next;
+
+        /* literal/length table */
+        sym = 0;
+        while (sym < 144) state->lens[sym++] = 8;
+        while (sym < 256) state->lens[sym++] = 9;
+        while (sym < 280) state->lens[sym++] = 7;
+        while (sym < 288) state->lens[sym++] = 8;
+        next = fixed;
+        lenfix = next;
+        bits = 9;
+        inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
+
+        /* distance table */
+        sym = 0;
+        while (sym < 32) state->lens[sym++] = 5;
+        distfix = next;
+        bits = 5;
+        inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
+
+        /* do this just once */
+        virgin = 0;
+    }
+#else /* !BUILDFIXED */
+#   include "inffixed.h"
+#endif /* BUILDFIXED */
+    state->lencode = lenfix;
+    state->lenbits = 9;
+    state->distcode = distfix;
+    state->distbits = 5;
+}
+
+/* Macros for inflateBack(): */
+
+/* Load returned state from inflate_fast() */
+#define LOAD() \
+    do { \
+        put = strm->next_out; \
+        left = strm->avail_out; \
+        next = strm->next_in; \
+        have = strm->avail_in; \
+        hold = state->hold; \
+        bits = state->bits; \
+    } while (0)
+
+/* Set state from registers for inflate_fast() */
+#define RESTORE() \
+    do { \
+        strm->next_out = put; \
+        strm->avail_out = left; \
+        strm->next_in = next; \
+        strm->avail_in = have; \
+        state->hold = hold; \
+        state->bits = bits; \
+    } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+    do { \
+        hold = 0; \
+        bits = 0; \
+    } while (0)
+
+/* Assure that some input is available.  If input is requested, but denied,
+   then return a Z_BUF_ERROR from inflateBack(). */
+#define PULL() \
+    do { \
+        if (have == 0) { \
+            have = in(in_desc, &next); \
+            if (have == 0) { \
+                next = Z_NULL; \
+                ret = Z_BUF_ERROR; \
+                goto inf_leave; \
+            } \
+        } \
+    } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflateBack()
+   with an error if there is no input available. */
+#define PULLBYTE() \
+    do { \
+        PULL(); \
+        have--; \
+        hold += (unsigned long)(*next++) << bits; \
+        bits += 8; \
+    } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator.  If there is
+   not enough available input to do that, then return from inflateBack() with
+   an error. */
+#define NEEDBITS(n) \
+    do { \
+        while (bits < (unsigned)(n)) \
+            PULLBYTE(); \
+    } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+    ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+    do { \
+        hold >>= (n); \
+        bits -= (unsigned)(n); \
+    } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+    do { \
+        hold >>= bits & 7; \
+        bits -= bits & 7; \
+    } while (0)
+
+/* Assure that some output space is available, by writing out the window
+   if it's full.  If the write fails, return from inflateBack() with a
+   Z_BUF_ERROR. */
+#define ROOM() \
+    do { \
+        if (left == 0) { \
+            put = state->window; \
+            left = state->wsize; \
+            state->whave = left; \
+            if (out(out_desc, put, left)) { \
+                ret = Z_BUF_ERROR; \
+                goto inf_leave; \
+            } \
+        } \
+    } while (0)
+
+/*
+   strm provides the memory allocation functions and window buffer on input,
+   and provides information on the unused input on return.  For Z_DATA_ERROR
+   returns, strm will also provide an error message.
+
+   in() and out() are the call-back input and output functions.  When
+   inflateBack() needs more input, it calls in().  When inflateBack() has
+   filled the window with output, or when it completes with data in the
+   window, it calls out() to write out the data.  The application must not
+   change the provided input until in() is called again or inflateBack()
+   returns.  The application must not change the window/output buffer until
+   inflateBack() returns.
+
+   in() and out() are called with a descriptor parameter provided in the
+   inflateBack() call.  This parameter can be a structure that provides the
+   information required to do the read or write, as well as accumulated
+   information on the input and output such as totals and check values.
+
+   in() should return zero on failure.  out() should return non-zero on
+   failure.  If either in() or out() fails, than inflateBack() returns a
+   Z_BUF_ERROR.  strm->next_in can be checked for Z_NULL to see whether it
+   was in() or out() that caused in the error.  Otherwise,  inflateBack()
+   returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format
+   error, or Z_MEM_ERROR if it could not allocate memory for the state.
+   inflateBack() can also return Z_STREAM_ERROR if the input parameters
+   are not correct, i.e. strm is Z_NULL or the state was not initialized.
+ */
+int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc)
+z_streamp strm;
+in_func in;
+void FAR *in_desc;
+out_func out;
+void FAR *out_desc;
+{
+    struct inflate_state FAR *state;
+    z_const unsigned char FAR *next;    /* next input */
+    unsigned char FAR *put;     /* next output */
+    unsigned have, left;        /* available input and output */
+    unsigned long hold;         /* bit buffer */
+    unsigned bits;              /* bits in bit buffer */
+    unsigned copy;              /* number of stored or match bytes to copy */
+    unsigned char FAR *from;    /* where to copy match bytes from */
+    code here;                  /* current decoding table entry */
+    code last;                  /* parent table entry */
+    unsigned len;               /* length to copy for repeats, bits to drop */
+    int ret;                    /* return code */
+    static const unsigned short order[19] = /* permutation of code lengths */
+        {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+    /* Check that the strm exists and that the state was initialized */
+    if (strm == Z_NULL || strm->state == Z_NULL)
+        return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+
+    /* Reset the state */
+    strm->msg = Z_NULL;
+    state->mode = TYPE;
+    state->last = 0;
+    state->whave = 0;
+    next = strm->next_in;
+    have = next != Z_NULL ? strm->avail_in : 0;
+    hold = 0;
+    bits = 0;
+    put = state->window;
+    left = state->wsize;
+
+    /* Inflate until end of block marked as last */
+    for (;;)
+        switch (state->mode) {
+        case TYPE:
+            /* determine and dispatch block type */
+            if (state->last) {
+                BYTEBITS();
+                state->mode = DONE;
+                break;
+            }
+            NEEDBITS(3);
+            state->last = BITS(1);
+            DROPBITS(1);
+            switch (BITS(2)) {
+            case 0:                             /* stored block */
+                Tracev((stderr, "inflate:     stored block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = STORED;
+                break;
+            case 1:                             /* fixed block */
+                fixedtables(state);
+                Tracev((stderr, "inflate:     fixed codes block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = LEN;              /* decode codes */
+                break;
+            case 2:                             /* dynamic block */
+                Tracev((stderr, "inflate:     dynamic codes block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = TABLE;
+                break;
+            case 3:
+                strm->msg = (char *)"invalid block type";
+                state->mode = BAD;
+            }
+            DROPBITS(2);
+            break;
+
+        case STORED:
+            /* get and verify stored block length */
+            BYTEBITS();                         /* go to byte boundary */
+            NEEDBITS(32);
+            if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+                strm->msg = (char *)"invalid stored block lengths";
+                state->mode = BAD;
+                break;
+            }
+            state->length = (unsigned)hold & 0xffff;
+            Tracev((stderr, "inflate:       stored length %u\n",
+                    state->length));
+            INITBITS();
+
+            /* copy stored block from input to output */
+            while (state->length != 0) {
+                copy = state->length;
+                PULL();
+                ROOM();
+                if (copy > have) copy = have;
+                if (copy > left) copy = left;
+                zmemcpy(put, next, copy);
+                have -= copy;
+                next += copy;
+                left -= copy;
+                put += copy;
+                state->length -= copy;
+            }
+            Tracev((stderr, "inflate:       stored end\n"));
+            state->mode = TYPE;
+            break;
+
+        case TABLE:
+            /* get dynamic table entries descriptor */
+            NEEDBITS(14);
+            state->nlen = BITS(5) + 257;
+            DROPBITS(5);
+            state->ndist = BITS(5) + 1;
+            DROPBITS(5);
+            state->ncode = BITS(4) + 4;
+            DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+            if (state->nlen > 286 || state->ndist > 30) {
+                strm->msg = (char *)"too many length or distance symbols";
+                state->mode = BAD;
+                break;
+            }
+#endif
+            Tracev((stderr, "inflate:       table sizes ok\n"));
+
+            /* get code length code lengths (not a typo) */
+            state->have = 0;
+            while (state->have < state->ncode) {
+                NEEDBITS(3);
+                state->lens[order[state->have++]] = (unsigned short)BITS(3);
+                DROPBITS(3);
+            }
+            while (state->have < 19)
+                state->lens[order[state->have++]] = 0;
+            state->next = state->codes;
+            state->lencode = (code const FAR *)(state->next);
+            state->lenbits = 7;
+            ret = inflate_table(CODES, state->lens, 19, &(state->next),
+                                &(state->lenbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid code lengths set";
+                state->mode = BAD;
+                break;
+            }
+            Tracev((stderr, "inflate:       code lengths ok\n"));
+
+            /* get length and distance code code lengths */
+            state->have = 0;
+            while (state->have < state->nlen + state->ndist) {
+                for (;;) {
+                    here = state->lencode[BITS(state->lenbits)];
+                    if ((unsigned)(here.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                if (here.val < 16) {
+                    DROPBITS(here.bits);
+                    state->lens[state->have++] = here.val;
+                }
+                else {
+                    if (here.val == 16) {
+                        NEEDBITS(here.bits + 2);
+                        DROPBITS(here.bits);
+                        if (state->have == 0) {
+                            strm->msg = (char *)"invalid bit length repeat";
+                            state->mode = BAD;
+                            break;
+                        }
+                        len = (unsigned)(state->lens[state->have - 1]);
+                        copy = 3 + BITS(2);
+                        DROPBITS(2);
+                    }
+                    else if (here.val == 17) {
+                        NEEDBITS(here.bits + 3);
+                        DROPBITS(here.bits);
+                        len = 0;
+                        copy = 3 + BITS(3);
+                        DROPBITS(3);
+                    }
+                    else {
+                        NEEDBITS(here.bits + 7);
+                        DROPBITS(here.bits);
+                        len = 0;
+                        copy = 11 + BITS(7);
+                        DROPBITS(7);
+                    }
+                    if (state->have + copy > state->nlen + state->ndist) {
+                        strm->msg = (char *)"invalid bit length repeat";
+                        state->mode = BAD;
+                        break;
+                    }
+                    while (copy--)
+                        state->lens[state->have++] = (unsigned short)len;
+                }
+            }
+
+            /* handle error breaks in while */
+            if (state->mode == BAD) break;
+
+            /* check for end-of-block code (better have one) */
+            if (state->lens[256] == 0) {
+                strm->msg = (char *)"invalid code -- missing end-of-block";
+                state->mode = BAD;
+                break;
+            }
+
+            /* build code tables -- note: do not change the lenbits or distbits
+               values here (9 and 6) without reading the comments in inftrees.h
+               concerning the ENOUGH constants, which depend on those values */
+            state->next = state->codes;
+            state->lencode = (code const FAR *)(state->next);
+            state->lenbits = 9;
+            ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
+                                &(state->lenbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid literal/lengths set";
+                state->mode = BAD;
+                break;
+            }
+            state->distcode = (code const FAR *)(state->next);
+            state->distbits = 6;
+            ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+                            &(state->next), &(state->distbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid distances set";
+                state->mode = BAD;
+                break;
+            }
+            Tracev((stderr, "inflate:       codes ok\n"));
+            state->mode = LEN;
+
+        case LEN:
+            /* use inflate_fast() if we have enough input and output */
+            if (have >= 6 && left >= 258) {
+                RESTORE();
+                if (state->whave < state->wsize)
+                    state->whave = state->wsize - left;
+                inflate_fast(strm, state->wsize);
+                LOAD();
+                break;
+            }
+
+            /* get a literal, length, or end-of-block code */
+            for (;;) {
+                here = state->lencode[BITS(state->lenbits)];
+                if ((unsigned)(here.bits) <= bits) break;
+                PULLBYTE();
+            }
+            if (here.op && (here.op & 0xf0) == 0) {
+                last = here;
+                for (;;) {
+                    here = state->lencode[last.val +
+                            (BITS(last.bits + last.op) >> last.bits)];
+                    if ((unsigned)(last.bits + here.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                DROPBITS(last.bits);
+            }
+            DROPBITS(here.bits);
+            state->length = (unsigned)here.val;
+
+            /* process literal */
+            if (here.op == 0) {
+                Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
+                        "inflate:         literal '%c'\n" :
+                        "inflate:         literal 0x%02x\n", here.val));
+                ROOM();
+                *put++ = (unsigned char)(state->length);
+                left--;
+                state->mode = LEN;
+                break;
+            }
+
+            /* process end of block */
+            if (here.op & 32) {
+                Tracevv((stderr, "inflate:         end of block\n"));
+                state->mode = TYPE;
+                break;
+            }
+
+            /* invalid code */
+            if (here.op & 64) {
+                strm->msg = (char *)"invalid literal/length code";
+                state->mode = BAD;
+                break;
+            }
+
+            /* length code -- get extra bits, if any */
+            state->extra = (unsigned)(here.op) & 15;
+            if (state->extra != 0) {
+                NEEDBITS(state->extra);
+                state->length += BITS(state->extra);
+                DROPBITS(state->extra);
+            }
+            Tracevv((stderr, "inflate:         length %u\n", state->length));
+
+            /* get distance code */
+            for (;;) {
+                here = state->distcode[BITS(state->distbits)];
+                if ((unsigned)(here.bits) <= bits) break;
+                PULLBYTE();
+            }
+            if ((here.op & 0xf0) == 0) {
+                last = here;
+                for (;;) {
+                    here = state->distcode[last.val +
+                            (BITS(last.bits + last.op) >> last.bits)];
+                    if ((unsigned)(last.bits + here.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                DROPBITS(last.bits);
+            }
+            DROPBITS(here.bits);
+            if (here.op & 64) {
+                strm->msg = (char *)"invalid distance code";
+                state->mode = BAD;
+                break;
+            }
+            state->offset = (unsigned)here.val;
+
+            /* get distance extra bits, if any */
+            state->extra = (unsigned)(here.op) & 15;
+            if (state->extra != 0) {
+                NEEDBITS(state->extra);
+                state->offset += BITS(state->extra);
+                DROPBITS(state->extra);
+            }
+            if (state->offset > state->wsize - (state->whave < state->wsize ?
+                                                left : 0)) {
+                strm->msg = (char *)"invalid distance too far back";
+                state->mode = BAD;
+                break;
+            }
+            Tracevv((stderr, "inflate:         distance %u\n", state->offset));
+
+            /* copy match from window to output */
+            do {
+                ROOM();
+                copy = state->wsize - state->offset;
+                if (copy < left) {
+                    from = put + copy;
+                    copy = left - copy;
+                }
+                else {
+                    from = put - state->offset;
+                    copy = left;
+                }
+                if (copy > state->length) copy = state->length;
+                state->length -= copy;
+                left -= copy;
+                do {
+                    *put++ = *from++;
+                } while (--copy);
+            } while (state->length != 0);
+            break;
+
+        case DONE:
+            /* inflate stream terminated properly -- write leftover output */
+            ret = Z_STREAM_END;
+            if (left < state->wsize) {
+                if (out(out_desc, state->window, state->wsize - left))
+                    ret = Z_BUF_ERROR;
+            }
+            goto inf_leave;
+
+        case BAD:
+            ret = Z_DATA_ERROR;
+            goto inf_leave;
+
+        default:                /* can't happen, but makes compilers happy */
+            ret = Z_STREAM_ERROR;
+            goto inf_leave;
+        }
+
+    /* Return unused input */
+  inf_leave:
+    strm->next_in = next;
+    strm->avail_in = have;
+    return ret;
+}
+
+int ZEXPORT inflateBackEnd(strm)
+z_streamp strm;
+{
+    if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
+        return Z_STREAM_ERROR;
+    ZFREE(strm, strm->state);
+    strm->state = Z_NULL;
+    Tracev((stderr, "inflate: end\n"));
+    return Z_OK;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/inffast.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,323 @@
+/* inffast.c -- fast decoding
+ * Copyright (C) 1995-2017 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+#ifdef ASMINF
+#  pragma message("Assembler code may have bugs -- use at your own risk")
+#else
+
+/*
+   Decode literal, length, and distance codes and write out the resulting
+   literal and match bytes until either not enough input or output is
+   available, an end-of-block is encountered, or a data error is encountered.
+   When large enough input and output buffers are supplied to inflate(), for
+   example, a 16K input buffer and a 64K output buffer, more than 95% of the
+   inflate execution time is spent in this routine.
+
+   Entry assumptions:
+
+        state->mode == LEN
+        strm->avail_in >= 6
+        strm->avail_out >= 258
+        start >= strm->avail_out
+        state->bits < 8
+
+   On return, state->mode is one of:
+
+        LEN -- ran out of enough output space or enough available input
+        TYPE -- reached end of block code, inflate() to interpret next block
+        BAD -- error in block data
+
+   Notes:
+
+    - The maximum input bits used by a length/distance pair is 15 bits for the
+      length code, 5 bits for the length extra, 15 bits for the distance code,
+      and 13 bits for the distance extra.  This totals 48 bits, or six bytes.
+      Therefore if strm->avail_in >= 6, then there is enough input to avoid
+      checking for available input while decoding.
+
+    - The maximum bytes that a single length/distance pair can output is 258
+      bytes, which is the maximum length that can be coded.  inflate_fast()
+      requires strm->avail_out >= 258 for each loop to avoid checking for
+      output space.
+ */
+void ZLIB_INTERNAL inflate_fast(strm, start)
+z_streamp strm;
+unsigned start;         /* inflate()'s starting value for strm->avail_out */
+{
+    struct inflate_state FAR *state;
+    z_const unsigned char FAR *in;      /* local strm->next_in */
+    z_const unsigned char FAR *last;    /* have enough input while in < last */
+    unsigned char FAR *out;     /* local strm->next_out */
+    unsigned char FAR *beg;     /* inflate()'s initial strm->next_out */
+    unsigned char FAR *end;     /* while out < end, enough space available */
+#ifdef INFLATE_STRICT
+    unsigned dmax;              /* maximum distance from zlib header */
+#endif
+    unsigned wsize;             /* window size or zero if not using window */
+    unsigned whave;             /* valid bytes in the window */
+    unsigned wnext;             /* window write index */
+    unsigned char FAR *window;  /* allocated sliding window, if wsize != 0 */
+    unsigned long hold;         /* local strm->hold */
+    unsigned bits;              /* local strm->bits */
+    code const FAR *lcode;      /* local strm->lencode */
+    code const FAR *dcode;      /* local strm->distcode */
+    unsigned lmask;             /* mask for first level of length codes */
+    unsigned dmask;             /* mask for first level of distance codes */
+    code here;                  /* retrieved table entry */
+    unsigned op;                /* code bits, operation, extra bits, or */
+                                /*  window position, window bytes to copy */
+    unsigned len;               /* match length, unused bytes */
+    unsigned dist;              /* match distance */
+    unsigned char FAR *from;    /* where to copy match from */
+
+    /* copy state to local variables */
+    state = (struct inflate_state FAR *)strm->state;
+    in = strm->next_in;
+    last = in + (strm->avail_in - 5);
+    out = strm->next_out;
+    beg = out - (start - strm->avail_out);
+    end = out + (strm->avail_out - 257);
+#ifdef INFLATE_STRICT
+    dmax = state->dmax;
+#endif
+    wsize = state->wsize;
+    whave = state->whave;
+    wnext = state->wnext;
+    window = state->window;
+    hold = state->hold;
+    bits = state->bits;
+    lcode = state->lencode;
+    dcode = state->distcode;
+    lmask = (1U << state->lenbits) - 1;
+    dmask = (1U << state->distbits) - 1;
+
+    /* decode literals and length/distances until end-of-block or not enough
+       input data or output space */
+    do {
+        if (bits < 15) {
+            hold += (unsigned long)(*in++) << bits;
+            bits += 8;
+            hold += (unsigned long)(*in++) << bits;
+            bits += 8;
+        }
+        here = lcode[hold & lmask];
+      dolen:
+        op = (unsigned)(here.bits);
+        hold >>= op;
+        bits -= op;
+        op = (unsigned)(here.op);
+        if (op == 0) {                          /* literal */
+            Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
+                    "inflate:         literal '%c'\n" :
+                    "inflate:         literal 0x%02x\n", here.val));
+            *out++ = (unsigned char)(here.val);
+        }
+        else if (op & 16) {                     /* length base */
+            len = (unsigned)(here.val);
+            op &= 15;                           /* number of extra bits */
+            if (op) {
+                if (bits < op) {
+                    hold += (unsigned long)(*in++) << bits;
+                    bits += 8;
+                }
+                len += (unsigned)hold & ((1U << op) - 1);
+                hold >>= op;
+                bits -= op;
+            }
+            Tracevv((stderr, "inflate:         length %u\n", len));
+            if (bits < 15) {
+                hold += (unsigned long)(*in++) << bits;
+                bits += 8;
+                hold += (unsigned long)(*in++) << bits;
+                bits += 8;
+            }
+            here = dcode[hold & dmask];
+          dodist:
+            op = (unsigned)(here.bits);
+            hold >>= op;
+            bits -= op;
+            op = (unsigned)(here.op);
+            if (op & 16) {                      /* distance base */
+                dist = (unsigned)(here.val);
+                op &= 15;                       /* number of extra bits */
+                if (bits < op) {
+                    hold += (unsigned long)(*in++) << bits;
+                    bits += 8;
+                    if (bits < op) {
+                        hold += (unsigned long)(*in++) << bits;
+                        bits += 8;
+                    }
+                }
+                dist += (unsigned)hold & ((1U << op) - 1);
+#ifdef INFLATE_STRICT
+                if (dist > dmax) {
+                    strm->msg = (char *)"invalid distance too far back";
+                    state->mode = BAD;
+                    break;
+                }
+#endif
+                hold >>= op;
+                bits -= op;
+                Tracevv((stderr, "inflate:         distance %u\n", dist));
+                op = (unsigned)(out - beg);     /* max distance in output */
+                if (dist > op) {                /* see if copy from window */
+                    op = dist - op;             /* distance back in window */
+                    if (op > whave) {
+                        if (state->sane) {
+                            strm->msg =
+                                (char *)"invalid distance too far back";
+                            state->mode = BAD;
+                            break;
+                        }
+#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+                        if (len <= op - whave) {
+                            do {
+                                *out++ = 0;
+                            } while (--len);
+                            continue;
+                        }
+                        len -= op - whave;
+                        do {
+                            *out++ = 0;
+                        } while (--op > whave);
+                        if (op == 0) {
+                            from = out - dist;
+                            do {
+                                *out++ = *from++;
+                            } while (--len);
+                            continue;
+                        }
+#endif
+                    }
+                    from = window;
+                    if (wnext == 0) {           /* very common case */
+                        from += wsize - op;
+                        if (op < len) {         /* some from window */
+                            len -= op;
+                            do {
+                                *out++ = *from++;
+                            } while (--op);
+                            from = out - dist;  /* rest from output */
+                        }
+                    }
+                    else if (wnext < op) {      /* wrap around window */
+                        from += wsize + wnext - op;
+                        op -= wnext;
+                        if (op < len) {         /* some from end of window */
+                            len -= op;
+                            do {
+                                *out++ = *from++;
+                            } while (--op);
+                            from = window;
+                            if (wnext < len) {  /* some from start of window */
+                                op = wnext;
+                                len -= op;
+                                do {
+                                    *out++ = *from++;
+                                } while (--op);
+                                from = out - dist;      /* rest from output */
+                            }
+                        }
+                    }
+                    else {                      /* contiguous in window */
+                        from += wnext - op;
+                        if (op < len) {         /* some from window */
+                            len -= op;
+                            do {
+                                *out++ = *from++;
+                            } while (--op);
+                            from = out - dist;  /* rest from output */
+                        }
+                    }
+                    while (len > 2) {
+                        *out++ = *from++;
+                        *out++ = *from++;
+                        *out++ = *from++;
+                        len -= 3;
+                    }
+                    if (len) {
+                        *out++ = *from++;
+                        if (len > 1)
+                            *out++ = *from++;
+                    }
+                }
+                else {
+                    from = out - dist;          /* copy direct from output */
+                    do {                        /* minimum length is three */
+                        *out++ = *from++;
+                        *out++ = *from++;
+                        *out++ = *from++;
+                        len -= 3;
+                    } while (len > 2);
+                    if (len) {
+                        *out++ = *from++;
+                        if (len > 1)
+                            *out++ = *from++;
+                    }
+                }
+            }
+            else if ((op & 64) == 0) {          /* 2nd level distance code */
+                here = dcode[here.val + (hold & ((1U << op) - 1))];
+                goto dodist;
+            }
+            else {
+                strm->msg = (char *)"invalid distance code";
+                state->mode = BAD;
+                break;
+            }
+        }
+        else if ((op & 64) == 0) {              /* 2nd level length code */
+            here = lcode[here.val + (hold & ((1U << op) - 1))];
+            goto dolen;
+        }
+        else if (op & 32) {                     /* end-of-block */
+            Tracevv((stderr, "inflate:         end of block\n"));
+            state->mode = TYPE;
+            break;
+        }
+        else {
+            strm->msg = (char *)"invalid literal/length code";
+            state->mode = BAD;
+            break;
+        }
+    } while (in < last && out < end);
+
+    /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
+    len = bits >> 3;
+    in -= len;
+    bits -= len << 3;
+    hold &= (1U << bits) - 1;
+
+    /* update state and return */
+    strm->next_in = in;
+    strm->next_out = out;
+    strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last));
+    strm->avail_out = (unsigned)(out < end ?
+                                 257 + (end - out) : 257 - (out - end));
+    state->hold = hold;
+    state->bits = bits;
+    return;
+}
+
+/*
+   inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):
+   - Using bit fields for code structure
+   - Different op definition to avoid & for extra bits (do & for table bits)
+   - Three separate decoding do-loops for direct, window, and wnext == 0
+   - Special case for distance > 1 copies to do overlapped load and store copy
+   - Explicit branch predictions (based on measured branch probabilities)
+   - Deferring match copy and interspersed it with decoding subsequent codes
+   - Swapping literal/length else
+   - Swapping window/direct else
+   - Larger unrolled copy loops (three is about right)
+   - Moving len -= 3 statement into middle of loop
+ */
+
+#endif /* !ASMINF */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/inffast.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,11 @@
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995-2003, 2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/inffixed.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,94 @@
+    /* inffixed.h -- table for decoding fixed codes
+     * Generated automatically by makefixed().
+     */
+
+    /* WARNING: this file should *not* be used by applications.
+       It is part of the implementation of this library and is
+       subject to change. Applications should only use zlib.h.
+     */
+
+    static const code lenfix[512] = {
+        {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48},
+        {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128},
+        {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59},
+        {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176},
+        {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20},
+        {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100},
+        {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8},
+        {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216},
+        {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76},
+        {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114},
+        {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},
+        {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148},
+        {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42},
+        {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86},
+        {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15},
+        {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236},
+        {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62},
+        {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},
+        {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31},
+        {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162},
+        {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25},
+        {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105},
+        {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4},
+        {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202},
+        {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69},
+        {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125},
+        {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13},
+        {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195},
+        {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35},
+        {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91},
+        {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19},
+        {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246},
+        {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55},
+        {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135},
+        {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99},
+        {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190},
+        {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16},
+        {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96},
+        {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6},
+        {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209},
+        {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},
+        {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116},
+        {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4},
+        {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153},
+        {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44},
+        {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82},
+        {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11},
+        {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},
+        {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58},
+        {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138},
+        {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51},
+        {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173},
+        {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30},
+        {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110},
+        {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0},
+        {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195},
+        {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65},
+        {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121},
+        {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},
+        {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258},
+        {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37},
+        {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93},
+        {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23},
+        {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251},
+        {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51},
+        {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},
+        {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67},
+        {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183},
+        {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23},
+        {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103},
+        {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9},
+        {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223},
+        {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79},
+        {0,9,255}
+    };
+
+    static const code distfix[32] = {
+        {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025},
+        {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193},
+        {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385},
+        {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577},
+        {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073},
+        {22,5,193},{64,5,0}
+    };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/inflate.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,1561 @@
+/* inflate.c -- zlib decompression
+ * Copyright (C) 1995-2016 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * Change history:
+ *
+ * 1.2.beta0    24 Nov 2002
+ * - First version -- complete rewrite of inflate to simplify code, avoid
+ *   creation of window when not needed, minimize use of window when it is
+ *   needed, make inffast.c even faster, implement gzip decoding, and to
+ *   improve code readability and style over the previous zlib inflate code
+ *
+ * 1.2.beta1    25 Nov 2002
+ * - Use pointers for available input and output checking in inffast.c
+ * - Remove input and output counters in inffast.c
+ * - Change inffast.c entry and loop from avail_in >= 7 to >= 6
+ * - Remove unnecessary second byte pull from length extra in inffast.c
+ * - Unroll direct copy to three copies per loop in inffast.c
+ *
+ * 1.2.beta2    4 Dec 2002
+ * - Change external routine names to reduce potential conflicts
+ * - Correct filename to inffixed.h for fixed tables in inflate.c
+ * - Make hbuf[] unsigned char to match parameter type in inflate.c
+ * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset)
+ *   to avoid negation problem on Alphas (64 bit) in inflate.c
+ *
+ * 1.2.beta3    22 Dec 2002
+ * - Add comments on state->bits assertion in inffast.c
+ * - Add comments on op field in inftrees.h
+ * - Fix bug in reuse of allocated window after inflateReset()
+ * - Remove bit fields--back to byte structure for speed
+ * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths
+ * - Change post-increments to pre-increments in inflate_fast(), PPC biased?
+ * - Add compile time option, POSTINC, to use post-increments instead (Intel?)
+ * - Make MATCH copy in inflate() much faster for when inflate_fast() not used
+ * - Use local copies of stream next and avail values, as well as local bit
+ *   buffer and bit count in inflate()--for speed when inflate_fast() not used
+ *
+ * 1.2.beta4    1 Jan 2003
+ * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings
+ * - Move a comment on output buffer sizes from inffast.c to inflate.c
+ * - Add comments in inffast.c to introduce the inflate_fast() routine
+ * - Rearrange window copies in inflate_fast() for speed and simplification
+ * - Unroll last copy for window match in inflate_fast()
+ * - Use local copies of window variables in inflate_fast() for speed
+ * - Pull out common wnext == 0 case for speed in inflate_fast()
+ * - Make op and len in inflate_fast() unsigned for consistency
+ * - Add FAR to lcode and dcode declarations in inflate_fast()
+ * - Simplified bad distance check in inflate_fast()
+ * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new
+ *   source file infback.c to provide a call-back interface to inflate for
+ *   programs like gzip and unzip -- uses window as output buffer to avoid
+ *   window copying
+ *
+ * 1.2.beta5    1 Jan 2003
+ * - Improved inflateBack() interface to allow the caller to provide initial
+ *   input in strm.
+ * - Fixed stored blocks bug in inflateBack()
+ *
+ * 1.2.beta6    4 Jan 2003
+ * - Added comments in inffast.c on effectiveness of POSTINC
+ * - Typecasting all around to reduce compiler warnings
+ * - Changed loops from while (1) or do {} while (1) to for (;;), again to
+ *   make compilers happy
+ * - Changed type of window in inflateBackInit() to unsigned char *
+ *
+ * 1.2.beta7    27 Jan 2003
+ * - Changed many types to unsigned or unsigned short to avoid warnings
+ * - Added inflateCopy() function
+ *
+ * 1.2.0        9 Mar 2003
+ * - Changed inflateBack() interface to provide separate opaque descriptors
+ *   for the in() and out() functions
+ * - Changed inflateBack() argument and in_func typedef to swap the length
+ *   and buffer address return values for the input function
+ * - Check next_in and next_out for Z_NULL on entry to inflate()
+ *
+ * The history for versions after 1.2.0 are in ChangeLog in zlib distribution.
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+#ifdef MAKEFIXED
+#  ifndef BUILDFIXED
+#    define BUILDFIXED
+#  endif
+#endif
+
+/* function prototypes */
+local int inflateStateCheck OF((z_streamp strm));
+local void fixedtables OF((struct inflate_state FAR *state));
+local int updatewindow OF((z_streamp strm, const unsigned char FAR *end,
+                           unsigned copy));
+#ifdef BUILDFIXED
+   void makefixed OF((void));
+#endif
+local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf,
+                              unsigned len));
+
+local int inflateStateCheck(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+    if (strm == Z_NULL ||
+        strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0)
+        return 1;
+    state = (struct inflate_state FAR *)strm->state;
+    if (state == Z_NULL || state->strm != strm ||
+        state->mode < HEAD || state->mode > SYNC)
+        return 1;
+    return 0;
+}
+
+int ZEXPORT inflateResetKeep(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    strm->total_in = strm->total_out = state->total = 0;
+    strm->msg = Z_NULL;
+    if (state->wrap)        /* to support ill-conceived Java test suite */
+        strm->adler = state->wrap & 1;
+    state->mode = HEAD;
+    state->last = 0;
+    state->havedict = 0;
+    state->dmax = 32768U;
+    state->head = Z_NULL;
+    state->hold = 0;
+    state->bits = 0;
+    state->lencode = state->distcode = state->next = state->codes;
+    state->sane = 1;
+    state->back = -1;
+    Tracev((stderr, "inflate: reset\n"));
+    return Z_OK;
+}
+
+int ZEXPORT inflateReset(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    state->wsize = 0;
+    state->whave = 0;
+    state->wnext = 0;
+    return inflateResetKeep(strm);
+}
+
+int ZEXPORT inflateReset2(strm, windowBits)
+z_streamp strm;
+int windowBits;
+{
+    int wrap;
+    struct inflate_state FAR *state;
+
+    /* get the state */
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+
+    /* extract wrap request from windowBits parameter */
+    if (windowBits < 0) {
+        wrap = 0;
+        windowBits = -windowBits;
+    }
+    else {
+        wrap = (windowBits >> 4) + 5;
+#ifdef GUNZIP
+        if (windowBits < 48)
+            windowBits &= 15;
+#endif
+    }
+
+    /* set number of window bits, free window if different */
+    if (windowBits && (windowBits < 8 || windowBits > 15))
+        return Z_STREAM_ERROR;
+    if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) {
+        ZFREE(strm, state->window);
+        state->window = Z_NULL;
+    }
+
+    /* update state and reset the rest of it */
+    state->wrap = wrap;
+    state->wbits = (unsigned)windowBits;
+    return inflateReset(strm);
+}
+
+int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)
+z_streamp strm;
+int windowBits;
+const char *version;
+int stream_size;
+{
+    int ret;
+    struct inflate_state FAR *state;
+
+    if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+        stream_size != (int)(sizeof(z_stream)))
+        return Z_VERSION_ERROR;
+    if (strm == Z_NULL) return Z_STREAM_ERROR;
+    strm->msg = Z_NULL;                 /* in case we return an error */
+    if (strm->zalloc == (alloc_func)0) {
+#ifdef Z_SOLO
+        return Z_STREAM_ERROR;
+#else
+        strm->zalloc = zcalloc;
+        strm->opaque = (voidpf)0;
+#endif
+    }
+    if (strm->zfree == (free_func)0)
+#ifdef Z_SOLO
+        return Z_STREAM_ERROR;
+#else
+        strm->zfree = zcfree;
+#endif
+    state = (struct inflate_state FAR *)
+            ZALLOC(strm, 1, sizeof(struct inflate_state));
+    if (state == Z_NULL) return Z_MEM_ERROR;
+    Tracev((stderr, "inflate: allocated\n"));
+    strm->state = (struct internal_state FAR *)state;
+    state->strm = strm;
+    state->window = Z_NULL;
+    state->mode = HEAD;     /* to pass state test in inflateReset2() */
+    ret = inflateReset2(strm, windowBits);
+    if (ret != Z_OK) {
+        ZFREE(strm, state);
+        strm->state = Z_NULL;
+    }
+    return ret;
+}
+
+int ZEXPORT inflateInit_(strm, version, stream_size)
+z_streamp strm;
+const char *version;
+int stream_size;
+{
+    return inflateInit2_(strm, DEF_WBITS, version, stream_size);
+}
+
+int ZEXPORT inflatePrime(strm, bits, value)
+z_streamp strm;
+int bits;
+int value;
+{
+    struct inflate_state FAR *state;
+
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if (bits < 0) {
+        state->hold = 0;
+        state->bits = 0;
+        return Z_OK;
+    }
+    if (bits > 16 || state->bits + (uInt)bits > 32) return Z_STREAM_ERROR;
+    value &= (1L << bits) - 1;
+    state->hold += (unsigned)value << state->bits;
+    state->bits += (uInt)bits;
+    return Z_OK;
+}
+
+/*
+   Return state with length and distance decoding tables and index sizes set to
+   fixed code decoding.  Normally this returns fixed tables from inffixed.h.
+   If BUILDFIXED is defined, then instead this routine builds the tables the
+   first time it's called, and returns those tables the first time and
+   thereafter.  This reduces the size of the code by about 2K bytes, in
+   exchange for a little execution time.  However, BUILDFIXED should not be
+   used for threaded applications, since the rewriting of the tables and virgin
+   may not be thread-safe.
+ */
+local void fixedtables(state)
+struct inflate_state FAR *state;
+{
+#ifdef BUILDFIXED
+    static int virgin = 1;
+    static code *lenfix, *distfix;
+    static code fixed[544];
+
+    /* build fixed huffman tables if first call (may not be thread safe) */
+    if (virgin) {
+        unsigned sym, bits;
+        static code *next;
+
+        /* literal/length table */
+        sym = 0;
+        while (sym < 144) state->lens[sym++] = 8;
+        while (sym < 256) state->lens[sym++] = 9;
+        while (sym < 280) state->lens[sym++] = 7;
+        while (sym < 288) state->lens[sym++] = 8;
+        next = fixed;
+        lenfix = next;
+        bits = 9;
+        inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
+
+        /* distance table */
+        sym = 0;
+        while (sym < 32) state->lens[sym++] = 5;
+        distfix = next;
+        bits = 5;
+        inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
+
+        /* do this just once */
+        virgin = 0;
+    }
+#else /* !BUILDFIXED */
+#   include "inffixed.h"
+#endif /* BUILDFIXED */
+    state->lencode = lenfix;
+    state->lenbits = 9;
+    state->distcode = distfix;
+    state->distbits = 5;
+}
+
+#ifdef MAKEFIXED
+#include <stdio.h>
+
+/*
+   Write out the inffixed.h that is #include'd above.  Defining MAKEFIXED also
+   defines BUILDFIXED, so the tables are built on the fly.  makefixed() writes
+   those tables to stdout, which would be piped to inffixed.h.  A small program
+   can simply call makefixed to do this:
+
+    void makefixed(void);
+
+    int main(void)
+    {
+        makefixed();
+        return 0;
+    }
+
+   Then that can be linked with zlib built with MAKEFIXED defined and run:
+
+    a.out > inffixed.h
+ */
+void makefixed()
+{
+    unsigned low, size;
+    struct inflate_state state;
+
+    fixedtables(&state);
+    puts("    /* inffixed.h -- table for decoding fixed codes");
+    puts("     * Generated automatically by makefixed().");
+    puts("     */");
+    puts("");
+    puts("    /* WARNING: this file should *not* be used by applications.");
+    puts("       It is part of the implementation of this library and is");
+    puts("       subject to change. Applications should only use zlib.h.");
+    puts("     */");
+    puts("");
+    size = 1U << 9;
+    printf("    static const code lenfix[%u] = {", size);
+    low = 0;
+    for (;;) {
+        if ((low % 7) == 0) printf("\n        ");
+        printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op,
+               state.lencode[low].bits, state.lencode[low].val);
+        if (++low == size) break;
+        putchar(',');
+    }
+    puts("\n    };");
+    size = 1U << 5;
+    printf("\n    static const code distfix[%u] = {", size);
+    low = 0;
+    for (;;) {
+        if ((low % 6) == 0) printf("\n        ");
+        printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits,
+               state.distcode[low].val);
+        if (++low == size) break;
+        putchar(',');
+    }
+    puts("\n    };");
+}
+#endif /* MAKEFIXED */
+
+/*
+   Update the window with the last wsize (normally 32K) bytes written before
+   returning.  If window does not exist yet, create it.  This is only called
+   when a window is already in use, or when output has been written during this
+   inflate call, but the end of the deflate stream has not been reached yet.
+   It is also called to create a window for dictionary data when a dictionary
+   is loaded.
+
+   Providing output buffers larger than 32K to inflate() should provide a speed
+   advantage, since only the last 32K of output is copied to the sliding window
+   upon return from inflate(), and since all distances after the first 32K of
+   output will fall in the output data, making match copies simpler and faster.
+   The advantage may be dependent on the size of the processor's data caches.
+ */
+local int updatewindow(strm, end, copy)
+z_streamp strm;
+const Bytef *end;
+unsigned copy;
+{
+    struct inflate_state FAR *state;
+    unsigned dist;
+
+    state = (struct inflate_state FAR *)strm->state;
+
+    /* if it hasn't been done already, allocate space for the window */
+    if (state->window == Z_NULL) {
+        state->window = (unsigned char FAR *)
+                        ZALLOC(strm, 1U << state->wbits,
+                               sizeof(unsigned char));
+        if (state->window == Z_NULL) return 1;
+    }
+
+    /* if window not in use yet, initialize */
+    if (state->wsize == 0) {
+        state->wsize = 1U << state->wbits;
+        state->wnext = 0;
+        state->whave = 0;
+    }
+
+    /* copy state->wsize or less output bytes into the circular window */
+    if (copy >= state->wsize) {
+        zmemcpy(state->window, end - state->wsize, state->wsize);
+        state->wnext = 0;
+        state->whave = state->wsize;
+    }
+    else {
+        dist = state->wsize - state->wnext;
+        if (dist > copy) dist = copy;
+        zmemcpy(state->window + state->wnext, end - copy, dist);
+        copy -= dist;
+        if (copy) {
+            zmemcpy(state->window, end - copy, copy);
+            state->wnext = copy;
+            state->whave = state->wsize;
+        }
+        else {
+            state->wnext += dist;
+            if (state->wnext == state->wsize) state->wnext = 0;
+            if (state->whave < state->wsize) state->whave += dist;
+        }
+    }
+    return 0;
+}
+
+/* Macros for inflate(): */
+
+/* check function to use adler32() for zlib or crc32() for gzip */
+#ifdef GUNZIP
+#  define UPDATE(check, buf, len) \
+    (state->flags ? crc32(check, buf, len) : adler32(check, buf, len))
+#else
+#  define UPDATE(check, buf, len) adler32(check, buf, len)
+#endif
+
+/* check macros for header crc */
+#ifdef GUNZIP
+#  define CRC2(check, word) \
+    do { \
+        hbuf[0] = (unsigned char)(word); \
+        hbuf[1] = (unsigned char)((word) >> 8); \
+        check = crc32(check, hbuf, 2); \
+    } while (0)
+
+#  define CRC4(check, word) \
+    do { \
+        hbuf[0] = (unsigned char)(word); \
+        hbuf[1] = (unsigned char)((word) >> 8); \
+        hbuf[2] = (unsigned char)((word) >> 16); \
+        hbuf[3] = (unsigned char)((word) >> 24); \
+        check = crc32(check, hbuf, 4); \
+    } while (0)
+#endif
+
+/* Load registers with state in inflate() for speed */
+#define LOAD() \
+    do { \
+        put = strm->next_out; \
+        left = strm->avail_out; \
+        next = strm->next_in; \
+        have = strm->avail_in; \
+        hold = state->hold; \
+        bits = state->bits; \
+    } while (0)
+
+/* Restore state from registers in inflate() */
+#define RESTORE() \
+    do { \
+        strm->next_out = put; \
+        strm->avail_out = left; \
+        strm->next_in = next; \
+        strm->avail_in = have; \
+        state->hold = hold; \
+        state->bits = bits; \
+    } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+    do { \
+        hold = 0; \
+        bits = 0; \
+    } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflate()
+   if there is no input available. */
+#define PULLBYTE() \
+    do { \
+        if (have == 0) goto inf_leave; \
+        have--; \
+        hold += (unsigned long)(*next++) << bits; \
+        bits += 8; \
+    } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator.  If there is
+   not enough available input to do that, then return from inflate(). */
+#define NEEDBITS(n) \
+    do { \
+        while (bits < (unsigned)(n)) \
+            PULLBYTE(); \
+    } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+    ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+    do { \
+        hold >>= (n); \
+        bits -= (unsigned)(n); \
+    } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+    do { \
+        hold >>= bits & 7; \
+        bits -= bits & 7; \
+    } while (0)
+
+/*
+   inflate() uses a state machine to process as much input data and generate as
+   much output data as possible before returning.  The state machine is
+   structured roughly as follows:
+
+    for (;;) switch (state) {
+    ...
+    case STATEn:
+        if (not enough input data or output space to make progress)
+            return;
+        ... make progress ...
+        state = STATEm;
+        break;
+    ...
+    }
+
+   so when inflate() is called again, the same case is attempted again, and
+   if the appropriate resources are provided, the machine proceeds to the
+   next state.  The NEEDBITS() macro is usually the way the state evaluates
+   whether it can proceed or should return.  NEEDBITS() does the return if
+   the requested bits are not available.  The typical use of the BITS macros
+   is:
+
+        NEEDBITS(n);
+        ... do something with BITS(n) ...
+        DROPBITS(n);
+
+   where NEEDBITS(n) either returns from inflate() if there isn't enough
+   input left to load n bits into the accumulator, or it continues.  BITS(n)
+   gives the low n bits in the accumulator.  When done, DROPBITS(n) drops
+   the low n bits off the accumulator.  INITBITS() clears the accumulator
+   and sets the number of available bits to zero.  BYTEBITS() discards just
+   enough bits to put the accumulator on a byte boundary.  After BYTEBITS()
+   and a NEEDBITS(8), then BITS(8) would return the next byte in the stream.
+
+   NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return
+   if there is no input available.  The decoding of variable length codes uses
+   PULLBYTE() directly in order to pull just enough bytes to decode the next
+   code, and no more.
+
+   Some states loop until they get enough input, making sure that enough
+   state information is maintained to continue the loop where it left off
+   if NEEDBITS() returns in the loop.  For example, want, need, and keep
+   would all have to actually be part of the saved state in case NEEDBITS()
+   returns:
+
+    case STATEw:
+        while (want < need) {
+            NEEDBITS(n);
+            keep[want++] = BITS(n);
+            DROPBITS(n);
+        }
+        state = STATEx;
+    case STATEx:
+
+   As shown above, if the next state is also the next case, then the break
+   is omitted.
+
+   A state may also return if there is not enough output space available to
+   complete that state.  Those states are copying stored data, writing a
+   literal byte, and copying a matching string.
+
+   When returning, a "goto inf_leave" is used to update the total counters,
+   update the check value, and determine whether any progress has been made
+   during that inflate() call in order to return the proper return code.
+   Progress is defined as a change in either strm->avail_in or strm->avail_out.
+   When there is a window, goto inf_leave will update the window with the last
+   output written.  If a goto inf_leave occurs in the middle of decompression
+   and there is no window currently, goto inf_leave will create one and copy
+   output to the window for the next call of inflate().
+
+   In this implementation, the flush parameter of inflate() only affects the
+   return code (per zlib.h).  inflate() always writes as much as possible to
+   strm->next_out, given the space available and the provided input--the effect
+   documented in zlib.h of Z_SYNC_FLUSH.  Furthermore, inflate() always defers
+   the allocation of and copying into a sliding window until necessary, which
+   provides the effect documented in zlib.h for Z_FINISH when the entire input
+   stream available.  So the only thing the flush parameter actually does is:
+   when flush is set to Z_FINISH, inflate() cannot return Z_OK.  Instead it
+   will return Z_BUF_ERROR if it has not reached the end of the stream.
+ */
+
+int ZEXPORT inflate(strm, flush)
+z_streamp strm;
+int flush;
+{
+    struct inflate_state FAR *state;
+    z_const unsigned char FAR *next;    /* next input */
+    unsigned char FAR *put;     /* next output */
+    unsigned have, left;        /* available input and output */
+    unsigned long hold;         /* bit buffer */
+    unsigned bits;              /* bits in bit buffer */
+    unsigned in, out;           /* save starting available input and output */
+    unsigned copy;              /* number of stored or match bytes to copy */
+    unsigned char FAR *from;    /* where to copy match bytes from */
+    code here;                  /* current decoding table entry */
+    code last;                  /* parent table entry */
+    unsigned len;               /* length to copy for repeats, bits to drop */
+    int ret;                    /* return code */
+#ifdef GUNZIP
+    unsigned char hbuf[4];      /* buffer for gzip header crc calculation */
+#endif
+    static const unsigned short order[19] = /* permutation of code lengths */
+        {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+    if (inflateStateCheck(strm) || strm->next_out == Z_NULL ||
+        (strm->next_in == Z_NULL && strm->avail_in != 0))
+        return Z_STREAM_ERROR;
+
+    state = (struct inflate_state FAR *)strm->state;
+    if (state->mode == TYPE) state->mode = TYPEDO;      /* skip check */
+    LOAD();
+    in = have;
+    out = left;
+    ret = Z_OK;
+    for (;;)
+        switch (state->mode) {
+        case HEAD:
+            if (state->wrap == 0) {
+                state->mode = TYPEDO;
+                break;
+            }
+            NEEDBITS(16);
+#ifdef GUNZIP
+            if ((state->wrap & 2) && hold == 0x8b1f) {  /* gzip header */
+                if (state->wbits == 0)
+                    state->wbits = 15;
+                state->check = crc32(0L, Z_NULL, 0);
+                CRC2(state->check, hold);
+                INITBITS();
+                state->mode = FLAGS;
+                break;
+            }
+            state->flags = 0;           /* expect zlib header */
+            if (state->head != Z_NULL)
+                state->head->done = -1;
+            if (!(state->wrap & 1) ||   /* check if zlib header allowed */
+#else
+            if (
+#endif
+                ((BITS(8) << 8) + (hold >> 8)) % 31) {
+                strm->msg = (char *)"incorrect header check";
+                state->mode = BAD;
+                break;
+            }
+            if (BITS(4) != Z_DEFLATED) {
+                strm->msg = (char *)"unknown compression method";
+                state->mode = BAD;
+                break;
+            }
+            DROPBITS(4);
+            len = BITS(4) + 8;
+            if (state->wbits == 0)
+                state->wbits = len;
+            if (len > 15 || len > state->wbits) {
+                strm->msg = (char *)"invalid window size";
+                state->mode = BAD;
+                break;
+            }
+            state->dmax = 1U << len;
+            Tracev((stderr, "inflate:   zlib header ok\n"));
+            strm->adler = state->check = adler32(0L, Z_NULL, 0);
+            state->mode = hold & 0x200 ? DICTID : TYPE;
+            INITBITS();
+            break;
+#ifdef GUNZIP
+        case FLAGS:
+            NEEDBITS(16);
+            state->flags = (int)(hold);
+            if ((state->flags & 0xff) != Z_DEFLATED) {
+                strm->msg = (char *)"unknown compression method";
+                state->mode = BAD;
+                break;
+            }
+            if (state->flags & 0xe000) {
+                strm->msg = (char *)"unknown header flags set";
+                state->mode = BAD;
+                break;
+            }
+            if (state->head != Z_NULL)
+                state->head->text = (int)((hold >> 8) & 1);
+            if ((state->flags & 0x0200) && (state->wrap & 4))
+                CRC2(state->check, hold);
+            INITBITS();
+            state->mode = TIME;
+        case TIME:
+            NEEDBITS(32);
+            if (state->head != Z_NULL)
+                state->head->time = hold;
+            if ((state->flags & 0x0200) && (state->wrap & 4))
+                CRC4(state->check, hold);
+            INITBITS();
+            state->mode = OS;
+        case OS:
+            NEEDBITS(16);
+            if (state->head != Z_NULL) {
+                state->head->xflags = (int)(hold & 0xff);
+                state->head->os = (int)(hold >> 8);
+            }
+            if ((state->flags & 0x0200) && (state->wrap & 4))
+                CRC2(state->check, hold);
+            INITBITS();
+            state->mode = EXLEN;
+        case EXLEN:
+            if (state->flags & 0x0400) {
+                NEEDBITS(16);
+                state->length = (unsigned)(hold);
+                if (state->head != Z_NULL)
+                    state->head->extra_len = (unsigned)hold;
+                if ((state->flags & 0x0200) && (state->wrap & 4))
+                    CRC2(state->check, hold);
+                INITBITS();
+            }
+            else if (state->head != Z_NULL)
+                state->head->extra = Z_NULL;
+            state->mode = EXTRA;
+        case EXTRA:
+            if (state->flags & 0x0400) {
+                copy = state->length;
+                if (copy > have) copy = have;
+                if (copy) {
+                    if (state->head != Z_NULL &&
+                        state->head->extra != Z_NULL) {
+                        len = state->head->extra_len - state->length;
+                        zmemcpy(state->head->extra + len, next,
+                                len + copy > state->head->extra_max ?
+                                state->head->extra_max - len : copy);
+                    }
+                    if ((state->flags & 0x0200) && (state->wrap & 4))
+                        state->check = crc32(state->check, next, copy);
+                    have -= copy;
+                    next += copy;
+                    state->length -= copy;
+                }
+                if (state->length) goto inf_leave;
+            }
+            state->length = 0;
+            state->mode = NAME;
+        case NAME:
+            if (state->flags & 0x0800) {
+                if (have == 0) goto inf_leave;
+                copy = 0;
+                do {
+                    len = (unsigned)(next[copy++]);
+                    if (state->head != Z_NULL &&
+                            state->head->name != Z_NULL &&
+                            state->length < state->head->name_max)
+                        state->head->name[state->length++] = (Bytef)len;
+                } while (len && copy < have);
+                if ((state->flags & 0x0200) && (state->wrap & 4))
+                    state->check = crc32(state->check, next, copy);
+                have -= copy;
+                next += copy;
+                if (len) goto inf_leave;
+            }
+            else if (state->head != Z_NULL)
+                state->head->name = Z_NULL;
+            state->length = 0;
+            state->mode = COMMENT;
+        case COMMENT:
+            if (state->flags & 0x1000) {
+                if (have == 0) goto inf_leave;
+                copy = 0;
+                do {
+                    len = (unsigned)(next[copy++]);
+                    if (state->head != Z_NULL &&
+                            state->head->comment != Z_NULL &&
+                            state->length < state->head->comm_max)
+                        state->head->comment[state->length++] = (Bytef)len;
+                } while (len && copy < have);
+                if ((state->flags & 0x0200) && (state->wrap & 4))
+                    state->check = crc32(state->check, next, copy);
+                have -= copy;
+                next += copy;
+                if (len) goto inf_leave;
+            }
+            else if (state->head != Z_NULL)
+                state->head->comment = Z_NULL;
+            state->mode = HCRC;
+        case HCRC:
+            if (state->flags & 0x0200) {
+                NEEDBITS(16);
+                if ((state->wrap & 4) && hold != (state->check & 0xffff)) {
+                    strm->msg = (char *)"header crc mismatch";
+                    state->mode = BAD;
+                    break;
+                }
+                INITBITS();
+            }
+            if (state->head != Z_NULL) {
+                state->head->hcrc = (int)((state->flags >> 9) & 1);
+                state->head->done = 1;
+            }
+            strm->adler = state->check = crc32(0L, Z_NULL, 0);
+            state->mode = TYPE;
+            break;
+#endif
+        case DICTID:
+            NEEDBITS(32);
+            strm->adler = state->check = ZSWAP32(hold);
+            INITBITS();
+            state->mode = DICT;
+        case DICT:
+            if (state->havedict == 0) {
+                RESTORE();
+                return Z_NEED_DICT;
+            }
+            strm->adler = state->check = adler32(0L, Z_NULL, 0);
+            state->mode = TYPE;
+        case TYPE:
+            if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave;
+        case TYPEDO:
+            if (state->last) {
+                BYTEBITS();
+                state->mode = CHECK;
+                break;
+            }
+            NEEDBITS(3);
+            state->last = BITS(1);
+            DROPBITS(1);
+            switch (BITS(2)) {
+            case 0:                             /* stored block */
+                Tracev((stderr, "inflate:     stored block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = STORED;
+                break;
+            case 1:                             /* fixed block */
+                fixedtables(state);
+                Tracev((stderr, "inflate:     fixed codes block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = LEN_;             /* decode codes */
+                if (flush == Z_TREES) {
+                    DROPBITS(2);
+                    goto inf_leave;
+                }
+                break;
+            case 2:                             /* dynamic block */
+                Tracev((stderr, "inflate:     dynamic codes block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = TABLE;
+                break;
+            case 3:
+                strm->msg = (char *)"invalid block type";
+                state->mode = BAD;
+            }
+            DROPBITS(2);
+            break;
+        case STORED:
+            BYTEBITS();                         /* go to byte boundary */
+            NEEDBITS(32);
+            if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+                strm->msg = (char *)"invalid stored block lengths";
+                state->mode = BAD;
+                break;
+            }
+            state->length = (unsigned)hold & 0xffff;
+            Tracev((stderr, "inflate:       stored length %u\n",
+                    state->length));
+            INITBITS();
+            state->mode = COPY_;
+            if (flush == Z_TREES) goto inf_leave;
+        case COPY_:
+            state->mode = COPY;
+        case COPY:
+            copy = state->length;
+            if (copy) {
+                if (copy > have) copy = have;
+                if (copy > left) copy = left;
+                if (copy == 0) goto inf_leave;
+                zmemcpy(put, next, copy);
+                have -= copy;
+                next += copy;
+                left -= copy;
+                put += copy;
+                state->length -= copy;
+                break;
+            }
+            Tracev((stderr, "inflate:       stored end\n"));
+            state->mode = TYPE;
+            break;
+        case TABLE:
+            NEEDBITS(14);
+            state->nlen = BITS(5) + 257;
+            DROPBITS(5);
+            state->ndist = BITS(5) + 1;
+            DROPBITS(5);
+            state->ncode = BITS(4) + 4;
+            DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+            if (state->nlen > 286 || state->ndist > 30) {
+                strm->msg = (char *)"too many length or distance symbols";
+                state->mode = BAD;
+                break;
+            }
+#endif
+            Tracev((stderr, "inflate:       table sizes ok\n"));
+            state->have = 0;
+            state->mode = LENLENS;
+        case LENLENS:
+            while (state->have < state->ncode) {
+                NEEDBITS(3);
+                state->lens[order[state->have++]] = (unsigned short)BITS(3);
+                DROPBITS(3);
+            }
+            while (state->have < 19)
+                state->lens[order[state->have++]] = 0;
+            state->next = state->codes;
+            state->lencode = (const code FAR *)(state->next);
+            state->lenbits = 7;
+            ret = inflate_table(CODES, state->lens, 19, &(state->next),
+                                &(state->lenbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid code lengths set";
+                state->mode = BAD;
+                break;
+            }
+            Tracev((stderr, "inflate:       code lengths ok\n"));
+            state->have = 0;
+            state->mode = CODELENS;
+        case CODELENS:
+            while (state->have < state->nlen + state->ndist) {
+                for (;;) {
+                    here = state->lencode[BITS(state->lenbits)];
+                    if ((unsigned)(here.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                if (here.val < 16) {
+                    DROPBITS(here.bits);
+                    state->lens[state->have++] = here.val;
+                }
+                else {
+                    if (here.val == 16) {
+                        NEEDBITS(here.bits + 2);
+                        DROPBITS(here.bits);
+                        if (state->have == 0) {
+                            strm->msg = (char *)"invalid bit length repeat";
+                            state->mode = BAD;
+                            break;
+                        }
+                        len = state->lens[state->have - 1];
+                        copy = 3 + BITS(2);
+                        DROPBITS(2);
+                    }
+                    else if (here.val == 17) {
+                        NEEDBITS(here.bits + 3);
+                        DROPBITS(here.bits);
+                        len = 0;
+                        copy = 3 + BITS(3);
+                        DROPBITS(3);
+                    }
+                    else {
+                        NEEDBITS(here.bits + 7);
+                        DROPBITS(here.bits);
+                        len = 0;
+                        copy = 11 + BITS(7);
+                        DROPBITS(7);
+                    }
+                    if (state->have + copy > state->nlen + state->ndist) {
+                        strm->msg = (char *)"invalid bit length repeat";
+                        state->mode = BAD;
+                        break;
+                    }
+                    while (copy--)
+                        state->lens[state->have++] = (unsigned short)len;
+                }
+            }
+
+            /* handle error breaks in while */
+            if (state->mode == BAD) break;
+
+            /* check for end-of-block code (better have one) */
+            if (state->lens[256] == 0) {
+                strm->msg = (char *)"invalid code -- missing end-of-block";
+                state->mode = BAD;
+                break;
+            }
+
+            /* build code tables -- note: do not change the lenbits or distbits
+               values here (9 and 6) without reading the comments in inftrees.h
+               concerning the ENOUGH constants, which depend on those values */
+            state->next = state->codes;
+            state->lencode = (const code FAR *)(state->next);
+            state->lenbits = 9;
+            ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
+                                &(state->lenbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid literal/lengths set";
+                state->mode = BAD;
+                break;
+            }
+            state->distcode = (const code FAR *)(state->next);
+            state->distbits = 6;
+            ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+                            &(state->next), &(state->distbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid distances set";
+                state->mode = BAD;
+                break;
+            }
+            Tracev((stderr, "inflate:       codes ok\n"));
+            state->mode = LEN_;
+            if (flush == Z_TREES) goto inf_leave;
+        case LEN_:
+            state->mode = LEN;
+        case LEN:
+            if (have >= 6 && left >= 258) {
+                RESTORE();
+                inflate_fast(strm, out);
+                LOAD();
+                if (state->mode == TYPE)
+                    state->back = -1;
+                break;
+            }
+            state->back = 0;
+            for (;;) {
+                here = state->lencode[BITS(state->lenbits)];
+                if ((unsigned)(here.bits) <= bits) break;
+                PULLBYTE();
+            }
+            if (here.op && (here.op & 0xf0) == 0) {
+                last = here;
+                for (;;) {
+                    here = state->lencode[last.val +
+                            (BITS(last.bits + last.op) >> last.bits)];
+                    if ((unsigned)(last.bits + here.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                DROPBITS(last.bits);
+                state->back += last.bits;
+            }
+            DROPBITS(here.bits);
+            state->back += here.bits;
+            state->length = (unsigned)here.val;
+            if ((int)(here.op) == 0) {
+                Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
+                        "inflate:         literal '%c'\n" :
+                        "inflate:         literal 0x%02x\n", here.val));
+                state->mode = LIT;
+                break;
+            }
+            if (here.op & 32) {
+                Tracevv((stderr, "inflate:         end of block\n"));
+                state->back = -1;
+                state->mode = TYPE;
+                break;
+            }
+            if (here.op & 64) {
+                strm->msg = (char *)"invalid literal/length code";
+                state->mode = BAD;
+                break;
+            }
+            state->extra = (unsigned)(here.op) & 15;
+            state->mode = LENEXT;
+        case LENEXT:
+            if (state->extra) {
+                NEEDBITS(state->extra);
+                state->length += BITS(state->extra);
+                DROPBITS(state->extra);
+                state->back += state->extra;
+            }
+            Tracevv((stderr, "inflate:         length %u\n", state->length));
+            state->was = state->length;
+            state->mode = DIST;
+        case DIST:
+            for (;;) {
+                here = state->distcode[BITS(state->distbits)];
+                if ((unsigned)(here.bits) <= bits) break;
+                PULLBYTE();
+            }
+            if ((here.op & 0xf0) == 0) {
+                last = here;
+                for (;;) {
+                    here = state->distcode[last.val +
+                            (BITS(last.bits + last.op) >> last.bits)];
+                    if ((unsigned)(last.bits + here.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                DROPBITS(last.bits);
+                state->back += last.bits;
+            }
+            DROPBITS(here.bits);
+            state->back += here.bits;
+            if (here.op & 64) {
+                strm->msg = (char *)"invalid distance code";
+                state->mode = BAD;
+                break;
+            }
+            state->offset = (unsigned)here.val;
+            state->extra = (unsigned)(here.op) & 15;
+            state->mode = DISTEXT;
+        case DISTEXT:
+            if (state->extra) {
+                NEEDBITS(state->extra);
+                state->offset += BITS(state->extra);
+                DROPBITS(state->extra);
+                state->back += state->extra;
+            }
+#ifdef INFLATE_STRICT
+            if (state->offset > state->dmax) {
+                strm->msg = (char *)"invalid distance too far back";
+                state->mode = BAD;
+                break;
+            }
+#endif
+            Tracevv((stderr, "inflate:         distance %u\n", state->offset));
+            state->mode = MATCH;
+        case MATCH:
+            if (left == 0) goto inf_leave;
+            copy = out - left;
+            if (state->offset > copy) {         /* copy from window */
+                copy = state->offset - copy;
+                if (copy > state->whave) {
+                    if (state->sane) {
+                        strm->msg = (char *)"invalid distance too far back";
+                        state->mode = BAD;
+                        break;
+                    }
+#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+                    Trace((stderr, "inflate.c too far\n"));
+                    copy -= state->whave;
+                    if (copy > state->length) copy = state->length;
+                    if (copy > left) copy = left;
+                    left -= copy;
+                    state->length -= copy;
+                    do {
+                        *put++ = 0;
+                    } while (--copy);
+                    if (state->length == 0) state->mode = LEN;
+                    break;
+#endif
+                }
+                if (copy > state->wnext) {
+                    copy -= state->wnext;
+                    from = state->window + (state->wsize - copy);
+                }
+                else
+                    from = state->window + (state->wnext - copy);
+                if (copy > state->length) copy = state->length;
+            }
+            else {                              /* copy from output */
+                from = put - state->offset;
+                copy = state->length;
+            }
+            if (copy > left) copy = left;
+            left -= copy;
+            state->length -= copy;
+            do {
+                *put++ = *from++;
+            } while (--copy);
+            if (state->length == 0) state->mode = LEN;
+            break;
+        case LIT:
+            if (left == 0) goto inf_leave;
+            *put++ = (unsigned char)(state->length);
+            left--;
+            state->mode = LEN;
+            break;
+        case CHECK:
+            if (state->wrap) {
+                NEEDBITS(32);
+                out -= left;
+                strm->total_out += out;
+                state->total += out;
+                if ((state->wrap & 4) && out)
+                    strm->adler = state->check =
+                        UPDATE(state->check, put - out, out);
+                out = left;
+                if ((state->wrap & 4) && (
+#ifdef GUNZIP
+                     state->flags ? hold :
+#endif
+                     ZSWAP32(hold)) != state->check) {
+                    strm->msg = (char *)"incorrect data check";
+                    state->mode = BAD;
+                    break;
+                }
+                INITBITS();
+                Tracev((stderr, "inflate:   check matches trailer\n"));
+            }
+#ifdef GUNZIP
+            state->mode = LENGTH;
+        case LENGTH:
+            if (state->wrap && state->flags) {
+                NEEDBITS(32);
+                if (hold != (state->total & 0xffffffffUL)) {
+                    strm->msg = (char *)"incorrect length check";
+                    state->mode = BAD;
+                    break;
+                }
+                INITBITS();
+                Tracev((stderr, "inflate:   length matches trailer\n"));
+            }
+#endif
+            state->mode = DONE;
+        case DONE:
+            ret = Z_STREAM_END;
+            goto inf_leave;
+        case BAD:
+            ret = Z_DATA_ERROR;
+            goto inf_leave;
+        case MEM:
+            return Z_MEM_ERROR;
+        case SYNC:
+        default:
+            return Z_STREAM_ERROR;
+        }
+
+    /*
+       Return from inflate(), updating the total counts and the check value.
+       If there was no progress during the inflate() call, return a buffer
+       error.  Call updatewindow() to create and/or update the window state.
+       Note: a memory error from inflate() is non-recoverable.
+     */
+  inf_leave:
+    RESTORE();
+    if (state->wsize || (out != strm->avail_out && state->mode < BAD &&
+            (state->mode < CHECK || flush != Z_FINISH)))
+        if (updatewindow(strm, strm->next_out, out - strm->avail_out)) {
+            state->mode = MEM;
+            return Z_MEM_ERROR;
+        }
+    in -= strm->avail_in;
+    out -= strm->avail_out;
+    strm->total_in += in;
+    strm->total_out += out;
+    state->total += out;
+    if ((state->wrap & 4) && out)
+        strm->adler = state->check =
+            UPDATE(state->check, strm->next_out - out, out);
+    strm->data_type = (int)state->bits + (state->last ? 64 : 0) +
+                      (state->mode == TYPE ? 128 : 0) +
+                      (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0);
+    if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK)
+        ret = Z_BUF_ERROR;
+    return ret;
+}
+
+int ZEXPORT inflateEnd(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+    if (inflateStateCheck(strm))
+        return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if (state->window != Z_NULL) ZFREE(strm, state->window);
+    ZFREE(strm, strm->state);
+    strm->state = Z_NULL;
+    Tracev((stderr, "inflate: end\n"));
+    return Z_OK;
+}
+
+int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength)
+z_streamp strm;
+Bytef *dictionary;
+uInt *dictLength;
+{
+    struct inflate_state FAR *state;
+
+    /* check state */
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+
+    /* copy dictionary */
+    if (state->whave && dictionary != Z_NULL) {
+        zmemcpy(dictionary, state->window + state->wnext,
+                state->whave - state->wnext);
+        zmemcpy(dictionary + state->whave - state->wnext,
+                state->window, state->wnext);
+    }
+    if (dictLength != Z_NULL)
+        *dictLength = state->whave;
+    return Z_OK;
+}
+
+int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength)
+z_streamp strm;
+const Bytef *dictionary;
+uInt dictLength;
+{
+    struct inflate_state FAR *state;
+    unsigned long dictid;
+    int ret;
+
+    /* check state */
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if (state->wrap != 0 && state->mode != DICT)
+        return Z_STREAM_ERROR;
+
+    /* check for correct dictionary identifier */
+    if (state->mode == DICT) {
+        dictid = adler32(0L, Z_NULL, 0);
+        dictid = adler32(dictid, dictionary, dictLength);
+        if (dictid != state->check)
+            return Z_DATA_ERROR;
+    }
+
+    /* copy dictionary to window using updatewindow(), which will amend the
+       existing dictionary if appropriate */
+    ret = updatewindow(strm, dictionary + dictLength, dictLength);
+    if (ret) {
+        state->mode = MEM;
+        return Z_MEM_ERROR;
+    }
+    state->havedict = 1;
+    Tracev((stderr, "inflate:   dictionary set\n"));
+    return Z_OK;
+}
+
+int ZEXPORT inflateGetHeader(strm, head)
+z_streamp strm;
+gz_headerp head;
+{
+    struct inflate_state FAR *state;
+
+    /* check state */
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if ((state->wrap & 2) == 0) return Z_STREAM_ERROR;
+
+    /* save header structure */
+    state->head = head;
+    head->done = 0;
+    return Z_OK;
+}
+
+/*
+   Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff.  Return when found
+   or when out of input.  When called, *have is the number of pattern bytes
+   found in order so far, in 0..3.  On return *have is updated to the new
+   state.  If on return *have equals four, then the pattern was found and the
+   return value is how many bytes were read including the last byte of the
+   pattern.  If *have is less than four, then the pattern has not been found
+   yet and the return value is len.  In the latter case, syncsearch() can be
+   called again with more data and the *have state.  *have is initialized to
+   zero for the first call.
+ */
+local unsigned syncsearch(have, buf, len)
+unsigned FAR *have;
+const unsigned char FAR *buf;
+unsigned len;
+{
+    unsigned got;
+    unsigned next;
+
+    got = *have;
+    next = 0;
+    while (next < len && got < 4) {
+        if ((int)(buf[next]) == (got < 2 ? 0 : 0xff))
+            got++;
+        else if (buf[next])
+            got = 0;
+        else
+            got = 4 - got;
+        next++;
+    }
+    *have = got;
+    return next;
+}
+
+int ZEXPORT inflateSync(strm)
+z_streamp strm;
+{
+    unsigned len;               /* number of bytes to look at or looked at */
+    unsigned long in, out;      /* temporary to save total_in and total_out */
+    unsigned char buf[4];       /* to restore bit buffer to byte string */
+    struct inflate_state FAR *state;
+
+    /* check parameters */
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR;
+
+    /* if first time, start search in bit buffer */
+    if (state->mode != SYNC) {
+        state->mode = SYNC;
+        state->hold <<= state->bits & 7;
+        state->bits -= state->bits & 7;
+        len = 0;
+        while (state->bits >= 8) {
+            buf[len++] = (unsigned char)(state->hold);
+            state->hold >>= 8;
+            state->bits -= 8;
+        }
+        state->have = 0;
+        syncsearch(&(state->have), buf, len);
+    }
+
+    /* search available input */
+    len = syncsearch(&(state->have), strm->next_in, strm->avail_in);
+    strm->avail_in -= len;
+    strm->next_in += len;
+    strm->total_in += len;
+
+    /* return no joy or set up to restart inflate() on a new block */
+    if (state->have != 4) return Z_DATA_ERROR;
+    in = strm->total_in;  out = strm->total_out;
+    inflateReset(strm);
+    strm->total_in = in;  strm->total_out = out;
+    state->mode = TYPE;
+    return Z_OK;
+}
+
+/*
+   Returns true if inflate is currently at the end of a block generated by
+   Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
+   implementation to provide an additional safety check. PPP uses
+   Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored
+   block. When decompressing, PPP checks that at the end of input packet,
+   inflate is waiting for these length bytes.
+ */
+int ZEXPORT inflateSyncPoint(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    return state->mode == STORED && state->bits == 0;
+}
+
+int ZEXPORT inflateCopy(dest, source)
+z_streamp dest;
+z_streamp source;
+{
+    struct inflate_state FAR *state;
+    struct inflate_state FAR *copy;
+    unsigned char FAR *window;
+    unsigned wsize;
+
+    /* check input */
+    if (inflateStateCheck(source) || dest == Z_NULL)
+        return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)source->state;
+
+    /* allocate space */
+    copy = (struct inflate_state FAR *)
+           ZALLOC(source, 1, sizeof(struct inflate_state));
+    if (copy == Z_NULL) return Z_MEM_ERROR;
+    window = Z_NULL;
+    if (state->window != Z_NULL) {
+        window = (unsigned char FAR *)
+                 ZALLOC(source, 1U << state->wbits, sizeof(unsigned char));
+        if (window == Z_NULL) {
+            ZFREE(source, copy);
+            return Z_MEM_ERROR;
+        }
+    }
+
+    /* copy state */
+    zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream));
+    zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state));
+    copy->strm = dest;
+    if (state->lencode >= state->codes &&
+        state->lencode <= state->codes + ENOUGH - 1) {
+        copy->lencode = copy->codes + (state->lencode - state->codes);
+        copy->distcode = copy->codes + (state->distcode - state->codes);
+    }
+    copy->next = copy->codes + (state->next - state->codes);
+    if (window != Z_NULL) {
+        wsize = 1U << state->wbits;
+        zmemcpy(window, state->window, wsize);
+    }
+    copy->window = window;
+    dest->state = (struct internal_state FAR *)copy;
+    return Z_OK;
+}
+
+int ZEXPORT inflateUndermine(strm, subvert)
+z_streamp strm;
+int subvert;
+{
+    struct inflate_state FAR *state;
+
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+    state->sane = !subvert;
+    return Z_OK;
+#else
+    (void)subvert;
+    state->sane = 1;
+    return Z_DATA_ERROR;
+#endif
+}
+
+int ZEXPORT inflateValidate(strm, check)
+z_streamp strm;
+int check;
+{
+    struct inflate_state FAR *state;
+
+    if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if (check)
+        state->wrap |= 4;
+    else
+        state->wrap &= ~4;
+    return Z_OK;
+}
+
+long ZEXPORT inflateMark(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+
+    if (inflateStateCheck(strm))
+        return -(1L << 16);
+    state = (struct inflate_state FAR *)strm->state;
+    return (long)(((unsigned long)((long)state->back)) << 16) +
+        (state->mode == COPY ? state->length :
+            (state->mode == MATCH ? state->was - state->length : 0));
+}
+
+unsigned long ZEXPORT inflateCodesUsed(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+    if (inflateStateCheck(strm)) return (unsigned long)-1;
+    state = (struct inflate_state FAR *)strm->state;
+    return (unsigned long)(state->next - state->codes);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/inflate.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,125 @@
+/* inflate.h -- internal inflate state definition
+ * Copyright (C) 1995-2016 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+   trailer decoding by inflate().  NO_GZIP would be used to avoid linking in
+   the crc code when it is not needed.  For shared libraries, gzip decoding
+   should be left enabled. */
+#ifndef NO_GZIP
+#  define GUNZIP
+#endif
+
+/* Possible inflate modes between inflate() calls */
+typedef enum {
+    HEAD = 16180,   /* i: waiting for magic header */
+    FLAGS,      /* i: waiting for method and flags (gzip) */
+    TIME,       /* i: waiting for modification time (gzip) */
+    OS,         /* i: waiting for extra flags and operating system (gzip) */
+    EXLEN,      /* i: waiting for extra length (gzip) */
+    EXTRA,      /* i: waiting for extra bytes (gzip) */
+    NAME,       /* i: waiting for end of file name (gzip) */
+    COMMENT,    /* i: waiting for end of comment (gzip) */
+    HCRC,       /* i: waiting for header crc (gzip) */
+    DICTID,     /* i: waiting for dictionary check value */
+    DICT,       /* waiting for inflateSetDictionary() call */
+        TYPE,       /* i: waiting for type bits, including last-flag bit */
+        TYPEDO,     /* i: same, but skip check to exit inflate on new block */
+        STORED,     /* i: waiting for stored size (length and complement) */
+        COPY_,      /* i/o: same as COPY below, but only first time in */
+        COPY,       /* i/o: waiting for input or output to copy stored block */
+        TABLE,      /* i: waiting for dynamic block table lengths */
+        LENLENS,    /* i: waiting for code length code lengths */
+        CODELENS,   /* i: waiting for length/lit and distance code lengths */
+            LEN_,       /* i: same as LEN below, but only first time in */
+            LEN,        /* i: waiting for length/lit/eob code */
+            LENEXT,     /* i: waiting for length extra bits */
+            DIST,       /* i: waiting for distance code */
+            DISTEXT,    /* i: waiting for distance extra bits */
+            MATCH,      /* o: waiting for output space to copy string */
+            LIT,        /* o: waiting for output space to write literal */
+    CHECK,      /* i: waiting for 32-bit check value */
+    LENGTH,     /* i: waiting for 32-bit length (gzip) */
+    DONE,       /* finished check, done -- remain here until reset */
+    BAD,        /* got a data error -- remain here until reset */
+    MEM,        /* got an inflate() memory error -- remain here until reset */
+    SYNC        /* looking for synchronization bytes to restart inflate() */
+} inflate_mode;
+
+/*
+    State transitions between above modes -
+
+    (most modes can go to BAD or MEM on error -- not shown for clarity)
+
+    Process header:
+        HEAD -> (gzip) or (zlib) or (raw)
+        (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT ->
+                  HCRC -> TYPE
+        (zlib) -> DICTID or TYPE
+        DICTID -> DICT -> TYPE
+        (raw) -> TYPEDO
+    Read deflate blocks:
+            TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK
+            STORED -> COPY_ -> COPY -> TYPE
+            TABLE -> LENLENS -> CODELENS -> LEN_
+            LEN_ -> LEN
+    Read deflate codes in fixed or dynamic block:
+                LEN -> LENEXT or LIT or TYPE
+                LENEXT -> DIST -> DISTEXT -> MATCH -> LEN
+                LIT -> LEN
+    Process trailer:
+        CHECK -> LENGTH -> DONE
+ */
+
+/* State maintained between inflate() calls -- approximately 7K bytes, not
+   including the allocated sliding window, which is up to 32K bytes. */
+struct inflate_state {
+    z_streamp strm;             /* pointer back to this zlib stream */
+    inflate_mode mode;          /* current inflate mode */
+    int last;                   /* true if processing last block */
+    int wrap;                   /* bit 0 true for zlib, bit 1 true for gzip,
+                                   bit 2 true to validate check value */
+    int havedict;               /* true if dictionary provided */
+    int flags;                  /* gzip header method and flags (0 if zlib) */
+    unsigned dmax;              /* zlib header max distance (INFLATE_STRICT) */
+    unsigned long check;        /* protected copy of check value */
+    unsigned long total;        /* protected copy of output count */
+    gz_headerp head;            /* where to save gzip header information */
+        /* sliding window */
+    unsigned wbits;             /* log base 2 of requested window size */
+    unsigned wsize;             /* window size or zero if not using window */
+    unsigned whave;             /* valid bytes in the window */
+    unsigned wnext;             /* window write index */
+    unsigned char FAR *window;  /* allocated sliding window, if needed */
+        /* bit accumulator */
+    unsigned long hold;         /* input bit accumulator */
+    unsigned bits;              /* number of bits in "in" */
+        /* for string and stored block copying */
+    unsigned length;            /* literal or length of data to copy */
+    unsigned offset;            /* distance back to copy string from */
+        /* for table and code decoding */
+    unsigned extra;             /* extra bits needed */
+        /* fixed and dynamic code tables */
+    code const FAR *lencode;    /* starting table for length/literal codes */
+    code const FAR *distcode;   /* starting table for distance codes */
+    unsigned lenbits;           /* index bits for lencode */
+    unsigned distbits;          /* index bits for distcode */
+        /* dynamic table building */
+    unsigned ncode;             /* number of code length code lengths */
+    unsigned nlen;              /* number of length code lengths */
+    unsigned ndist;             /* number of distance code lengths */
+    unsigned have;              /* number of code lengths in lens[] */
+    code FAR *next;             /* next available space in codes[] */
+    unsigned short lens[320];   /* temporary storage for code lengths */
+    unsigned short work[288];   /* work area for code table building */
+    code codes[ENOUGH];         /* space for code tables */
+    int sane;                   /* if false, allow invalid distance too far */
+    int back;                   /* bits back of last unprocessed length/lit */
+    unsigned was;               /* initial length of match */
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/inftrees.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,304 @@
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995-2017 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+
+#define MAXBITS 15
+
+const char inflate_copyright[] =
+   " inflate 1.2.11 Copyright 1995-2017 Mark Adler ";
+/*
+  If you use the zlib library in a product, an acknowledgment is welcome
+  in the documentation of your product. If for some reason you cannot
+  include such an acknowledgment, I would appreciate that you keep this
+  copyright string in the executable of your product.
+ */
+
+/*
+   Build a set of tables to decode the provided canonical Huffman code.
+   The code lengths are lens[0..codes-1].  The result starts at *table,
+   whose indices are 0..2^bits-1.  work is a writable array of at least
+   lens shorts, which is used as a work area.  type is the type of code
+   to be generated, CODES, LENS, or DISTS.  On return, zero is success,
+   -1 is an invalid code, and +1 means that ENOUGH isn't enough.  table
+   on return points to the next available entry's address.  bits is the
+   requested root table index bits, and on return it is the actual root
+   table index bits.  It will differ if the request is greater than the
+   longest code or if it is less than the shortest code.
+ */
+int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work)
+codetype type;
+unsigned short FAR *lens;
+unsigned codes;
+code FAR * FAR *table;
+unsigned FAR *bits;
+unsigned short FAR *work;
+{
+    unsigned len;               /* a code's length in bits */
+    unsigned sym;               /* index of code symbols */
+    unsigned min, max;          /* minimum and maximum code lengths */
+    unsigned root;              /* number of index bits for root table */
+    unsigned curr;              /* number of index bits for current table */
+    unsigned drop;              /* code bits to drop for sub-table */
+    int left;                   /* number of prefix codes available */
+    unsigned used;              /* code entries in table used */
+    unsigned huff;              /* Huffman code */
+    unsigned incr;              /* for incrementing code, index */
+    unsigned fill;              /* index for replicating entries */
+    unsigned low;               /* low bits for current root entry */
+    unsigned mask;              /* mask for low root bits */
+    code here;                  /* table entry for duplication */
+    code FAR *next;             /* next available space in table */
+    const unsigned short FAR *base;     /* base value table to use */
+    const unsigned short FAR *extra;    /* extra bits table to use */
+    unsigned match;             /* use base and extra for symbol >= match */
+    unsigned short count[MAXBITS+1];    /* number of codes of each length */
+    unsigned short offs[MAXBITS+1];     /* offsets in table for each length */
+    static const unsigned short lbase[31] = { /* Length codes 257..285 base */
+        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 unsigned short lext[31] = { /* Length codes 257..285 extra */
+        16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
+        19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 77, 202};
+    static const unsigned short dbase[32] = { /* Distance codes 0..29 base */
+        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 unsigned short dext[32] = { /* Distance codes 0..29 extra */
+        16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
+        23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
+        28, 28, 29, 29, 64, 64};
+
+    /*
+       Process a set of code lengths to create a canonical Huffman code.  The
+       code lengths are lens[0..codes-1].  Each length corresponds to the
+       symbols 0..codes-1.  The Huffman code is generated by first sorting the
+       symbols by length from short to long, and retaining the symbol order
+       for codes with equal lengths.  Then the code starts with all zero bits
+       for the first code of the shortest length, and the codes are integer
+       increments for the same length, and zeros are appended as the length
+       increases.  For the deflate format, these bits are stored backwards
+       from their more natural integer increment ordering, and so when the
+       decoding tables are built in the large loop below, the integer codes
+       are incremented backwards.
+
+       This routine assumes, but does not check, that all of the entries in
+       lens[] are in the range 0..MAXBITS.  The caller must assure this.
+       1..MAXBITS is interpreted as that code length.  zero means that that
+       symbol does not occur in this code.
+
+       The codes are sorted by computing a count of codes for each length,
+       creating from that a table of starting indices for each length in the
+       sorted table, and then entering the symbols in order in the sorted
+       table.  The sorted table is work[], with that space being provided by
+       the caller.
+
+       The length counts are used for other purposes as well, i.e. finding
+       the minimum and maximum length codes, determining if there are any
+       codes at all, checking for a valid set of lengths, and looking ahead
+       at length counts to determine sub-table sizes when building the
+       decoding tables.
+     */
+
+    /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
+    for (len = 0; len <= MAXBITS; len++)
+        count[len] = 0;
+    for (sym = 0; sym < codes; sym++)
+        count[lens[sym]]++;
+
+    /* bound code lengths, force root to be within code lengths */
+    root = *bits;
+    for (max = MAXBITS; max >= 1; max--)
+        if (count[max] != 0) break;
+    if (root > max) root = max;
+    if (max == 0) {                     /* no symbols to code at all */
+        here.op = (unsigned char)64;    /* invalid code marker */
+        here.bits = (unsigned char)1;
+        here.val = (unsigned short)0;
+        *(*table)++ = here;             /* make a table to force an error */
+        *(*table)++ = here;
+        *bits = 1;
+        return 0;     /* no symbols, but wait for decoding to report error */
+    }
+    for (min = 1; min < max; min++)
+        if (count[min] != 0) break;
+    if (root < min) root = min;
+
+    /* check for an over-subscribed or incomplete set of lengths */
+    left = 1;
+    for (len = 1; len <= MAXBITS; len++) {
+        left <<= 1;
+        left -= count[len];
+        if (left < 0) return -1;        /* over-subscribed */
+    }
+    if (left > 0 && (type == CODES || max != 1))
+        return -1;                      /* incomplete set */
+
+    /* generate offsets into symbol table for each length for sorting */
+    offs[1] = 0;
+    for (len = 1; len < MAXBITS; len++)
+        offs[len + 1] = offs[len] + count[len];
+
+    /* sort symbols by length, by symbol order within each length */
+    for (sym = 0; sym < codes; sym++)
+        if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym;
+
+    /*
+       Create and fill in decoding tables.  In this loop, the table being
+       filled is at next and has curr index bits.  The code being used is huff
+       with length len.  That code is converted to an index by dropping drop
+       bits off of the bottom.  For codes where len is less than drop + curr,
+       those top drop + curr - len bits are incremented through all values to
+       fill the table with replicated entries.
+
+       root is the number of index bits for the root table.  When len exceeds
+       root, sub-tables are created pointed to by the root entry with an index
+       of the low root bits of huff.  This is saved in low to check for when a
+       new sub-table should be started.  drop is zero when the root table is
+       being filled, and drop is root when sub-tables are being filled.
+
+       When a new sub-table is needed, it is necessary to look ahead in the
+       code lengths to determine what size sub-table is needed.  The length
+       counts are used for this, and so count[] is decremented as codes are
+       entered in the tables.
+
+       used keeps track of how many table entries have been allocated from the
+       provided *table space.  It is checked for LENS and DIST tables against
+       the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
+       the initial root table size constants.  See the comments in inftrees.h
+       for more information.
+
+       sym increments through all symbols, and the loop terminates when
+       all codes of length max, i.e. all codes, have been processed.  This
+       routine permits incomplete codes, so another loop after this one fills
+       in the rest of the decoding tables with invalid code markers.
+     */
+
+    /* set up for code type */
+    switch (type) {
+    case CODES:
+        base = extra = work;    /* dummy value--not used */
+        match = 20;
+        break;
+    case LENS:
+        base = lbase;
+        extra = lext;
+        match = 257;
+        break;
+    default:    /* DISTS */
+        base = dbase;
+        extra = dext;
+        match = 0;
+    }
+
+    /* initialize state for loop */
+    huff = 0;                   /* starting code */
+    sym = 0;                    /* starting code symbol */
+    len = min;                  /* starting code length */
+    next = *table;              /* current table to fill in */
+    curr = root;                /* current table index bits */
+    drop = 0;                   /* current bits to drop from code for index */
+    low = (unsigned)(-1);       /* trigger new sub-table when len > root */
+    used = 1U << root;          /* use root table entries */
+    mask = used - 1;            /* mask for comparing low */
+
+    /* check available table space */
+    if ((type == LENS && used > ENOUGH_LENS) ||
+        (type == DISTS && used > ENOUGH_DISTS))
+        return 1;
+
+    /* process all codes and make table entries */
+    for (;;) {
+        /* create table entry */
+        here.bits = (unsigned char)(len - drop);
+        if (work[sym] + 1U < match) {
+            here.op = (unsigned char)0;
+            here.val = work[sym];
+        }
+        else if (work[sym] >= match) {
+            here.op = (unsigned char)(extra[work[sym] - match]);
+            here.val = base[work[sym] - match];
+        }
+        else {
+            here.op = (unsigned char)(32 + 64);         /* end of block */
+            here.val = 0;
+        }
+
+        /* replicate for those indices with low len bits equal to huff */
+        incr = 1U << (len - drop);
+        fill = 1U << curr;
+        min = fill;                 /* save offset to next table */
+        do {
+            fill -= incr;
+            next[(huff >> drop) + fill] = here;
+        } while (fill != 0);
+
+        /* backwards increment the len-bit code huff */
+        incr = 1U << (len - 1);
+        while (huff & incr)
+            incr >>= 1;
+        if (incr != 0) {
+            huff &= incr - 1;
+            huff += incr;
+        }
+        else
+            huff = 0;
+
+        /* go to next symbol, update count, len */
+        sym++;
+        if (--(count[len]) == 0) {
+            if (len == max) break;
+            len = lens[work[sym]];
+        }
+
+        /* create new sub-table if needed */
+        if (len > root && (huff & mask) != low) {
+            /* if first time, transition to sub-tables */
+            if (drop == 0)
+                drop = root;
+
+            /* increment past last table */
+            next += min;            /* here min is 1 << curr */
+
+            /* determine length of next table */
+            curr = len - drop;
+            left = (int)(1 << curr);
+            while (curr + drop < max) {
+                left -= count[curr + drop];
+                if (left <= 0) break;
+                curr++;
+                left <<= 1;
+            }
+
+            /* check for enough space */
+            used += 1U << curr;
+            if ((type == LENS && used > ENOUGH_LENS) ||
+                (type == DISTS && used > ENOUGH_DISTS))
+                return 1;
+
+            /* point entry in root table to sub-table */
+            low = huff & mask;
+            (*table)[low].op = (unsigned char)curr;
+            (*table)[low].bits = (unsigned char)root;
+            (*table)[low].val = (unsigned short)(next - *table);
+        }
+    }
+
+    /* fill in remaining table entry if code is incomplete (guaranteed to have
+       at most one remaining entry, since if the code is incomplete, the
+       maximum code length that was allowed to get this far is one bit) */
+    if (huff != 0) {
+        here.op = (unsigned char)64;            /* invalid code marker */
+        here.bits = (unsigned char)(len - drop);
+        here.val = (unsigned short)0;
+        next[huff] = here;
+    }
+
+    /* set return parameters */
+    *table += used;
+    *bits = root;
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/inftrees.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,62 @@
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995-2005, 2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* Structure for decoding tables.  Each entry provides either the
+   information needed to do the operation requested by the code that
+   indexed that table entry, or it provides a pointer to another
+   table that indexes more bits of the code.  op indicates whether
+   the entry is a pointer to another table, a literal, a length or
+   distance, an end-of-block, or an invalid code.  For a table
+   pointer, the low four bits of op is the number of index bits of
+   that table.  For a length or distance, the low four bits of op
+   is the number of extra bits to get after the code.  bits is
+   the number of bits in this code or part of the code to drop off
+   of the bit buffer.  val is the actual byte to output in the case
+   of a literal, the base length or distance, or the offset from
+   the current table to the next table.  Each entry is four bytes. */
+typedef struct {
+    unsigned char op;           /* operation, extra bits, table bits */
+    unsigned char bits;         /* bits in this part of the code */
+    unsigned short val;         /* offset in table or code value */
+} code;
+
+/* op values as set by inflate_table():
+    00000000 - literal
+    0000tttt - table link, tttt != 0 is the number of table index bits
+    0001eeee - length or distance, eeee is the number of extra bits
+    01100000 - end of block
+    01000000 - invalid code
+ */
+
+/* Maximum size of the dynamic table.  The maximum number of code structures is
+   1444, which is the sum of 852 for literal/length codes and 592 for distance
+   codes.  These values were found by exhaustive searches using the program
+   examples/enough.c found in the zlib distribtution.  The arguments to that
+   program are the number of symbols, the initial root table size, and the
+   maximum bit length of a code.  "enough 286 9 15" for literal/length codes
+   returns returns 852, and "enough 30 6 15" for distance codes returns 592.
+   The initial root table size (9 or 6) is found in the fifth argument of the
+   inflate_table() calls in inflate.c and infback.c.  If the root table size is
+   changed, then these maximum sizes would be need to be recalculated and
+   updated. */
+#define ENOUGH_LENS 852
+#define ENOUGH_DISTS 592
+#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS)
+
+/* Type of code to build for inflate_table() */
+typedef enum {
+    CODES,
+    LENS,
+    DISTS
+} codetype;
+
+int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens,
+                             unsigned codes, code FAR * FAR *table,
+                             unsigned FAR *bits, unsigned short FAR *work));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/trees.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,1203 @@
+/* trees.c -- output deflated data using Huffman coding
+ * Copyright (C) 1995-2017 Jean-loup Gailly
+ * detect_data_type() function provided freely by Cosmin Truta, 2006
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ *  ALGORITHM
+ *
+ *      The "deflation" process uses several Huffman trees. The more
+ *      common source values are represented by shorter bit sequences.
+ *
+ *      Each code tree is stored in a compressed form which is itself
+ * a Huffman encoding of the lengths of all the code strings (in
+ * ascending order by source values).  The actual code strings are
+ * reconstructed from the lengths in the inflate process, as described
+ * in the deflate specification.
+ *
+ *  REFERENCES
+ *
+ *      Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
+ *      Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
+ *
+ *      Storer, James A.
+ *          Data Compression:  Methods and Theory, pp. 49-50.
+ *          Computer Science Press, 1988.  ISBN 0-7167-8156-5.
+ *
+ *      Sedgewick, R.
+ *          Algorithms, p290.
+ *          Addison-Wesley, 1983. ISBN 0-201-06672-6.
+ */
+
+/* @(#) $Id$ */
+
+/* #define GEN_TREES_H */
+
+#include "deflate.h"
+
+#ifdef ZLIB_DEBUG
+#  include <ctype.h>
+#endif
+
+/* ===========================================================================
+ * Constants
+ */
+
+#define MAX_BL_BITS 7
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+#define END_BLOCK 256
+/* end of block literal code */
+
+#define REP_3_6      16
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+
+#define REPZ_3_10    17
+/* repeat a zero length 3-10 times  (3 bits of repeat count) */
+
+#define REPZ_11_138  18
+/* repeat a zero length 11-138 times  (7 bits of repeat count) */
+
+local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
+   = {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};
+
+local const int extra_dbits[D_CODES] /* extra bits for each distance code */
+   = {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};
+
+local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */
+   = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
+
+local const uch bl_order[BL_CODES]
+   = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+/* ===========================================================================
+ * Local data. These are initialized only once.
+ */
+
+#define DIST_CODE_LEN  512 /* see definition of array dist_code below */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+/* non ANSI compilers may not accept trees.h */
+
+local ct_data static_ltree[L_CODES+2];
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
+ * below).
+ */
+
+local ct_data static_dtree[D_CODES];
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+uch _dist_code[DIST_CODE_LEN];
+/* Distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+uch _length_code[MAX_MATCH-MIN_MATCH+1];
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+local int base_length[LENGTH_CODES];
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+local int base_dist[D_CODES];
+/* First normalized distance for each code (0 = distance of 1) */
+
+#else
+#  include "trees.h"
+#endif /* GEN_TREES_H */
+
+struct static_tree_desc_s {
+    const ct_data *static_tree;  /* static tree or NULL */
+    const intf *extra_bits;      /* extra bits for each code or NULL */
+    int     extra_base;          /* base index for extra_bits */
+    int     elems;               /* max number of elements in the tree */
+    int     max_length;          /* max bit length for the codes */
+};
+
+local const static_tree_desc  static_l_desc =
+{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
+
+local const static_tree_desc  static_d_desc =
+{static_dtree, extra_dbits, 0,          D_CODES, MAX_BITS};
+
+local const static_tree_desc  static_bl_desc =
+{(const ct_data *)0, extra_blbits, 0,   BL_CODES, MAX_BL_BITS};
+
+/* ===========================================================================
+ * Local (static) routines in this file.
+ */
+
+local void tr_static_init OF((void));
+local void init_block     OF((deflate_state *s));
+local void pqdownheap     OF((deflate_state *s, ct_data *tree, int k));
+local void gen_bitlen     OF((deflate_state *s, tree_desc *desc));
+local void gen_codes      OF((ct_data *tree, int max_code, ushf *bl_count));
+local void build_tree     OF((deflate_state *s, tree_desc *desc));
+local void scan_tree      OF((deflate_state *s, ct_data *tree, int max_code));
+local void send_tree      OF((deflate_state *s, ct_data *tree, int max_code));
+local int  build_bl_tree  OF((deflate_state *s));
+local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
+                              int blcodes));
+local void compress_block OF((deflate_state *s, const ct_data *ltree,
+                              const ct_data *dtree));
+local int  detect_data_type OF((deflate_state *s));
+local unsigned bi_reverse OF((unsigned value, int length));
+local void bi_windup      OF((deflate_state *s));
+local void bi_flush       OF((deflate_state *s));
+
+#ifdef GEN_TREES_H
+local void gen_trees_header OF((void));
+#endif
+
+#ifndef ZLIB_DEBUG
+#  define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
+   /* Send a code of the given tree. c and tree must not have side effects */
+
+#else /* !ZLIB_DEBUG */
+#  define send_code(s, c, tree) \
+     { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \
+       send_bits(s, tree[c].Code, tree[c].Len); }
+#endif
+
+/* ===========================================================================
+ * Output a short LSB first on the stream.
+ * IN assertion: there is enough room in pendingBuf.
+ */
+#define put_short(s, w) { \
+    put_byte(s, (uch)((w) & 0xff)); \
+    put_byte(s, (uch)((ush)(w) >> 8)); \
+}
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+#ifdef ZLIB_DEBUG
+local void send_bits      OF((deflate_state *s, int value, int length));
+
+local void send_bits(s, value, length)
+    deflate_state *s;
+    int value;  /* value to send */
+    int length; /* number of bits */
+{
+    Tracevv((stderr," l %2d v %4x ", length, value));
+    Assert(length > 0 && length <= 15, "invalid length");
+    s->bits_sent += (ulg)length;
+
+    /* If not enough room in bi_buf, use (valid) bits from bi_buf and
+     * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+     * unused bits in value.
+     */
+    if (s->bi_valid > (int)Buf_size - length) {
+        s->bi_buf |= (ush)value << s->bi_valid;
+        put_short(s, s->bi_buf);
+        s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
+        s->bi_valid += length - Buf_size;
+    } else {
+        s->bi_buf |= (ush)value << s->bi_valid;
+        s->bi_valid += length;
+    }
+}
+#else /* !ZLIB_DEBUG */
+
+#define send_bits(s, value, length) \
+{ int len = length;\
+  if (s->bi_valid > (int)Buf_size - len) {\
+    int val = (int)value;\
+    s->bi_buf |= (ush)val << s->bi_valid;\
+    put_short(s, s->bi_buf);\
+    s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
+    s->bi_valid += len - Buf_size;\
+  } else {\
+    s->bi_buf |= (ush)(value) << s->bi_valid;\
+    s->bi_valid += len;\
+  }\
+}
+#endif /* ZLIB_DEBUG */
+
+
+/* the arguments must not have side effects */
+
+/* ===========================================================================
+ * Initialize the various 'constant' tables.
+ */
+local void tr_static_init()
+{
+#if defined(GEN_TREES_H) || !defined(STDC)
+    static int static_init_done = 0;
+    int n;        /* iterates over tree elements */
+    int bits;     /* bit counter */
+    int length;   /* length value */
+    int code;     /* code value */
+    int dist;     /* distance index */
+    ush bl_count[MAX_BITS+1];
+    /* number of codes at each bit length for an optimal tree */
+
+    if (static_init_done) return;
+
+    /* For some embedded targets, global variables are not initialized: */
+#ifdef NO_INIT_GLOBAL_POINTERS
+    static_l_desc.static_tree = static_ltree;
+    static_l_desc.extra_bits = extra_lbits;
+    static_d_desc.static_tree = static_dtree;
+    static_d_desc.extra_bits = extra_dbits;
+    static_bl_desc.extra_bits = extra_blbits;
+#endif
+
+    /* Initialize the mapping length (0..255) -> length code (0..28) */
+    length = 0;
+    for (code = 0; code < LENGTH_CODES-1; code++) {
+        base_length[code] = length;
+        for (n = 0; n < (1<<extra_lbits[code]); n++) {
+            _length_code[length++] = (uch)code;
+        }
+    }
+    Assert (length == 256, "tr_static_init: length != 256");
+    /* Note that the length 255 (match length 258) can be represented
+     * in two different ways: code 284 + 5 bits or code 285, so we
+     * overwrite length_code[255] to use the best encoding:
+     */
+    _length_code[length-1] = (uch)code;
+
+    /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+    dist = 0;
+    for (code = 0 ; code < 16; code++) {
+        base_dist[code] = dist;
+        for (n = 0; n < (1<<extra_dbits[code]); n++) {
+            _dist_code[dist++] = (uch)code;
+        }
+    }
+    Assert (dist == 256, "tr_static_init: dist != 256");
+    dist >>= 7; /* from now on, all distances are divided by 128 */
+    for ( ; code < D_CODES; code++) {
+        base_dist[code] = dist << 7;
+        for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
+            _dist_code[256 + dist++] = (uch)code;
+        }
+    }
+    Assert (dist == 256, "tr_static_init: 256+dist != 512");
+
+    /* Construct the codes of the static literal tree */
+    for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
+    n = 0;
+    while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
+    while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
+    while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
+    while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
+    /* Codes 286 and 287 do not exist, but we must include them in the
+     * tree construction to get a canonical Huffman tree (longest code
+     * all ones)
+     */
+    gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
+
+    /* The static distance tree is trivial: */
+    for (n = 0; n < D_CODES; n++) {
+        static_dtree[n].Len = 5;
+        static_dtree[n].Code = bi_reverse((unsigned)n, 5);
+    }
+    static_init_done = 1;
+
+#  ifdef GEN_TREES_H
+    gen_trees_header();
+#  endif
+#endif /* defined(GEN_TREES_H) || !defined(STDC) */
+}
+
+/* ===========================================================================
+ * Genererate the file trees.h describing the static trees.
+ */
+#ifdef GEN_TREES_H
+#  ifndef ZLIB_DEBUG
+#    include <stdio.h>
+#  endif
+
+#  define SEPARATOR(i, last, width) \
+      ((i) == (last)? "\n};\n\n" :    \
+       ((i) % (width) == (width)-1 ? ",\n" : ", "))
+
+void gen_trees_header()
+{
+    FILE *header = fopen("trees.h", "w");
+    int i;
+
+    Assert (header != NULL, "Can't open trees.h");
+    fprintf(header,
+            "/* header created automatically with -DGEN_TREES_H */\n\n");
+
+    fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n");
+    for (i = 0; i < L_CODES+2; i++) {
+        fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code,
+                static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5));
+    }
+
+    fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n");
+    for (i = 0; i < D_CODES; i++) {
+        fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code,
+                static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5));
+    }
+
+    fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n");
+    for (i = 0; i < DIST_CODE_LEN; i++) {
+        fprintf(header, "%2u%s", _dist_code[i],
+                SEPARATOR(i, DIST_CODE_LEN-1, 20));
+    }
+
+    fprintf(header,
+        "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n");
+    for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) {
+        fprintf(header, "%2u%s", _length_code[i],
+                SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20));
+    }
+
+    fprintf(header, "local const int base_length[LENGTH_CODES] = {\n");
+    for (i = 0; i < LENGTH_CODES; i++) {
+        fprintf(header, "%1u%s", base_length[i],
+                SEPARATOR(i, LENGTH_CODES-1, 20));
+    }
+
+    fprintf(header, "local const int base_dist[D_CODES] = {\n");
+    for (i = 0; i < D_CODES; i++) {
+        fprintf(header, "%5u%s", base_dist[i],
+                SEPARATOR(i, D_CODES-1, 10));
+    }
+
+    fclose(header);
+}
+#endif /* GEN_TREES_H */
+
+/* ===========================================================================
+ * Initialize the tree data structures for a new zlib stream.
+ */
+void ZLIB_INTERNAL _tr_init(s)
+    deflate_state *s;
+{
+    tr_static_init();
+
+    s->l_desc.dyn_tree = s->dyn_ltree;
+    s->l_desc.stat_desc = &static_l_desc;
+
+    s->d_desc.dyn_tree = s->dyn_dtree;
+    s->d_desc.stat_desc = &static_d_desc;
+
+    s->bl_desc.dyn_tree = s->bl_tree;
+    s->bl_desc.stat_desc = &static_bl_desc;
+
+    s->bi_buf = 0;
+    s->bi_valid = 0;
+#ifdef ZLIB_DEBUG
+    s->compressed_len = 0L;
+    s->bits_sent = 0L;
+#endif
+
+    /* Initialize the first block of the first file: */
+    init_block(s);
+}
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+local void init_block(s)
+    deflate_state *s;
+{
+    int n; /* iterates over tree elements */
+
+    /* Initialize the trees. */
+    for (n = 0; n < L_CODES;  n++) s->dyn_ltree[n].Freq = 0;
+    for (n = 0; n < D_CODES;  n++) s->dyn_dtree[n].Freq = 0;
+    for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
+
+    s->dyn_ltree[END_BLOCK].Freq = 1;
+    s->opt_len = s->static_len = 0L;
+    s->last_lit = s->matches = 0;
+}
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+
+/* ===========================================================================
+ * Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len.
+ */
+#define pqremove(s, tree, top) \
+{\
+    top = s->heap[SMALLEST]; \
+    s->heap[SMALLEST] = s->heap[s->heap_len--]; \
+    pqdownheap(s, tree, SMALLEST); \
+}
+
+/* ===========================================================================
+ * Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length.
+ */
+#define smaller(tree, n, m, depth) \
+   (tree[n].Freq < tree[m].Freq || \
+   (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+local void pqdownheap(s, tree, k)
+    deflate_state *s;
+    ct_data *tree;  /* the tree to restore */
+    int k;               /* node to move down */
+{
+    int v = s->heap[k];
+    int j = k << 1;  /* left son of k */
+    while (j <= s->heap_len) {
+        /* Set j to the smallest of the two sons: */
+        if (j < s->heap_len &&
+            smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
+            j++;
+        }
+        /* Exit if v is smaller than both sons */
+        if (smaller(tree, v, s->heap[j], s->depth)) break;
+
+        /* Exchange v with the smallest son */
+        s->heap[k] = s->heap[j];  k = j;
+
+        /* And continue down the tree, setting j to the left son of k */
+        j <<= 1;
+    }
+    s->heap[k] = v;
+}
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ *    above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ *     array bl_count contains the frequencies for each bit length.
+ *     The length opt_len is updated; static_len is also updated if stree is
+ *     not null.
+ */
+local void gen_bitlen(s, desc)
+    deflate_state *s;
+    tree_desc *desc;    /* the tree descriptor */
+{
+    ct_data *tree        = desc->dyn_tree;
+    int max_code         = desc->max_code;
+    const ct_data *stree = desc->stat_desc->static_tree;
+    const intf *extra    = desc->stat_desc->extra_bits;
+    int base             = desc->stat_desc->extra_base;
+    int max_length       = desc->stat_desc->max_length;
+    int h;              /* heap index */
+    int n, m;           /* iterate over the tree elements */
+    int bits;           /* bit length */
+    int xbits;          /* extra bits */
+    ush f;              /* frequency */
+    int overflow = 0;   /* number of elements with bit length too large */
+
+    for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
+
+    /* In a first pass, compute the optimal bit lengths (which may
+     * overflow in the case of the bit length tree).
+     */
+    tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
+
+    for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
+        n = s->heap[h];
+        bits = tree[tree[n].Dad].Len + 1;
+        if (bits > max_length) bits = max_length, overflow++;
+        tree[n].Len = (ush)bits;
+        /* We overwrite tree[n].Dad which is no longer needed */
+
+        if (n > max_code) continue; /* not a leaf node */
+
+        s->bl_count[bits]++;
+        xbits = 0;
+        if (n >= base) xbits = extra[n-base];
+        f = tree[n].Freq;
+        s->opt_len += (ulg)f * (unsigned)(bits + xbits);
+        if (stree) s->static_len += (ulg)f * (unsigned)(stree[n].Len + xbits);
+    }
+    if (overflow == 0) return;
+
+    Tracev((stderr,"\nbit length overflow\n"));
+    /* This happens for example on obj2 and pic of the Calgary corpus */
+
+    /* Find the first bit length which could increase: */
+    do {
+        bits = max_length-1;
+        while (s->bl_count[bits] == 0) bits--;
+        s->bl_count[bits]--;      /* move one leaf down the tree */
+        s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
+        s->bl_count[max_length]--;
+        /* The brother of the overflow item also moves one step up,
+         * but this does not affect bl_count[max_length]
+         */
+        overflow -= 2;
+    } while (overflow > 0);
+
+    /* Now recompute all bit lengths, scanning in increasing frequency.
+     * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+     * lengths instead of fixing only the wrong ones. This idea is taken
+     * from 'ar' written by Haruhiko Okumura.)
+     */
+    for (bits = max_length; bits != 0; bits--) {
+        n = s->bl_count[bits];
+        while (n != 0) {
+            m = s->heap[--h];
+            if (m > max_code) continue;
+            if ((unsigned) tree[m].Len != (unsigned) bits) {
+                Tracev((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
+                s->opt_len += ((ulg)bits - tree[m].Len) * tree[m].Freq;
+                tree[m].Len = (ush)bits;
+            }
+            n--;
+        }
+    }
+}
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ *     zero code length.
+ */
+local void gen_codes (tree, max_code, bl_count)
+    ct_data *tree;             /* the tree to decorate */
+    int max_code;              /* largest code with non zero frequency */
+    ushf *bl_count;            /* number of codes at each bit length */
+{
+    ush next_code[MAX_BITS+1]; /* next code value for each bit length */
+    unsigned code = 0;         /* running code value */
+    int bits;                  /* bit index */
+    int n;                     /* code index */
+
+    /* The distribution counts are first used to generate the code values
+     * without bit reversal.
+     */
+    for (bits = 1; bits <= MAX_BITS; bits++) {
+        code = (code + bl_count[bits-1]) << 1;
+        next_code[bits] = (ush)code;
+    }
+    /* Check that the bit counts in bl_count are consistent. The last code
+     * must be all ones.
+     */
+    Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
+            "inconsistent bit counts");
+    Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
+
+    for (n = 0;  n <= max_code; n++) {
+        int len = tree[n].Len;
+        if (len == 0) continue;
+        /* Now reverse the bits */
+        tree[n].Code = (ush)bi_reverse(next_code[len]++, len);
+
+        Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
+             n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
+    }
+}
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ *     and corresponding code. The length opt_len is updated; static_len is
+ *     also updated if stree is not null. The field max_code is set.
+ */
+local void build_tree(s, desc)
+    deflate_state *s;
+    tree_desc *desc; /* the tree descriptor */
+{
+    ct_data *tree         = desc->dyn_tree;
+    const ct_data *stree  = desc->stat_desc->static_tree;
+    int elems             = desc->stat_desc->elems;
+    int n, m;          /* iterate over heap elements */
+    int max_code = -1; /* largest code with non zero frequency */
+    int node;          /* new node being created */
+
+    /* Construct the initial heap, with least frequent element in
+     * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+     * heap[0] is not used.
+     */
+    s->heap_len = 0, s->heap_max = HEAP_SIZE;
+
+    for (n = 0; n < elems; n++) {
+        if (tree[n].Freq != 0) {
+            s->heap[++(s->heap_len)] = max_code = n;
+            s->depth[n] = 0;
+        } else {
+            tree[n].Len = 0;
+        }
+    }
+
+    /* The pkzip format requires that at least one distance code exists,
+     * and that at least one bit should be sent even if there is only one
+     * possible code. So to avoid special checks later on we force at least
+     * two codes of non zero frequency.
+     */
+    while (s->heap_len < 2) {
+        node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
+        tree[node].Freq = 1;
+        s->depth[node] = 0;
+        s->opt_len--; if (stree) s->static_len -= stree[node].Len;
+        /* node is 0 or 1 so it does not have extra bits */
+    }
+    desc->max_code = max_code;
+
+    /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+     * establish sub-heaps of increasing lengths:
+     */
+    for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
+
+    /* Construct the Huffman tree by repeatedly combining the least two
+     * frequent nodes.
+     */
+    node = elems;              /* next internal node of the tree */
+    do {
+        pqremove(s, tree, n);  /* n = node of least frequency */
+        m = s->heap[SMALLEST]; /* m = node of next least frequency */
+
+        s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
+        s->heap[--(s->heap_max)] = m;
+
+        /* Create a new node father of n and m */
+        tree[node].Freq = tree[n].Freq + tree[m].Freq;
+        s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ?
+                                s->depth[n] : s->depth[m]) + 1);
+        tree[n].Dad = tree[m].Dad = (ush)node;
+#ifdef DUMP_BL_TREE
+        if (tree == s->bl_tree) {
+            fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
+                    node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
+        }
+#endif
+        /* and insert the new node in the heap */
+        s->heap[SMALLEST] = node++;
+        pqdownheap(s, tree, SMALLEST);
+
+    } while (s->heap_len >= 2);
+
+    s->heap[--(s->heap_max)] = s->heap[SMALLEST];
+
+    /* At this point, the fields freq and dad are set. We can now
+     * generate the bit lengths.
+     */
+    gen_bitlen(s, (tree_desc *)desc);
+
+    /* The field len is now set, we can generate the bit codes */
+    gen_codes ((ct_data *)tree, max_code, s->bl_count);
+}
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree.
+ */
+local void scan_tree (s, tree, max_code)
+    deflate_state *s;
+    ct_data *tree;   /* the tree to be scanned */
+    int max_code;    /* and its largest code of non zero frequency */
+{
+    int n;                     /* iterates over all tree elements */
+    int prevlen = -1;          /* last emitted length */
+    int curlen;                /* length of current code */
+    int nextlen = tree[0].Len; /* length of next code */
+    int count = 0;             /* repeat count of the current code */
+    int max_count = 7;         /* max repeat count */
+    int min_count = 4;         /* min repeat count */
+
+    if (nextlen == 0) max_count = 138, min_count = 3;
+    tree[max_code+1].Len = (ush)0xffff; /* guard */
+
+    for (n = 0; n <= max_code; n++) {
+        curlen = nextlen; nextlen = tree[n+1].Len;
+        if (++count < max_count && curlen == nextlen) {
+            continue;
+        } else if (count < min_count) {
+            s->bl_tree[curlen].Freq += count;
+        } else if (curlen != 0) {
+            if (curlen != prevlen) s->bl_tree[curlen].Freq++;
+            s->bl_tree[REP_3_6].Freq++;
+        } else if (count <= 10) {
+            s->bl_tree[REPZ_3_10].Freq++;
+        } else {
+            s->bl_tree[REPZ_11_138].Freq++;
+        }
+        count = 0; prevlen = curlen;
+        if (nextlen == 0) {
+            max_count = 138, min_count = 3;
+        } else if (curlen == nextlen) {
+            max_count = 6, min_count = 3;
+        } else {
+            max_count = 7, min_count = 4;
+        }
+    }
+}
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+local void send_tree (s, tree, max_code)
+    deflate_state *s;
+    ct_data *tree; /* the tree to be scanned */
+    int max_code;       /* and its largest code of non zero frequency */
+{
+    int n;                     /* iterates over all tree elements */
+    int prevlen = -1;          /* last emitted length */
+    int curlen;                /* length of current code */
+    int nextlen = tree[0].Len; /* length of next code */
+    int count = 0;             /* repeat count of the current code */
+    int max_count = 7;         /* max repeat count */
+    int min_count = 4;         /* min repeat count */
+
+    /* tree[max_code+1].Len = -1; */  /* guard already set */
+    if (nextlen == 0) max_count = 138, min_count = 3;
+
+    for (n = 0; n <= max_code; n++) {
+        curlen = nextlen; nextlen = tree[n+1].Len;
+        if (++count < max_count && curlen == nextlen) {
+            continue;
+        } else if (count < min_count) {
+            do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
+
+        } else if (curlen != 0) {
+            if (curlen != prevlen) {
+                send_code(s, curlen, s->bl_tree); count--;
+            }
+            Assert(count >= 3 && count <= 6, " 3_6?");
+            send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
+
+        } else if (count <= 10) {
+            send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
+
+        } else {
+            send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
+        }
+        count = 0; prevlen = curlen;
+        if (nextlen == 0) {
+            max_count = 138, min_count = 3;
+        } else if (curlen == nextlen) {
+            max_count = 6, min_count = 3;
+        } else {
+            max_count = 7, min_count = 4;
+        }
+    }
+}
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+local int build_bl_tree(s)
+    deflate_state *s;
+{
+    int max_blindex;  /* index of last bit length code of non zero freq */
+
+    /* Determine the bit length frequencies for literal and distance trees */
+    scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
+    scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
+
+    /* Build the bit length tree: */
+    build_tree(s, (tree_desc *)(&(s->bl_desc)));
+    /* opt_len now includes the length of the tree representations, except
+     * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+     */
+
+    /* Determine the number of bit length codes to send. The pkzip format
+     * requires that at least 4 bit length codes be sent. (appnote.txt says
+     * 3 but the actual value used is 4.)
+     */
+    for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
+        if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
+    }
+    /* Update opt_len to include the bit length tree and counts */
+    s->opt_len += 3*((ulg)max_blindex+1) + 5+5+4;
+    Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
+            s->opt_len, s->static_len));
+
+    return max_blindex;
+}
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+local void send_all_trees(s, lcodes, dcodes, blcodes)
+    deflate_state *s;
+    int lcodes, dcodes, blcodes; /* number of codes for each tree */
+{
+    int rank;                    /* index in bl_order */
+
+    Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+    Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
+            "too many codes");
+    Tracev((stderr, "\nbl counts: "));
+    send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
+    send_bits(s, dcodes-1,   5);
+    send_bits(s, blcodes-4,  4); /* not -3 as stated in appnote.txt */
+    for (rank = 0; rank < blcodes; rank++) {
+        Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+        send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
+    }
+    Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
+
+    send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
+    Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
+
+    send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
+    Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
+}
+
+/* ===========================================================================
+ * Send a stored block
+ */
+void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last)
+    deflate_state *s;
+    charf *buf;       /* input block */
+    ulg stored_len;   /* length of input block */
+    int last;         /* one if this is the last block for a file */
+{
+    send_bits(s, (STORED_BLOCK<<1)+last, 3);    /* send block type */
+    bi_windup(s);        /* align on byte boundary */
+    put_short(s, (ush)stored_len);
+    put_short(s, (ush)~stored_len);
+    zmemcpy(s->pending_buf + s->pending, (Bytef *)buf, stored_len);
+    s->pending += stored_len;
+#ifdef ZLIB_DEBUG
+    s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
+    s->compressed_len += (stored_len + 4) << 3;
+    s->bits_sent += 2*16;
+    s->bits_sent += stored_len<<3;
+#endif
+}
+
+/* ===========================================================================
+ * Flush the bits in the bit buffer to pending output (leaves at most 7 bits)
+ */
+void ZLIB_INTERNAL _tr_flush_bits(s)
+    deflate_state *s;
+{
+    bi_flush(s);
+}
+
+/* ===========================================================================
+ * Send one empty static block to give enough lookahead for inflate.
+ * This takes 10 bits, of which 7 may remain in the bit buffer.
+ */
+void ZLIB_INTERNAL _tr_align(s)
+    deflate_state *s;
+{
+    send_bits(s, STATIC_TREES<<1, 3);
+    send_code(s, END_BLOCK, static_ltree);
+#ifdef ZLIB_DEBUG
+    s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
+#endif
+    bi_flush(s);
+}
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and write out the encoded block.
+ */
+void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last)
+    deflate_state *s;
+    charf *buf;       /* input block, or NULL if too old */
+    ulg stored_len;   /* length of input block */
+    int last;         /* one if this is the last block for a file */
+{
+    ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
+    int max_blindex = 0;  /* index of last bit length code of non zero freq */
+
+    /* Build the Huffman trees unless a stored block is forced */
+    if (s->level > 0) {
+
+        /* Check if the file is binary or text */
+        if (s->strm->data_type == Z_UNKNOWN)
+            s->strm->data_type = detect_data_type(s);
+
+        /* Construct the literal and distance trees */
+        build_tree(s, (tree_desc *)(&(s->l_desc)));
+        Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
+                s->static_len));
+
+        build_tree(s, (tree_desc *)(&(s->d_desc)));
+        Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
+                s->static_len));
+        /* At this point, opt_len and static_len are the total bit lengths of
+         * the compressed block data, excluding the tree representations.
+         */
+
+        /* Build the bit length tree for the above two trees, and get the index
+         * in bl_order of the last bit length code to send.
+         */
+        max_blindex = build_bl_tree(s);
+
+        /* Determine the best encoding. Compute the block lengths in bytes. */
+        opt_lenb = (s->opt_len+3+7)>>3;
+        static_lenb = (s->static_len+3+7)>>3;
+
+        Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
+                opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
+                s->last_lit));
+
+        if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
+
+    } else {
+        Assert(buf != (char*)0, "lost buf");
+        opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
+    }
+
+#ifdef FORCE_STORED
+    if (buf != (char*)0) { /* force stored block */
+#else
+    if (stored_len+4 <= opt_lenb && buf != (char*)0) {
+                       /* 4: two words for the lengths */
+#endif
+        /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+         * Otherwise we can't have processed more than WSIZE input bytes since
+         * the last block flush, because compression would have been
+         * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+         * transform a block into a stored block.
+         */
+        _tr_stored_block(s, buf, stored_len, last);
+
+#ifdef FORCE_STATIC
+    } else if (static_lenb >= 0) { /* force static trees */
+#else
+    } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) {
+#endif
+        send_bits(s, (STATIC_TREES<<1)+last, 3);
+        compress_block(s, (const ct_data *)static_ltree,
+                       (const ct_data *)static_dtree);
+#ifdef ZLIB_DEBUG
+        s->compressed_len += 3 + s->static_len;
+#endif
+    } else {
+        send_bits(s, (DYN_TREES<<1)+last, 3);
+        send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
+                       max_blindex+1);
+        compress_block(s, (const ct_data *)s->dyn_ltree,
+                       (const ct_data *)s->dyn_dtree);
+#ifdef ZLIB_DEBUG
+        s->compressed_len += 3 + s->opt_len;
+#endif
+    }
+    Assert (s->compressed_len == s->bits_sent, "bad compressed size");
+    /* The above check is made mod 2^32, for files larger than 512 MB
+     * and uLong implemented on 32 bits.
+     */
+    init_block(s);
+
+    if (last) {
+        bi_windup(s);
+#ifdef ZLIB_DEBUG
+        s->compressed_len += 7;  /* align on byte boundary */
+#endif
+    }
+    Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
+           s->compressed_len-7*last));
+}
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+int ZLIB_INTERNAL _tr_tally (s, dist, lc)
+    deflate_state *s;
+    unsigned dist;  /* distance of matched string */
+    unsigned lc;    /* match length-MIN_MATCH or unmatched char (if dist==0) */
+{
+    s->d_buf[s->last_lit] = (ush)dist;
+    s->l_buf[s->last_lit++] = (uch)lc;
+    if (dist == 0) {
+        /* lc is the unmatched char */
+        s->dyn_ltree[lc].Freq++;
+    } else {
+        s->matches++;
+        /* Here, lc is the match length - MIN_MATCH */
+        dist--;             /* dist = match distance - 1 */
+        Assert((ush)dist < (ush)MAX_DIST(s) &&
+               (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
+               (ush)d_code(dist) < (ush)D_CODES,  "_tr_tally: bad match");
+
+        s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;
+        s->dyn_dtree[d_code(dist)].Freq++;
+    }
+
+#ifdef TRUNCATE_BLOCK
+    /* Try to guess if it is profitable to stop the current block here */
+    if ((s->last_lit & 0x1fff) == 0 && s->level > 2) {
+        /* Compute an upper bound for the compressed length */
+        ulg out_length = (ulg)s->last_lit*8L;
+        ulg in_length = (ulg)((long)s->strstart - s->block_start);
+        int dcode;
+        for (dcode = 0; dcode < D_CODES; dcode++) {
+            out_length += (ulg)s->dyn_dtree[dcode].Freq *
+                (5L+extra_dbits[dcode]);
+        }
+        out_length >>= 3;
+        Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
+               s->last_lit, in_length, out_length,
+               100L - out_length*100L/in_length));
+        if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
+    }
+#endif
+    return (s->last_lit == s->lit_bufsize-1);
+    /* We avoid equality with lit_bufsize because of wraparound at 64K
+     * on 16 bit machines and because stored blocks are restricted to
+     * 64K-1 bytes.
+     */
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+local void compress_block(s, ltree, dtree)
+    deflate_state *s;
+    const ct_data *ltree; /* literal tree */
+    const ct_data *dtree; /* distance tree */
+{
+    unsigned dist;      /* distance of matched string */
+    int lc;             /* match length or unmatched char (if dist == 0) */
+    unsigned lx = 0;    /* running index in l_buf */
+    unsigned code;      /* the code to send */
+    int extra;          /* number of extra bits to send */
+
+    if (s->last_lit != 0) do {
+        dist = s->d_buf[lx];
+        lc = s->l_buf[lx++];
+        if (dist == 0) {
+            send_code(s, lc, ltree); /* send a literal byte */
+            Tracecv(isgraph(lc), (stderr," '%c' ", lc));
+        } else {
+            /* Here, lc is the match length - MIN_MATCH */
+            code = _length_code[lc];
+            send_code(s, code+LITERALS+1, ltree); /* send the length code */
+            extra = extra_lbits[code];
+            if (extra != 0) {
+                lc -= base_length[code];
+                send_bits(s, lc, extra);       /* send the extra length bits */
+            }
+            dist--; /* dist is now the match distance - 1 */
+            code = d_code(dist);
+            Assert (code < D_CODES, "bad d_code");
+
+            send_code(s, code, dtree);       /* send the distance code */
+            extra = extra_dbits[code];
+            if (extra != 0) {
+                dist -= (unsigned)base_dist[code];
+                send_bits(s, dist, extra);   /* send the extra distance bits */
+            }
+        } /* literal or match pair ? */
+
+        /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
+        Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
+               "pendingBuf overflow");
+
+    } while (lx < s->last_lit);
+
+    send_code(s, END_BLOCK, ltree);
+}
+
+/* ===========================================================================
+ * Check if the data type is TEXT or BINARY, using the following algorithm:
+ * - TEXT if the two conditions below are satisfied:
+ *    a) There are no non-portable control characters belonging to the
+ *       "black list" (0..6, 14..25, 28..31).
+ *    b) There is at least one printable character belonging to the
+ *       "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
+ * - BINARY otherwise.
+ * - The following partially-portable control characters form a
+ *   "gray list" that is ignored in this detection algorithm:
+ *   (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
+ * IN assertion: the fields Freq of dyn_ltree are set.
+ */
+local int detect_data_type(s)
+    deflate_state *s;
+{
+    /* black_mask is the bit mask of black-listed bytes
+     * set bits 0..6, 14..25, and 28..31
+     * 0xf3ffc07f = binary 11110011111111111100000001111111
+     */
+    unsigned long black_mask = 0xf3ffc07fUL;
+    int n;
+
+    /* Check for non-textual ("black-listed") bytes. */
+    for (n = 0; n <= 31; n++, black_mask >>= 1)
+        if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0))
+            return Z_BINARY;
+
+    /* Check for textual ("white-listed") bytes. */
+    if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0
+            || s->dyn_ltree[13].Freq != 0)
+        return Z_TEXT;
+    for (n = 32; n < LITERALS; n++)
+        if (s->dyn_ltree[n].Freq != 0)
+            return Z_TEXT;
+
+    /* There are no "black-listed" or "white-listed" bytes:
+     * this stream either is empty or has tolerated ("gray-listed") bytes only.
+     */
+    return Z_BINARY;
+}
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+local unsigned bi_reverse(code, len)
+    unsigned code; /* the value to invert */
+    int len;       /* its bit length */
+{
+    register unsigned res = 0;
+    do {
+        res |= code & 1;
+        code >>= 1, res <<= 1;
+    } while (--len > 0);
+    return res >> 1;
+}
+
+/* ===========================================================================
+ * Flush the bit buffer, keeping at most 7 bits in it.
+ */
+local void bi_flush(s)
+    deflate_state *s;
+{
+    if (s->bi_valid == 16) {
+        put_short(s, s->bi_buf);
+        s->bi_buf = 0;
+        s->bi_valid = 0;
+    } else if (s->bi_valid >= 8) {
+        put_byte(s, (Byte)s->bi_buf);
+        s->bi_buf >>= 8;
+        s->bi_valid -= 8;
+    }
+}
+
+/* ===========================================================================
+ * Flush the bit buffer and align the output on a byte boundary
+ */
+local void bi_windup(s)
+    deflate_state *s;
+{
+    if (s->bi_valid > 8) {
+        put_short(s, s->bi_buf);
+    } else if (s->bi_valid > 0) {
+        put_byte(s, (Byte)s->bi_buf);
+    }
+    s->bi_buf = 0;
+    s->bi_valid = 0;
+#ifdef ZLIB_DEBUG
+    s->bits_sent = (s->bits_sent+7) & ~7;
+#endif
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/trees.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,128 @@
+/* header created automatically with -DGEN_TREES_H */
+
+local const ct_data static_ltree[L_CODES+2] = {
+{{ 12},{  8}}, {{140},{  8}}, {{ 76},{  8}}, {{204},{  8}}, {{ 44},{  8}},
+{{172},{  8}}, {{108},{  8}}, {{236},{  8}}, {{ 28},{  8}}, {{156},{  8}},
+{{ 92},{  8}}, {{220},{  8}}, {{ 60},{  8}}, {{188},{  8}}, {{124},{  8}},
+{{252},{  8}}, {{  2},{  8}}, {{130},{  8}}, {{ 66},{  8}}, {{194},{  8}},
+{{ 34},{  8}}, {{162},{  8}}, {{ 98},{  8}}, {{226},{  8}}, {{ 18},{  8}},
+{{146},{  8}}, {{ 82},{  8}}, {{210},{  8}}, {{ 50},{  8}}, {{178},{  8}},
+{{114},{  8}}, {{242},{  8}}, {{ 10},{  8}}, {{138},{  8}}, {{ 74},{  8}},
+{{202},{  8}}, {{ 42},{  8}}, {{170},{  8}}, {{106},{  8}}, {{234},{  8}},
+{{ 26},{  8}}, {{154},{  8}}, {{ 90},{  8}}, {{218},{  8}}, {{ 58},{  8}},
+{{186},{  8}}, {{122},{  8}}, {{250},{  8}}, {{  6},{  8}}, {{134},{  8}},
+{{ 70},{  8}}, {{198},{  8}}, {{ 38},{  8}}, {{166},{  8}}, {{102},{  8}},
+{{230},{  8}}, {{ 22},{  8}}, {{150},{  8}}, {{ 86},{  8}}, {{214},{  8}},
+{{ 54},{  8}}, {{182},{  8}}, {{118},{  8}}, {{246},{  8}}, {{ 14},{  8}},
+{{142},{  8}}, {{ 78},{  8}}, {{206},{  8}}, {{ 46},{  8}}, {{174},{  8}},
+{{110},{  8}}, {{238},{  8}}, {{ 30},{  8}}, {{158},{  8}}, {{ 94},{  8}},
+{{222},{  8}}, {{ 62},{  8}}, {{190},{  8}}, {{126},{  8}}, {{254},{  8}},
+{{  1},{  8}}, {{129},{  8}}, {{ 65},{  8}}, {{193},{  8}}, {{ 33},{  8}},
+{{161},{  8}}, {{ 97},{  8}}, {{225},{  8}}, {{ 17},{  8}}, {{145},{  8}},
+{{ 81},{  8}}, {{209},{  8}}, {{ 49},{  8}}, {{177},{  8}}, {{113},{  8}},
+{{241},{  8}}, {{  9},{  8}}, {{137},{  8}}, {{ 73},{  8}}, {{201},{  8}},
+{{ 41},{  8}}, {{169},{  8}}, {{105},{  8}}, {{233},{  8}}, {{ 25},{  8}},
+{{153},{  8}}, {{ 89},{  8}}, {{217},{  8}}, {{ 57},{  8}}, {{185},{  8}},
+{{121},{  8}}, {{249},{  8}}, {{  5},{  8}}, {{133},{  8}}, {{ 69},{  8}},
+{{197},{  8}}, {{ 37},{  8}}, {{165},{  8}}, {{101},{  8}}, {{229},{  8}},
+{{ 21},{  8}}, {{149},{  8}}, {{ 85},{  8}}, {{213},{  8}}, {{ 53},{  8}},
+{{181},{  8}}, {{117},{  8}}, {{245},{  8}}, {{ 13},{  8}}, {{141},{  8}},
+{{ 77},{  8}}, {{205},{  8}}, {{ 45},{  8}}, {{173},{  8}}, {{109},{  8}},
+{{237},{  8}}, {{ 29},{  8}}, {{157},{  8}}, {{ 93},{  8}}, {{221},{  8}},
+{{ 61},{  8}}, {{189},{  8}}, {{125},{  8}}, {{253},{  8}}, {{ 19},{  9}},
+{{275},{  9}}, {{147},{  9}}, {{403},{  9}}, {{ 83},{  9}}, {{339},{  9}},
+{{211},{  9}}, {{467},{  9}}, {{ 51},{  9}}, {{307},{  9}}, {{179},{  9}},
+{{435},{  9}}, {{115},{  9}}, {{371},{  9}}, {{243},{  9}}, {{499},{  9}},
+{{ 11},{  9}}, {{267},{  9}}, {{139},{  9}}, {{395},{  9}}, {{ 75},{  9}},
+{{331},{  9}}, {{203},{  9}}, {{459},{  9}}, {{ 43},{  9}}, {{299},{  9}},
+{{171},{  9}}, {{427},{  9}}, {{107},{  9}}, {{363},{  9}}, {{235},{  9}},
+{{491},{  9}}, {{ 27},{  9}}, {{283},{  9}}, {{155},{  9}}, {{411},{  9}},
+{{ 91},{  9}}, {{347},{  9}}, {{219},{  9}}, {{475},{  9}}, {{ 59},{  9}},
+{{315},{  9}}, {{187},{  9}}, {{443},{  9}}, {{123},{  9}}, {{379},{  9}},
+{{251},{  9}}, {{507},{  9}}, {{  7},{  9}}, {{263},{  9}}, {{135},{  9}},
+{{391},{  9}}, {{ 71},{  9}}, {{327},{  9}}, {{199},{  9}}, {{455},{  9}},
+{{ 39},{  9}}, {{295},{  9}}, {{167},{  9}}, {{423},{  9}}, {{103},{  9}},
+{{359},{  9}}, {{231},{  9}}, {{487},{  9}}, {{ 23},{  9}}, {{279},{  9}},
+{{151},{  9}}, {{407},{  9}}, {{ 87},{  9}}, {{343},{  9}}, {{215},{  9}},
+{{471},{  9}}, {{ 55},{  9}}, {{311},{  9}}, {{183},{  9}}, {{439},{  9}},
+{{119},{  9}}, {{375},{  9}}, {{247},{  9}}, {{503},{  9}}, {{ 15},{  9}},
+{{271},{  9}}, {{143},{  9}}, {{399},{  9}}, {{ 79},{  9}}, {{335},{  9}},
+{{207},{  9}}, {{463},{  9}}, {{ 47},{  9}}, {{303},{  9}}, {{175},{  9}},
+{{431},{  9}}, {{111},{  9}}, {{367},{  9}}, {{239},{  9}}, {{495},{  9}},
+{{ 31},{  9}}, {{287},{  9}}, {{159},{  9}}, {{415},{  9}}, {{ 95},{  9}},
+{{351},{  9}}, {{223},{  9}}, {{479},{  9}}, {{ 63},{  9}}, {{319},{  9}},
+{{191},{  9}}, {{447},{  9}}, {{127},{  9}}, {{383},{  9}}, {{255},{  9}},
+{{511},{  9}}, {{  0},{  7}}, {{ 64},{  7}}, {{ 32},{  7}}, {{ 96},{  7}},
+{{ 16},{  7}}, {{ 80},{  7}}, {{ 48},{  7}}, {{112},{  7}}, {{  8},{  7}},
+{{ 72},{  7}}, {{ 40},{  7}}, {{104},{  7}}, {{ 24},{  7}}, {{ 88},{  7}},
+{{ 56},{  7}}, {{120},{  7}}, {{  4},{  7}}, {{ 68},{  7}}, {{ 36},{  7}},
+{{100},{  7}}, {{ 20},{  7}}, {{ 84},{  7}}, {{ 52},{  7}}, {{116},{  7}},
+{{  3},{  8}}, {{131},{  8}}, {{ 67},{  8}}, {{195},{  8}}, {{ 35},{  8}},
+{{163},{  8}}, {{ 99},{  8}}, {{227},{  8}}
+};
+
+local const ct_data static_dtree[D_CODES] = {
+{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},
+{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},
+{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},
+{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},
+{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},
+{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}
+};
+
+const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {
+ 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,  0,  0, 16, 17,
+18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 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, 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, 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,
+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
+};
+
+const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {
+ 0,  1,  2,  3,  4,  5,  6,  7,  8,  8,  9,  9, 10, 10, 11, 11, 12, 12, 12, 12,
+13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
+17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
+19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
+22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 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,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
+};
+
+local const int base_length[LENGTH_CODES] = {
+0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
+64, 80, 96, 112, 128, 160, 192, 224, 0
+};
+
+local const int base_dist[D_CODES] = {
+    0,     1,     2,     3,     4,     6,     8,    12,    16,    24,
+   32,    48,    64,    96,   128,   192,   256,   384,   512,   768,
+ 1024,  1536,  2048,  3072,  4096,  6144,  8192, 12288, 16384, 24576
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/uncompr.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,93 @@
+/* uncompr.c -- decompress a memory buffer
+ * Copyright (C) 1995-2003, 2010, 2014, 2016 Jean-loup Gailly, Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+/* ===========================================================================
+     Decompresses the source buffer into the destination buffer.  *sourceLen is
+   the byte length of the source buffer. Upon entry, *destLen is the total size
+   of the destination buffer, which must be large enough to hold the entire
+   uncompressed data. (The size of the uncompressed data must have been saved
+   previously by the compressor and transmitted to the decompressor by some
+   mechanism outside the scope of this compression library.) Upon exit,
+   *destLen is the size of the decompressed data and *sourceLen is the number
+   of source bytes consumed. Upon return, source + *sourceLen points to the
+   first unused input byte.
+
+     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_BUF_ERROR if there was not enough room in the output buffer, or
+   Z_DATA_ERROR if the input data was corrupted, including if the input data is
+   an incomplete zlib stream.
+*/
+int ZEXPORT uncompress2 (dest, destLen, source, sourceLen)
+    Bytef *dest;
+    uLongf *destLen;
+    const Bytef *source;
+    uLong *sourceLen;
+{
+    z_stream stream;
+    int err;
+    const uInt max = (uInt)-1;
+    uLong len, left;
+    Byte buf[1];    /* for detection of incomplete stream when *destLen == 0 */
+
+    len = *sourceLen;
+    if (*destLen) {
+        left = *destLen;
+        *destLen = 0;
+    }
+    else {
+        left = 1;
+        dest = buf;
+    }
+
+    stream.next_in = (z_const Bytef *)source;
+    stream.avail_in = 0;
+    stream.zalloc = (alloc_func)0;
+    stream.zfree = (free_func)0;
+    stream.opaque = (voidpf)0;
+
+    err = inflateInit(&stream);
+    if (err != Z_OK) return err;
+
+    stream.next_out = dest;
+    stream.avail_out = 0;
+
+    do {
+        if (stream.avail_out == 0) {
+            stream.avail_out = left > (uLong)max ? max : (uInt)left;
+            left -= stream.avail_out;
+        }
+        if (stream.avail_in == 0) {
+            stream.avail_in = len > (uLong)max ? max : (uInt)len;
+            len -= stream.avail_in;
+        }
+        err = inflate(&stream, Z_NO_FLUSH);
+    } while (err == Z_OK);
+
+    *sourceLen -= len + stream.avail_in;
+    if (dest != buf)
+        *destLen = stream.total_out;
+    else if (stream.total_out && err == Z_BUF_ERROR)
+        left = 1;
+
+    inflateEnd(&stream);
+    return err == Z_STREAM_END ? Z_OK :
+           err == Z_NEED_DICT ? Z_DATA_ERROR  :
+           err == Z_BUF_ERROR && left + stream.avail_out ? Z_DATA_ERROR :
+           err;
+}
+
+int ZEXPORT uncompress (dest, destLen, source, sourceLen)
+    Bytef *dest;
+    uLongf *destLen;
+    const Bytef *source;
+    uLong sourceLen;
+{
+    return uncompress2(dest, destLen, source, &sourceLen);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/zconf.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,534 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZCONF_H
+#define ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ * Even better than compiling with -DZ_PREFIX would be to use configure to set
+ * this permanently in zconf.h using "./configure --zprefix".
+ */
+#ifdef Z_PREFIX     /* may be set to #if 1 by ./configure */
+#  define Z_PREFIX_SET
+
+/* all linked symbols and init macros */
+#  define _dist_code            z__dist_code
+#  define _length_code          z__length_code
+#  define _tr_align             z__tr_align
+#  define _tr_flush_bits        z__tr_flush_bits
+#  define _tr_flush_block       z__tr_flush_block
+#  define _tr_init              z__tr_init
+#  define _tr_stored_block      z__tr_stored_block
+#  define _tr_tally             z__tr_tally
+#  define adler32               z_adler32
+#  define adler32_combine       z_adler32_combine
+#  define adler32_combine64     z_adler32_combine64
+#  define adler32_z             z_adler32_z
+#  ifndef Z_SOLO
+#    define compress              z_compress
+#    define compress2             z_compress2
+#    define compressBound         z_compressBound
+#  endif
+#  define crc32                 z_crc32
+#  define crc32_combine         z_crc32_combine
+#  define crc32_combine64       z_crc32_combine64
+#  define crc32_z               z_crc32_z
+#  define deflate               z_deflate
+#  define deflateBound          z_deflateBound
+#  define deflateCopy           z_deflateCopy
+#  define deflateEnd            z_deflateEnd
+#  define deflateGetDictionary  z_deflateGetDictionary
+#  define deflateInit           z_deflateInit
+#  define deflateInit2          z_deflateInit2
+#  define deflateInit2_         z_deflateInit2_
+#  define deflateInit_          z_deflateInit_
+#  define deflateParams         z_deflateParams
+#  define deflatePending        z_deflatePending
+#  define deflatePrime          z_deflatePrime
+#  define deflateReset          z_deflateReset
+#  define deflateResetKeep      z_deflateResetKeep
+#  define deflateSetDictionary  z_deflateSetDictionary
+#  define deflateSetHeader      z_deflateSetHeader
+#  define deflateTune           z_deflateTune
+#  define deflate_copyright     z_deflate_copyright
+#  define get_crc_table         z_get_crc_table
+#  ifndef Z_SOLO
+#    define gz_error              z_gz_error
+#    define gz_intmax             z_gz_intmax
+#    define gz_strwinerror        z_gz_strwinerror
+#    define gzbuffer              z_gzbuffer
+#    define gzclearerr            z_gzclearerr
+#    define gzclose               z_gzclose
+#    define gzclose_r             z_gzclose_r
+#    define gzclose_w             z_gzclose_w
+#    define gzdirect              z_gzdirect
+#    define gzdopen               z_gzdopen
+#    define gzeof                 z_gzeof
+#    define gzerror               z_gzerror
+#    define gzflush               z_gzflush
+#    define gzfread               z_gzfread
+#    define gzfwrite              z_gzfwrite
+#    define gzgetc                z_gzgetc
+#    define gzgetc_               z_gzgetc_
+#    define gzgets                z_gzgets
+#    define gzoffset              z_gzoffset
+#    define gzoffset64            z_gzoffset64
+#    define gzopen                z_gzopen
+#    define gzopen64              z_gzopen64
+#    ifdef _WIN32
+#      define gzopen_w              z_gzopen_w
+#    endif
+#    define gzprintf              z_gzprintf
+#    define gzputc                z_gzputc
+#    define gzputs                z_gzputs
+#    define gzread                z_gzread
+#    define gzrewind              z_gzrewind
+#    define gzseek                z_gzseek
+#    define gzseek64              z_gzseek64
+#    define gzsetparams           z_gzsetparams
+#    define gztell                z_gztell
+#    define gztell64              z_gztell64
+#    define gzungetc              z_gzungetc
+#    define gzvprintf             z_gzvprintf
+#    define gzwrite               z_gzwrite
+#  endif
+#  define inflate               z_inflate
+#  define inflateBack           z_inflateBack
+#  define inflateBackEnd        z_inflateBackEnd
+#  define inflateBackInit       z_inflateBackInit
+#  define inflateBackInit_      z_inflateBackInit_
+#  define inflateCodesUsed      z_inflateCodesUsed
+#  define inflateCopy           z_inflateCopy
+#  define inflateEnd            z_inflateEnd
+#  define inflateGetDictionary  z_inflateGetDictionary
+#  define inflateGetHeader      z_inflateGetHeader
+#  define inflateInit           z_inflateInit
+#  define inflateInit2          z_inflateInit2
+#  define inflateInit2_         z_inflateInit2_
+#  define inflateInit_          z_inflateInit_
+#  define inflateMark           z_inflateMark
+#  define inflatePrime          z_inflatePrime
+#  define inflateReset          z_inflateReset
+#  define inflateReset2         z_inflateReset2
+#  define inflateResetKeep      z_inflateResetKeep
+#  define inflateSetDictionary  z_inflateSetDictionary
+#  define inflateSync           z_inflateSync
+#  define inflateSyncPoint      z_inflateSyncPoint
+#  define inflateUndermine      z_inflateUndermine
+#  define inflateValidate       z_inflateValidate
+#  define inflate_copyright     z_inflate_copyright
+#  define inflate_fast          z_inflate_fast
+#  define inflate_table         z_inflate_table
+#  ifndef Z_SOLO
+#    define uncompress            z_uncompress
+#    define uncompress2           z_uncompress2
+#  endif
+#  define zError                z_zError
+#  ifndef Z_SOLO
+#    define zcalloc               z_zcalloc
+#    define zcfree                z_zcfree
+#  endif
+#  define zlibCompileFlags      z_zlibCompileFlags
+#  define zlibVersion           z_zlibVersion
+
+/* all zlib typedefs in zlib.h and zconf.h */
+#  define Byte                  z_Byte
+#  define Bytef                 z_Bytef
+#  define alloc_func            z_alloc_func
+#  define charf                 z_charf
+#  define free_func             z_free_func
+#  ifndef Z_SOLO
+#    define gzFile                z_gzFile
+#  endif
+#  define gz_header             z_gz_header
+#  define gz_headerp            z_gz_headerp
+#  define in_func               z_in_func
+#  define intf                  z_intf
+#  define out_func              z_out_func
+#  define uInt                  z_uInt
+#  define uIntf                 z_uIntf
+#  define uLong                 z_uLong
+#  define uLongf                z_uLongf
+#  define voidp                 z_voidp
+#  define voidpc                z_voidpc
+#  define voidpf                z_voidpf
+
+/* all zlib structs in zlib.h and zconf.h */
+#  define gz_header_s           z_gz_header_s
+#  define internal_state        z_internal_state
+
+#endif
+
+#if defined(__MSDOS__) && !defined(MSDOS)
+#  define MSDOS
+#endif
+#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
+#  define OS2
+#endif
+#if defined(_WINDOWS) && !defined(WINDOWS)
+#  define WINDOWS
+#endif
+#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
+#  ifndef WIN32
+#    define WIN32
+#  endif
+#endif
+#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
+#  if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
+#    ifndef SYS16BIT
+#      define SYS16BIT
+#    endif
+#  endif
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#ifdef SYS16BIT
+#  define MAXSEG_64K
+#endif
+#ifdef MSDOS
+#  define UNALIGNED_OK
+#endif
+
+#ifdef __STDC_VERSION__
+#  ifndef STDC
+#    define STDC
+#  endif
+#  if __STDC_VERSION__ >= 199901L
+#    ifndef STDC99
+#      define STDC99
+#    endif
+#  endif
+#endif
+#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
+#  define STDC
+#endif
+#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
+#  define STDC
+#endif
+#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
+#  define STDC
+#endif
+#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
+#  define STDC
+#endif
+
+#if defined(__OS400__) && !defined(STDC)    /* iSeries (formerly AS/400). */
+#  define STDC
+#endif
+
+#ifndef STDC
+#  ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+#    define const       /* note: need a more gentle solution here */
+#  endif
+#endif
+
+#if defined(ZLIB_CONST) && !defined(z_const)
+#  define z_const const
+#else
+#  define z_const
+#endif
+
+#ifdef Z_SOLO
+   typedef unsigned long z_size_t;
+#else
+#  define z_longlong long long
+#  if defined(NO_SIZE_T)
+     typedef unsigned NO_SIZE_T z_size_t;
+#  elif defined(STDC)
+#    include <stddef.h>
+     typedef size_t z_size_t;
+#  else
+     typedef unsigned long z_size_t;
+#  endif
+#  undef z_longlong
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+#  ifdef MAXSEG_64K
+#    define MAX_MEM_LEVEL 8
+#  else
+#    define MAX_MEM_LEVEL 9
+#  endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+#  define MAX_WBITS   15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+            (1 << (windowBits+2)) +  (1 << (memLevel+9))
+ that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+     make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+   The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus about 7 kilobytes
+ for small objects.
+*/
+
+                        /* Type declarations */
+
+#ifndef OF /* function prototypes */
+#  ifdef STDC
+#    define OF(args)  args
+#  else
+#    define OF(args)  ()
+#  endif
+#endif
+
+#ifndef Z_ARG /* function prototypes for stdarg */
+#  if defined(STDC) || defined(Z_HAVE_STDARG_H)
+#    define Z_ARG(args)  args
+#  else
+#    define Z_ARG(args)  ()
+#  endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h.  If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#ifdef SYS16BIT
+#  if defined(M_I86SM) || defined(M_I86MM)
+     /* MSC small or medium model */
+#    define SMALL_MEDIUM
+#    ifdef _MSC_VER
+#      define FAR _far
+#    else
+#      define FAR far
+#    endif
+#  endif
+#  if (defined(__SMALL__) || defined(__MEDIUM__))
+     /* Turbo C small or medium model */
+#    define SMALL_MEDIUM
+#    ifdef __BORLANDC__
+#      define FAR _far
+#    else
+#      define FAR far
+#    endif
+#  endif
+#endif
+
+#if defined(WINDOWS) || defined(WIN32)
+   /* If building or using zlib as a DLL, define ZLIB_DLL.
+    * This is not mandatory, but it offers a little performance increase.
+    */
+#  ifdef ZLIB_DLL
+#    if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
+#      ifdef ZLIB_INTERNAL
+#        define ZEXTERN extern __declspec(dllexport)
+#      else
+#        define ZEXTERN extern __declspec(dllimport)
+#      endif
+#    endif
+#  endif  /* ZLIB_DLL */
+   /* If building or using zlib with the WINAPI/WINAPIV calling convention,
+    * define ZLIB_WINAPI.
+    * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
+    */
+#  ifdef ZLIB_WINAPI
+#    ifdef FAR
+#      undef FAR
+#    endif
+#    include <windows.h>
+     /* No need for _export, use ZLIB.DEF instead. */
+     /* For complete Windows compatibility, use WINAPI, not __stdcall. */
+#    define ZEXPORT WINAPI
+#    ifdef WIN32
+#      define ZEXPORTVA WINAPIV
+#    else
+#      define ZEXPORTVA FAR CDECL
+#    endif
+#  endif
+#endif
+
+#if defined (__BEOS__)
+#  ifdef ZLIB_DLL
+#    ifdef ZLIB_INTERNAL
+#      define ZEXPORT   __declspec(dllexport)
+#      define ZEXPORTVA __declspec(dllexport)
+#    else
+#      define ZEXPORT   __declspec(dllimport)
+#      define ZEXPORTVA __declspec(dllimport)
+#    endif
+#  endif
+#endif
+
+#ifndef ZEXTERN
+#  define ZEXTERN extern
+#endif
+#ifndef ZEXPORT
+#  define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+#  define ZEXPORTVA
+#endif
+
+#ifndef FAR
+#  define FAR
+#endif
+
+#if !defined(__MACTYPES__)
+typedef unsigned char  Byte;  /* 8 bits */
+#endif
+typedef unsigned int   uInt;  /* 16 bits or more */
+typedef unsigned long  uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+   /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+#  define Bytef Byte FAR
+#else
+   typedef Byte  FAR Bytef;
+#endif
+typedef char  FAR charf;
+typedef int   FAR intf;
+typedef uInt  FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+   typedef void const *voidpc;
+   typedef void FAR   *voidpf;
+   typedef void       *voidp;
+#else
+   typedef Byte const *voidpc;
+   typedef Byte FAR   *voidpf;
+   typedef Byte       *voidp;
+#endif
+
+#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC)
+#  include <limits.h>
+#  if (UINT_MAX == 0xffffffffUL)
+#    define Z_U4 unsigned
+#  elif (ULONG_MAX == 0xffffffffUL)
+#    define Z_U4 unsigned long
+#  elif (USHRT_MAX == 0xffffffffUL)
+#    define Z_U4 unsigned short
+#  endif
+#endif
+
+#ifdef Z_U4
+   typedef Z_U4 z_crc_t;
+#else
+   typedef unsigned long z_crc_t;
+#endif
+
+#ifdef HAVE_UNISTD_H    /* may be set to #if 1 by ./configure */
+#  define Z_HAVE_UNISTD_H
+#endif
+
+#ifdef HAVE_STDARG_H    /* may be set to #if 1 by ./configure */
+#  define Z_HAVE_STDARG_H
+#endif
+
+#ifdef STDC
+#  ifndef Z_SOLO
+#    include <sys/types.h>      /* for off_t */
+#  endif
+#endif
+
+#if defined(STDC) || defined(Z_HAVE_STDARG_H)
+#  ifndef Z_SOLO
+#    include <stdarg.h>         /* for va_list */
+#  endif
+#endif
+
+#ifdef _WIN32
+#  ifndef Z_SOLO
+#    include <stddef.h>         /* for wchar_t */
+#  endif
+#endif
+
+/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and
+ * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even
+ * though the former does not conform to the LFS document), but considering
+ * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as
+ * equivalently requesting no 64-bit operations
+ */
+#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1
+#  undef _LARGEFILE64_SOURCE
+#endif
+
+#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H)
+#  define Z_HAVE_UNISTD_H
+#endif
+#ifndef Z_SOLO
+#  if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE)
+#    include <unistd.h>         /* for SEEK_*, off_t, and _LFS64_LARGEFILE */
+#    ifdef VMS
+#      include <unixio.h>       /* for off_t */
+#    endif
+#    ifndef z_off_t
+#      define z_off_t off_t
+#    endif
+#  endif
+#endif
+
+#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0
+#  define Z_LFS64
+#endif
+
+#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64)
+#  define Z_LARGE64
+#endif
+
+#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64)
+#  define Z_WANT64
+#endif
+
+#if !defined(SEEK_SET) && !defined(Z_SOLO)
+#  define SEEK_SET        0       /* Seek from beginning of file.  */
+#  define SEEK_CUR        1       /* Seek from current position.  */
+#  define SEEK_END        2       /* Set file pointer to EOF plus "offset" */
+#endif
+
+#ifndef z_off_t
+#  define z_off_t long
+#endif
+
+#if !defined(_WIN32) && defined(Z_LARGE64)
+#  define z_off64_t off64_t
+#else
+#  if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO)
+#    define z_off64_t __int64
+#  else
+#    define z_off64_t z_off_t
+#  endif
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+  #pragma map(deflateInit_,"DEIN")
+  #pragma map(deflateInit2_,"DEIN2")
+  #pragma map(deflateEnd,"DEEND")
+  #pragma map(deflateBound,"DEBND")
+  #pragma map(inflateInit_,"ININ")
+  #pragma map(inflateInit2_,"ININ2")
+  #pragma map(inflateEnd,"INEND")
+  #pragma map(inflateSync,"INSY")
+  #pragma map(inflateSetDictionary,"INSEDI")
+  #pragma map(compressBound,"CMBND")
+  #pragma map(inflate_table,"INTABL")
+  #pragma map(inflate_fast,"INFA")
+  #pragma map(inflate_copyright,"INCOPY")
+#endif
+
+#endif /* ZCONF_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/zlib.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,1912 @@
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+  version 1.2.11, January 15th, 2017
+
+  Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
+
+  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.
+
+  Jean-loup Gailly        Mark Adler
+  jloup@gzip.org          madler@alumni.caltech.edu
+
+
+  The data format used by the zlib library is described by RFCs (Request for
+  Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+  (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
+*/
+
+#ifndef ZLIB_H
+#define ZLIB_H
+
+#include "zconf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_VERSION "1.2.11"
+#define ZLIB_VERNUM 0x12b0
+#define ZLIB_VER_MAJOR 1
+#define ZLIB_VER_MINOR 2
+#define ZLIB_VER_REVISION 11
+#define ZLIB_VER_SUBREVISION 0
+
+/*
+    The 'zlib' compression library provides in-memory compression and
+  decompression functions, including integrity checks of the uncompressed data.
+  This version of the library supports only one compression method (deflation)
+  but other algorithms will be added later and will have the same stream
+  interface.
+
+    Compression can be done in a single step if the buffers are large enough,
+  or can be done by repeated calls of the compression function.  In the latter
+  case, the application must provide more input and/or consume the output
+  (providing more output space) before each call.
+
+    The compressed data format used by default by the in-memory functions is
+  the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped
+  around a deflate stream, which is itself documented in RFC 1951.
+
+    The library also supports reading and writing files in gzip (.gz) format
+  with an interface similar to that of stdio using the functions that start
+  with "gz".  The gzip format is different from the zlib format.  gzip is a
+  gzip wrapper, documented in RFC 1952, wrapped around a deflate stream.
+
+    This library can optionally read and write gzip and raw deflate streams in
+  memory as well.
+
+    The zlib format was designed to be compact and fast for use in memory
+  and on communications channels.  The gzip format was designed for single-
+  file compression on file systems, has a larger header than zlib to maintain
+  directory information, and uses a different, slower check method than zlib.
+
+    The library does not install any signal handler.  The decoder checks
+  the consistency of the compressed data, so the library should never crash
+  even in the case of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void   (*free_func)  OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+    z_const Bytef *next_in;     /* next input byte */
+    uInt     avail_in;  /* number of bytes available at next_in */
+    uLong    total_in;  /* total number of input bytes read so far */
+
+    Bytef    *next_out; /* next output byte will go here */
+    uInt     avail_out; /* remaining free space at next_out */
+    uLong    total_out; /* total number of bytes output so far */
+
+    z_const char *msg;  /* last error message, NULL if no error */
+    struct internal_state FAR *state; /* not visible by applications */
+
+    alloc_func zalloc;  /* used to allocate the internal state */
+    free_func  zfree;   /* used to free the internal state */
+    voidpf     opaque;  /* private data object passed to zalloc and zfree */
+
+    int     data_type;  /* best guess about the data type: binary or text
+                           for deflate, or the decoding state for inflate */
+    uLong   adler;      /* Adler-32 or CRC-32 value of the uncompressed data */
+    uLong   reserved;   /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+     gzip header information passed to and from zlib routines.  See RFC 1952
+  for more details on the meanings of these fields.
+*/
+typedef struct gz_header_s {
+    int     text;       /* true if compressed data believed to be text */
+    uLong   time;       /* modification time */
+    int     xflags;     /* extra flags (not used when writing a gzip file) */
+    int     os;         /* operating system */
+    Bytef   *extra;     /* pointer to extra field or Z_NULL if none */
+    uInt    extra_len;  /* extra field length (valid if extra != Z_NULL) */
+    uInt    extra_max;  /* space at extra (only when reading header) */
+    Bytef   *name;      /* pointer to zero-terminated file name or Z_NULL */
+    uInt    name_max;   /* space at name (only when reading header) */
+    Bytef   *comment;   /* pointer to zero-terminated comment or Z_NULL */
+    uInt    comm_max;   /* space at comment (only when reading header) */
+    int     hcrc;       /* true if there was or will be a header crc */
+    int     done;       /* true when done reading gzip header (not used
+                           when writing a gzip file) */
+} gz_header;
+
+typedef gz_header FAR *gz_headerp;
+
+/*
+     The application must update next_in and avail_in when avail_in has dropped
+   to zero.  It must update next_out and avail_out when avail_out has dropped
+   to zero.  The application must initialize zalloc, zfree and opaque before
+   calling the init function.  All other fields are set by the compression
+   library and must not be updated by the application.
+
+     The opaque value provided by the application will be passed as the first
+   parameter for calls of zalloc and zfree.  This can be useful for custom
+   memory management.  The compression library attaches no meaning to the
+   opaque value.
+
+     zalloc must return Z_NULL if there is not enough memory for the object.
+   If zlib is used in a multi-threaded application, zalloc and zfree must be
+   thread safe.  In that case, zlib is thread-safe.  When zalloc and zfree are
+   Z_NULL on entry to the initialization function, they are set to internal
+   routines that use the standard library functions malloc() and free().
+
+     On 16-bit systems, the functions zalloc and zfree must be able to allocate
+   exactly 65536 bytes, but will not be required to allocate more than this if
+   the symbol MAXSEG_64K is defined (see zconf.h).  WARNING: On MSDOS, pointers
+   returned by zalloc for objects of exactly 65536 bytes *must* have their
+   offset normalized to zero.  The default allocation function provided by this
+   library ensures this (see zutil.c).  To reduce memory requirements and avoid
+   any allocation of 64K objects, at the expense of compression ratio, compile
+   the library with -DMAX_WBITS=14 (see zconf.h).
+
+     The fields total_in and total_out can be used for statistics or progress
+   reports.  After compression, total_in holds the total size of the
+   uncompressed data and may be saved for use by the decompressor (particularly
+   if the decompressor wants to decompress everything in a single step).
+*/
+
+                        /* constants */
+
+#define Z_NO_FLUSH      0
+#define Z_PARTIAL_FLUSH 1
+#define Z_SYNC_FLUSH    2
+#define Z_FULL_FLUSH    3
+#define Z_FINISH        4
+#define Z_BLOCK         5
+#define Z_TREES         6
+/* Allowed flush values; see deflate() and inflate() below for details */
+
+#define Z_OK            0
+#define Z_STREAM_END    1
+#define Z_NEED_DICT     2
+#define Z_ERRNO        (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR   (-3)
+#define Z_MEM_ERROR    (-4)
+#define Z_BUF_ERROR    (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative values
+ * are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION         0
+#define Z_BEST_SPEED             1
+#define Z_BEST_COMPRESSION       9
+#define Z_DEFAULT_COMPRESSION  (-1)
+/* compression levels */
+
+#define Z_FILTERED            1
+#define Z_HUFFMAN_ONLY        2
+#define Z_RLE                 3
+#define Z_FIXED               4
+#define Z_DEFAULT_STRATEGY    0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY   0
+#define Z_TEXT     1
+#define Z_ASCII    Z_TEXT   /* for compatibility with 1.2.2 and earlier */
+#define Z_UNKNOWN  2
+/* Possible values of the data_type field for deflate() */
+
+#define Z_DEFLATED   8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL  0  /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+
+                        /* basic functions */
+
+ZEXTERN const char * ZEXPORT zlibVersion OF((void));
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+   If the first character differs, the library code actually used is not
+   compatible with the zlib.h header file used by the application.  This check
+   is automatically made by deflateInit and inflateInit.
+ */
+
+/*
+ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
+
+     Initializes the internal stream state for compression.  The fields
+   zalloc, zfree and opaque must be initialized before by the caller.  If
+   zalloc and zfree are set to Z_NULL, deflateInit updates them to use default
+   allocation functions.
+
+     The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+   1 gives best speed, 9 gives best compression, 0 gives no compression at all
+   (the input data is simply copied a block at a time).  Z_DEFAULT_COMPRESSION
+   requests a default compromise between speed and compression (currently
+   equivalent to level 6).
+
+     deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_STREAM_ERROR if level is not a valid compression level, or
+   Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+   with the version assumed by the caller (ZLIB_VERSION).  msg is set to null
+   if there is no error message.  deflateInit does not perform any compression:
+   this will be done by deflate().
+*/
+
+
+ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
+/*
+    deflate compresses as much data as possible, and stops when the input
+  buffer becomes empty or the output buffer becomes full.  It may introduce
+  some output latency (reading input without producing any output) except when
+  forced to flush.
+
+    The detailed semantics are as follows.  deflate performs one or both of the
+  following actions:
+
+  - Compress more input starting at next_in and update next_in and avail_in
+    accordingly.  If not all input can be processed (because there is not
+    enough room in the output buffer), next_in and avail_in are updated and
+    processing will resume at this point for the next call of deflate().
+
+  - Generate more output starting at next_out and update next_out and avail_out
+    accordingly.  This action is forced if the parameter flush is non zero.
+    Forcing flush frequently degrades the compression ratio, so this parameter
+    should be set only when necessary.  Some output may be provided even if
+    flush is zero.
+
+    Before the call of deflate(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming more
+  output, and updating avail_in or avail_out accordingly; avail_out should
+  never be zero before the call.  The application can consume the compressed
+  output when it wants, for example when the output buffer is full (avail_out
+  == 0), or after each call of deflate().  If deflate returns Z_OK and with
+  zero avail_out, it must be called again after making room in the output
+  buffer because there might be more output pending. See deflatePending(),
+  which can be used if desired to determine whether or not there is more ouput
+  in that case.
+
+    Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to
+  decide how much data to accumulate before producing output, in order to
+  maximize compression.
+
+    If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
+  flushed to the output buffer and the output is aligned on a byte boundary, so
+  that the decompressor can get all input data available so far.  (In
+  particular avail_in is zero after the call if enough output space has been
+  provided before the call.) Flushing may degrade compression for some
+  compression algorithms and so it should be used only when necessary.  This
+  completes the current deflate block and follows it with an empty stored block
+  that is three bits plus filler bits to the next byte, followed by four bytes
+  (00 00 ff ff).
+
+    If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the
+  output buffer, but the output is not aligned to a byte boundary.  All of the
+  input data so far will be available to the decompressor, as for Z_SYNC_FLUSH.
+  This completes the current deflate block and follows it with an empty fixed
+  codes block that is 10 bits long.  This assures that enough bytes are output
+  in order for the decompressor to finish the block before the empty fixed
+  codes block.
+
+    If flush is set to Z_BLOCK, a deflate block is completed and emitted, as
+  for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to
+  seven bits of the current block are held to be written as the next byte after
+  the next deflate block is completed.  In this case, the decompressor may not
+  be provided enough bits at this point in order to complete decompression of
+  the data provided so far to the compressor.  It may need to wait for the next
+  block to be emitted.  This is for advanced applications that need to control
+  the emission of deflate blocks.
+
+    If flush is set to Z_FULL_FLUSH, all output is flushed as with
+  Z_SYNC_FLUSH, and the compression state is reset so that decompression can
+  restart from this point if previous compressed data has been damaged or if
+  random access is desired.  Using Z_FULL_FLUSH too often can seriously degrade
+  compression.
+
+    If deflate returns with avail_out == 0, this function must be called again
+  with the same value of the flush parameter and more output space (updated
+  avail_out), until the flush is complete (deflate returns with non-zero
+  avail_out).  In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
+  avail_out is greater than six to avoid repeated flush markers due to
+  avail_out == 0 on return.
+
+    If the parameter flush is set to Z_FINISH, pending input is processed,
+  pending output is flushed and deflate returns with Z_STREAM_END if there was
+  enough output space.  If deflate returns with Z_OK or Z_BUF_ERROR, this
+  function must be called again with Z_FINISH and more output space (updated
+  avail_out) but no more input data, until it returns with Z_STREAM_END or an
+  error.  After deflate has returned Z_STREAM_END, the only possible operations
+  on the stream are deflateReset or deflateEnd.
+
+    Z_FINISH can be used in the first deflate call after deflateInit if all the
+  compression is to be done in a single step.  In order to complete in one
+  call, avail_out must be at least the value returned by deflateBound (see
+  below).  Then deflate is guaranteed to return Z_STREAM_END.  If not enough
+  output space is provided, deflate will not return Z_STREAM_END, and it must
+  be called again as described above.
+
+    deflate() sets strm->adler to the Adler-32 checksum of all input read
+  so far (that is, total_in bytes).  If a gzip stream is being generated, then
+  strm->adler will be the CRC-32 checksum of the input read so far.  (See
+  deflateInit2 below.)
+
+    deflate() may update strm->data_type if it can make a good guess about
+  the input data type (Z_BINARY or Z_TEXT).  If in doubt, the data is
+  considered binary.  This field is only for information purposes and does not
+  affect the compression algorithm in any manner.
+
+    deflate() returns Z_OK if some progress has been made (more input
+  processed or more output produced), Z_STREAM_END if all input has been
+  consumed and all output has been produced (only when flush is set to
+  Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+  if next_in or next_out was Z_NULL or the state was inadvertently written over
+  by the application), or Z_BUF_ERROR if no progress is possible (for example
+  avail_in or avail_out was zero).  Note that Z_BUF_ERROR is not fatal, and
+  deflate() can be called again with more input and more output space to
+  continue compressing.
+*/
+
+
+ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
+/*
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any pending
+   output.
+
+     deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+   stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+   prematurely (some input or output was discarded).  In the error case, msg
+   may be set but then points to a static string (which must not be
+   deallocated).
+*/
+
+
+/*
+ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
+
+     Initializes the internal stream state for decompression.  The fields
+   next_in, avail_in, zalloc, zfree and opaque must be initialized before by
+   the caller.  In the current version of inflate, the provided input is not
+   read or consumed.  The allocation of a sliding window will be deferred to
+   the first call of inflate (if the decompression does not complete on the
+   first call).  If zalloc and zfree are set to Z_NULL, inflateInit updates
+   them to use default allocation functions.
+
+     inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+   version assumed by the caller, or Z_STREAM_ERROR if the parameters are
+   invalid, such as a null pointer to the structure.  msg is set to null if
+   there is no error message.  inflateInit does not perform any decompression.
+   Actual decompression will be done by inflate().  So next_in, and avail_in,
+   next_out, and avail_out are unused and unchanged.  The current
+   implementation of inflateInit() does not process any header information --
+   that is deferred until inflate() is called.
+*/
+
+
+ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
+/*
+    inflate decompresses as much data as possible, and stops when the input
+  buffer becomes empty or the output buffer becomes full.  It may introduce
+  some output latency (reading input without producing any output) except when
+  forced to flush.
+
+  The detailed semantics are as follows.  inflate performs one or both of the
+  following actions:
+
+  - Decompress more input starting at next_in and update next_in and avail_in
+    accordingly.  If not all input can be processed (because there is not
+    enough room in the output buffer), then next_in and avail_in are updated
+    accordingly, and processing will resume at this point for the next call of
+    inflate().
+
+  - Generate more output starting at next_out and update next_out and avail_out
+    accordingly.  inflate() provides as much output as possible, until there is
+    no more input data or no more space in the output buffer (see below about
+    the flush parameter).
+
+    Before the call of inflate(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming more
+  output, and updating the next_* and avail_* values accordingly.  If the
+  caller of inflate() does not provide both available input and available
+  output space, it is possible that there will be no progress made.  The
+  application can consume the uncompressed output when it wants, for example
+  when the output buffer is full (avail_out == 0), or after each call of
+  inflate().  If inflate returns Z_OK and with zero avail_out, it must be
+  called again after making room in the output buffer because there might be
+  more output pending.
+
+    The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH,
+  Z_BLOCK, or Z_TREES.  Z_SYNC_FLUSH requests that inflate() flush as much
+  output as possible to the output buffer.  Z_BLOCK requests that inflate()
+  stop if and when it gets to the next deflate block boundary.  When decoding
+  the zlib or gzip format, this will cause inflate() to return immediately
+  after the header and before the first block.  When doing a raw inflate,
+  inflate() will go ahead and process the first block, and will return when it
+  gets to the end of that block, or when it runs out of data.
+
+    The Z_BLOCK option assists in appending to or combining deflate streams.
+  To assist in this, on return inflate() always sets strm->data_type to the
+  number of unused bits in the last byte taken from strm->next_in, plus 64 if
+  inflate() is currently decoding the last block in the deflate stream, plus
+  128 if inflate() returned immediately after decoding an end-of-block code or
+  decoding the complete header up to just before the first byte of the deflate
+  stream.  The end-of-block will not be indicated until all of the uncompressed
+  data from that block has been written to strm->next_out.  The number of
+  unused bits may in general be greater than seven, except when bit 7 of
+  data_type is set, in which case the number of unused bits will be less than
+  eight.  data_type is set as noted here every time inflate() returns for all
+  flush options, and so can be used to determine the amount of currently
+  consumed input in bits.
+
+    The Z_TREES option behaves as Z_BLOCK does, but it also returns when the
+  end of each deflate block header is reached, before any actual data in that
+  block is decoded.  This allows the caller to determine the length of the
+  deflate block header for later use in random access within a deflate block.
+  256 is added to the value of strm->data_type when inflate() returns
+  immediately after reaching the end of the deflate block header.
+
+    inflate() should normally be called until it returns Z_STREAM_END or an
+  error.  However if all decompression is to be performed in a single step (a
+  single call of inflate), the parameter flush should be set to Z_FINISH.  In
+  this case all pending input is processed and all pending output is flushed;
+  avail_out must be large enough to hold all of the uncompressed data for the
+  operation to complete.  (The size of the uncompressed data may have been
+  saved by the compressor for this purpose.)  The use of Z_FINISH is not
+  required to perform an inflation in one step.  However it may be used to
+  inform inflate that a faster approach can be used for the single inflate()
+  call.  Z_FINISH also informs inflate to not maintain a sliding window if the
+  stream completes, which reduces inflate's memory footprint.  If the stream
+  does not complete, either because not all of the stream is provided or not
+  enough output space is provided, then a sliding window will be allocated and
+  inflate() can be called again to continue the operation as if Z_NO_FLUSH had
+  been used.
+
+     In this implementation, inflate() always flushes as much output as
+  possible to the output buffer, and always uses the faster approach on the
+  first call.  So the effects of the flush parameter in this implementation are
+  on the return value of inflate() as noted below, when inflate() returns early
+  when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of
+  memory for a sliding window when Z_FINISH is used.
+
+     If a preset dictionary is needed after this call (see inflateSetDictionary
+  below), inflate sets strm->adler to the Adler-32 checksum of the dictionary
+  chosen by the compressor and returns Z_NEED_DICT; otherwise it sets
+  strm->adler to the Adler-32 checksum of all output produced so far (that is,
+  total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described
+  below.  At the end of the stream, inflate() checks that its computed Adler-32
+  checksum is equal to that saved by the compressor and returns Z_STREAM_END
+  only if the checksum is correct.
+
+    inflate() can decompress and check either zlib-wrapped or gzip-wrapped
+  deflate data.  The header type is detected automatically, if requested when
+  initializing with inflateInit2().  Any information contained in the gzip
+  header is not retained unless inflateGetHeader() is used.  When processing
+  gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output
+  produced so far.  The CRC-32 is checked against the gzip trailer, as is the
+  uncompressed length, modulo 2^32.
+
+    inflate() returns Z_OK if some progress has been made (more input processed
+  or more output produced), Z_STREAM_END if the end of the compressed data has
+  been reached and all uncompressed output has been produced, Z_NEED_DICT if a
+  preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
+  corrupted (input stream not conforming to the zlib format or incorrect check
+  value, in which case strm->msg points to a string with a more specific
+  error), Z_STREAM_ERROR if the stream structure was inconsistent (for example
+  next_in or next_out was Z_NULL, or the state was inadvertently written over
+  by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR
+  if no progress was possible or if there was not enough room in the output
+  buffer when Z_FINISH is used.  Note that Z_BUF_ERROR is not fatal, and
+  inflate() can be called again with more input and more output space to
+  continue decompressing.  If Z_DATA_ERROR is returned, the application may
+  then call inflateSync() to look for a good compression block if a partial
+  recovery of the data is to be attempted.
+*/
+
+
+ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
+/*
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any pending
+   output.
+
+     inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state
+   was inconsistent.
+*/
+
+
+                        /* Advanced functions */
+
+/*
+    The following functions are needed only in some special applications.
+*/
+
+/*
+ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
+                                     int  level,
+                                     int  method,
+                                     int  windowBits,
+                                     int  memLevel,
+                                     int  strategy));
+
+     This is another version of deflateInit with more compression options.  The
+   fields next_in, zalloc, zfree and opaque must be initialized before by the
+   caller.
+
+     The method parameter is the compression method.  It must be Z_DEFLATED in
+   this version of the library.
+
+     The windowBits parameter is the base two logarithm of the window size
+   (the size of the history buffer).  It should be in the range 8..15 for this
+   version of the library.  Larger values of this parameter result in better
+   compression at the expense of memory usage.  The default value is 15 if
+   deflateInit is used instead.
+
+     For the current implementation of deflate(), a windowBits value of 8 (a
+   window size of 256 bytes) is not supported.  As a result, a request for 8
+   will result in 9 (a 512-byte window).  In that case, providing 8 to
+   inflateInit2() will result in an error when the zlib header with 9 is
+   checked against the initialization of inflate().  The remedy is to not use 8
+   with deflateInit2() with this initialization, or at least in that case use 9
+   with inflateInit2().
+
+     windowBits can also be -8..-15 for raw deflate.  In this case, -windowBits
+   determines the window size.  deflate() will then generate raw deflate data
+   with no zlib header or trailer, and will not compute a check value.
+
+     windowBits can also be greater than 15 for optional gzip encoding.  Add
+   16 to windowBits to write a simple gzip header and trailer around the
+   compressed data instead of a zlib wrapper.  The gzip header will have no
+   file name, no extra data, no comment, no modification time (set to zero), no
+   header crc, and the operating system will be set to the appropriate value,
+   if the operating system was determined at compile time.  If a gzip stream is
+   being written, strm->adler is a CRC-32 instead of an Adler-32.
+
+     For raw deflate or gzip encoding, a request for a 256-byte window is
+   rejected as invalid, since only the zlib header provides a means of
+   transmitting the window size to the decompressor.
+
+     The memLevel parameter specifies how much memory should be allocated
+   for the internal compression state.  memLevel=1 uses minimum memory but is
+   slow and reduces compression ratio; memLevel=9 uses maximum memory for
+   optimal speed.  The default value is 8.  See zconf.h for total memory usage
+   as a function of windowBits and memLevel.
+
+     The strategy parameter is used to tune the compression algorithm.  Use the
+   value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+   filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no
+   string match), or Z_RLE to limit match distances to one (run-length
+   encoding).  Filtered data consists mostly of small values with a somewhat
+   random distribution.  In this case, the compression algorithm is tuned to
+   compress them better.  The effect of Z_FILTERED is to force more Huffman
+   coding and less string matching; it is somewhat intermediate between
+   Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY.  Z_RLE is designed to be almost as
+   fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data.  The
+   strategy parameter only affects the compression ratio but not the
+   correctness of the compressed output even if it is not set appropriately.
+   Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler
+   decoder for special applications.
+
+     deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid
+   method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is
+   incompatible with the version assumed by the caller (ZLIB_VERSION).  msg is
+   set to null if there is no error message.  deflateInit2 does not perform any
+   compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
+                                             const Bytef *dictionary,
+                                             uInt  dictLength));
+/*
+     Initializes the compression dictionary from the given byte sequence
+   without producing any compressed output.  When using the zlib format, this
+   function must be called immediately after deflateInit, deflateInit2 or
+   deflateReset, and before any call of deflate.  When doing raw deflate, this
+   function must be called either before any call of deflate, or immediately
+   after the completion of a deflate block, i.e. after all input has been
+   consumed and all output has been delivered when using any of the flush
+   options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH.  The
+   compressor and decompressor must use exactly the same dictionary (see
+   inflateSetDictionary).
+
+     The dictionary should consist of strings (byte sequences) that are likely
+   to be encountered later in the data to be compressed, with the most commonly
+   used strings preferably put towards the end of the dictionary.  Using a
+   dictionary is most useful when the data to be compressed is short and can be
+   predicted with good accuracy; the data can then be compressed better than
+   with the default empty dictionary.
+
+     Depending on the size of the compression data structures selected by
+   deflateInit or deflateInit2, a part of the dictionary may in effect be
+   discarded, for example if the dictionary is larger than the window size
+   provided in deflateInit or deflateInit2.  Thus the strings most likely to be
+   useful should be put at the end of the dictionary, not at the front.  In
+   addition, the current implementation of deflate will use at most the window
+   size minus 262 bytes of the provided dictionary.
+
+     Upon return of this function, strm->adler is set to the Adler-32 value
+   of the dictionary; the decompressor may later use this value to determine
+   which dictionary has been used by the compressor.  (The Adler-32 value
+   applies to the whole dictionary even if only a subset of the dictionary is
+   actually used by the compressor.) If a raw deflate was requested, then the
+   Adler-32 value is not computed and strm->adler is not set.
+
+     deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+   parameter is invalid (e.g.  dictionary being Z_NULL) or the stream state is
+   inconsistent (for example if deflate has already been called for this stream
+   or if not at a block boundary for raw deflate).  deflateSetDictionary does
+   not perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm,
+                                             Bytef *dictionary,
+                                             uInt  *dictLength));
+/*
+     Returns the sliding dictionary being maintained by deflate.  dictLength is
+   set to the number of bytes in the dictionary, and that many bytes are copied
+   to dictionary.  dictionary must have enough space, where 32768 bytes is
+   always enough.  If deflateGetDictionary() is called with dictionary equal to
+   Z_NULL, then only the dictionary length is returned, and nothing is copied.
+   Similary, if dictLength is Z_NULL, then it is not set.
+
+     deflateGetDictionary() may return a length less than the window size, even
+   when more than the window size in input has been provided. It may return up
+   to 258 bytes less in that case, due to how zlib's implementation of deflate
+   manages the sliding window and lookahead for matches, where matches can be
+   up to 258 bytes long. If the application needs the last window-size bytes of
+   input, then that would need to be saved by the application outside of zlib.
+
+     deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the
+   stream state is inconsistent.
+*/
+
+ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
+                                    z_streamp source));
+/*
+     Sets the destination stream as a complete copy of the source stream.
+
+     This function can be useful when several compression strategies will be
+   tried, for example when there are several ways of pre-processing the input
+   data with a filter.  The streams that will be discarded should then be freed
+   by calling deflateEnd.  Note that deflateCopy duplicates the internal
+   compression state which can be quite large, so this strategy is slow and can
+   consume lots of memory.
+
+     deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+   (such as zalloc being Z_NULL).  msg is left unchanged in both source and
+   destination.
+*/
+
+ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
+/*
+     This function is equivalent to deflateEnd followed by deflateInit, but
+   does not free and reallocate the internal compression state.  The stream
+   will leave the compression level and any other attributes that may have been
+   set unchanged.
+
+     deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being Z_NULL).
+*/
+
+ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
+                                      int level,
+                                      int strategy));
+/*
+     Dynamically update the compression level and compression strategy.  The
+   interpretation of level and strategy is as in deflateInit2().  This can be
+   used to switch between compression and straight copy of the input data, or
+   to switch to a different kind of input data requiring a different strategy.
+   If the compression approach (which is a function of the level) or the
+   strategy is changed, and if any input has been consumed in a previous
+   deflate() call, then the input available so far is compressed with the old
+   level and strategy using deflate(strm, Z_BLOCK).  There are three approaches
+   for the compression levels 0, 1..3, and 4..9 respectively.  The new level
+   and strategy will take effect at the next call of deflate().
+
+     If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does
+   not have enough output space to complete, then the parameter change will not
+   take effect.  In this case, deflateParams() can be called again with the
+   same parameters and more output space to try again.
+
+     In order to assure a change in the parameters on the first try, the
+   deflate stream should be flushed using deflate() with Z_BLOCK or other flush
+   request until strm.avail_out is not zero, before calling deflateParams().
+   Then no more input data should be provided before the deflateParams() call.
+   If this is done, the old level and strategy will be applied to the data
+   compressed before deflateParams(), and the new level and strategy will be
+   applied to the the data compressed after deflateParams().
+
+     deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream
+   state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if
+   there was not enough output space to complete the compression of the
+   available input data before a change in the strategy or approach.  Note that
+   in the case of a Z_BUF_ERROR, the parameters are not changed.  A return
+   value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be
+   retried with more output space.
+*/
+
+ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm,
+                                    int good_length,
+                                    int max_lazy,
+                                    int nice_length,
+                                    int max_chain));
+/*
+     Fine tune deflate's internal compression parameters.  This should only be
+   used by someone who understands the algorithm used by zlib's deflate for
+   searching for the best matching string, and even then only by the most
+   fanatic optimizer trying to squeeze out the last compressed bit for their
+   specific input data.  Read the deflate.c source code for the meaning of the
+   max_lazy, good_length, nice_length, and max_chain parameters.
+
+     deflateTune() can be called after deflateInit() or deflateInit2(), and
+   returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream.
+ */
+
+ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
+                                       uLong sourceLen));
+/*
+     deflateBound() returns an upper bound on the compressed size after
+   deflation of sourceLen bytes.  It must be called after deflateInit() or
+   deflateInit2(), and after deflateSetHeader(), if used.  This would be used
+   to allocate an output buffer for deflation in a single pass, and so would be
+   called before deflate().  If that first deflate() call is provided the
+   sourceLen input bytes, an output buffer allocated to the size returned by
+   deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed
+   to return Z_STREAM_END.  Note that it is possible for the compressed size to
+   be larger than the value returned by deflateBound() if flush options other
+   than Z_FINISH or Z_NO_FLUSH are used.
+*/
+
+ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm,
+                                       unsigned *pending,
+                                       int *bits));
+/*
+     deflatePending() returns the number of bytes and bits of output that have
+   been generated, but not yet provided in the available output.  The bytes not
+   provided would be due to the available output space having being consumed.
+   The number of bits of output not provided are between 0 and 7, where they
+   await more bits to join them in order to fill out a full byte.  If pending
+   or bits are Z_NULL, then those values are not set.
+
+     deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.
+ */
+
+ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
+                                     int bits,
+                                     int value));
+/*
+     deflatePrime() inserts bits in the deflate output stream.  The intent
+   is that this function is used to start off the deflate output with the bits
+   leftover from a previous deflate stream when appending to it.  As such, this
+   function can only be used for raw deflate, and must be used before the first
+   deflate() call after a deflateInit2() or deflateReset().  bits must be less
+   than or equal to 16, and that many of the least significant bits of value
+   will be inserted in the output.
+
+     deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough
+   room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the
+   source stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm,
+                                         gz_headerp head));
+/*
+     deflateSetHeader() provides gzip header information for when a gzip
+   stream is requested by deflateInit2().  deflateSetHeader() may be called
+   after deflateInit2() or deflateReset() and before the first call of
+   deflate().  The text, time, os, extra field, name, and comment information
+   in the provided gz_header structure are written to the gzip header (xflag is
+   ignored -- the extra flags are set according to the compression level).  The
+   caller must assure that, if not Z_NULL, name and comment are terminated with
+   a zero byte, and that if extra is not Z_NULL, that extra_len bytes are
+   available there.  If hcrc is true, a gzip header crc is included.  Note that
+   the current versions of the command-line version of gzip (up through version
+   1.3.x) do not support header crc's, and will report that it is a "multi-part
+   gzip file" and give up.
+
+     If deflateSetHeader is not used, the default gzip header has text false,
+   the time set to zero, and os set to 255, with no extra, name, or comment
+   fields.  The gzip header is returned to the default state by deflateReset().
+
+     deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
+                                     int  windowBits));
+
+     This is another version of inflateInit with an extra parameter.  The
+   fields next_in, avail_in, zalloc, zfree and opaque must be initialized
+   before by the caller.
+
+     The windowBits parameter is the base two logarithm of the maximum window
+   size (the size of the history buffer).  It should be in the range 8..15 for
+   this version of the library.  The default value is 15 if inflateInit is used
+   instead.  windowBits must be greater than or equal to the windowBits value
+   provided to deflateInit2() while compressing, or it must be equal to 15 if
+   deflateInit2() was not used.  If a compressed stream with a larger window
+   size is given as input, inflate() will return with the error code
+   Z_DATA_ERROR instead of trying to allocate a larger window.
+
+     windowBits can also be zero to request that inflate use the window size in
+   the zlib header of the compressed stream.
+
+     windowBits can also be -8..-15 for raw inflate.  In this case, -windowBits
+   determines the window size.  inflate() will then process raw deflate data,
+   not looking for a zlib or gzip header, not generating a check value, and not
+   looking for any check values for comparison at the end of the stream.  This
+   is for use with other formats that use the deflate compressed data format
+   such as zip.  Those formats provide their own check values.  If a custom
+   format is developed using the raw deflate format for compressed data, it is
+   recommended that a check value such as an Adler-32 or a CRC-32 be applied to
+   the uncompressed data as is done in the zlib, gzip, and zip formats.  For
+   most applications, the zlib format should be used as is.  Note that comments
+   above on the use in deflateInit2() applies to the magnitude of windowBits.
+
+     windowBits can also be greater than 15 for optional gzip decoding.  Add
+   32 to windowBits to enable zlib and gzip decoding with automatic header
+   detection, or add 16 to decode only the gzip format (the zlib format will
+   return a Z_DATA_ERROR).  If a gzip stream is being decoded, strm->adler is a
+   CRC-32 instead of an Adler-32.  Unlike the gunzip utility and gzread() (see
+   below), inflate() will not automatically decode concatenated gzip streams.
+   inflate() will return Z_STREAM_END at the end of the gzip stream.  The state
+   would need to be reset to continue decoding a subsequent gzip stream.
+
+     inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+   version assumed by the caller, or Z_STREAM_ERROR if the parameters are
+   invalid, such as a null pointer to the structure.  msg is set to null if
+   there is no error message.  inflateInit2 does not perform any decompression
+   apart from possibly reading the zlib header if present: actual decompression
+   will be done by inflate().  (So next_in and avail_in may be modified, but
+   next_out and avail_out are unused and unchanged.) The current implementation
+   of inflateInit2() does not process any header information -- that is
+   deferred until inflate() is called.
+*/
+
+ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
+                                             const Bytef *dictionary,
+                                             uInt  dictLength));
+/*
+     Initializes the decompression dictionary from the given uncompressed byte
+   sequence.  This function must be called immediately after a call of inflate,
+   if that call returned Z_NEED_DICT.  The dictionary chosen by the compressor
+   can be determined from the Adler-32 value returned by that call of inflate.
+   The compressor and decompressor must use exactly the same dictionary (see
+   deflateSetDictionary).  For raw inflate, this function can be called at any
+   time to set the dictionary.  If the provided dictionary is smaller than the
+   window and there is already data in the window, then the provided dictionary
+   will amend what's there.  The application must insure that the dictionary
+   that was used for compression is provided.
+
+     inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+   parameter is invalid (e.g.  dictionary being Z_NULL) or the stream state is
+   inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+   expected one (incorrect Adler-32 value).  inflateSetDictionary does not
+   perform any decompression: this will be done by subsequent calls of
+   inflate().
+*/
+
+ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm,
+                                             Bytef *dictionary,
+                                             uInt  *dictLength));
+/*
+     Returns the sliding dictionary being maintained by inflate.  dictLength is
+   set to the number of bytes in the dictionary, and that many bytes are copied
+   to dictionary.  dictionary must have enough space, where 32768 bytes is
+   always enough.  If inflateGetDictionary() is called with dictionary equal to
+   Z_NULL, then only the dictionary length is returned, and nothing is copied.
+   Similary, if dictLength is Z_NULL, then it is not set.
+
+     inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the
+   stream state is inconsistent.
+*/
+
+ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
+/*
+     Skips invalid compressed data until a possible full flush point (see above
+   for the description of deflate with Z_FULL_FLUSH) can be found, or until all
+   available input is skipped.  No output is provided.
+
+     inflateSync searches for a 00 00 FF FF pattern in the compressed data.
+   All full flush points have this pattern, but not all occurrences of this
+   pattern are full flush points.
+
+     inflateSync returns Z_OK if a possible full flush point has been found,
+   Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point
+   has been found, or Z_STREAM_ERROR if the stream structure was inconsistent.
+   In the success case, the application may save the current current value of
+   total_in which indicates where valid compressed data was found.  In the
+   error case, the application may repeatedly call inflateSync, providing more
+   input each time, until success or end of the input data.
+*/
+
+ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
+                                    z_streamp source));
+/*
+     Sets the destination stream as a complete copy of the source stream.
+
+     This function can be useful when randomly accessing a large stream.  The
+   first pass through the stream can periodically record the inflate state,
+   allowing restarting inflate at those points when randomly accessing the
+   stream.
+
+     inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+   (such as zalloc being Z_NULL).  msg is left unchanged in both source and
+   destination.
+*/
+
+ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
+/*
+     This function is equivalent to inflateEnd followed by inflateInit,
+   but does not free and reallocate the internal decompression state.  The
+   stream will keep attributes that may have been set by inflateInit2.
+
+     inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being Z_NULL).
+*/
+
+ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm,
+                                      int windowBits));
+/*
+     This function is the same as inflateReset, but it also permits changing
+   the wrap and window size requests.  The windowBits parameter is interpreted
+   the same as it is for inflateInit2.  If the window size is changed, then the
+   memory allocated for the window is freed, and the window will be reallocated
+   by inflate() if needed.
+
+     inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being Z_NULL), or if
+   the windowBits parameter is invalid.
+*/
+
+ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm,
+                                     int bits,
+                                     int value));
+/*
+     This function inserts bits in the inflate input stream.  The intent is
+   that this function is used to start inflating at a bit position in the
+   middle of a byte.  The provided bits will be used before any bytes are used
+   from next_in.  This function should only be used with raw inflate, and
+   should be used before the first inflate() call after inflateInit2() or
+   inflateReset().  bits must be less than or equal to 16, and that many of the
+   least significant bits of value will be inserted in the input.
+
+     If bits is negative, then the input stream bit buffer is emptied.  Then
+   inflatePrime() can be called again to put bits in the buffer.  This is used
+   to clear out bits leftover after feeding inflate a block description prior
+   to feeding inflate codes.
+
+     inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.
+*/
+
+ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm));
+/*
+     This function returns two values, one in the lower 16 bits of the return
+   value, and the other in the remaining upper bits, obtained by shifting the
+   return value down 16 bits.  If the upper value is -1 and the lower value is
+   zero, then inflate() is currently decoding information outside of a block.
+   If the upper value is -1 and the lower value is non-zero, then inflate is in
+   the middle of a stored block, with the lower value equaling the number of
+   bytes from the input remaining to copy.  If the upper value is not -1, then
+   it is the number of bits back from the current bit position in the input of
+   the code (literal or length/distance pair) currently being processed.  In
+   that case the lower value is the number of bytes already emitted for that
+   code.
+
+     A code is being processed if inflate is waiting for more input to complete
+   decoding of the code, or if it has completed decoding but is waiting for
+   more output space to write the literal or match data.
+
+     inflateMark() is used to mark locations in the input data for random
+   access, which may be at bit positions, and to note those cases where the
+   output of a code may span boundaries of random access blocks.  The current
+   location in the input stream can be determined from avail_in and data_type
+   as noted in the description for the Z_BLOCK flush parameter for inflate.
+
+     inflateMark returns the value noted above, or -65536 if the provided
+   source stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm,
+                                         gz_headerp head));
+/*
+     inflateGetHeader() requests that gzip header information be stored in the
+   provided gz_header structure.  inflateGetHeader() may be called after
+   inflateInit2() or inflateReset(), and before the first call of inflate().
+   As inflate() processes the gzip stream, head->done is zero until the header
+   is completed, at which time head->done is set to one.  If a zlib stream is
+   being decoded, then head->done is set to -1 to indicate that there will be
+   no gzip header information forthcoming.  Note that Z_BLOCK or Z_TREES can be
+   used to force inflate() to return immediately after header processing is
+   complete and before any actual data is decompressed.
+
+     The text, time, xflags, and os fields are filled in with the gzip header
+   contents.  hcrc is set to true if there is a header CRC.  (The header CRC
+   was valid if done is set to one.) If extra is not Z_NULL, then extra_max
+   contains the maximum number of bytes to write to extra.  Once done is true,
+   extra_len contains the actual extra field length, and extra contains the
+   extra field, or that field truncated if extra_max is less than extra_len.
+   If name is not Z_NULL, then up to name_max characters are written there,
+   terminated with a zero unless the length is greater than name_max.  If
+   comment is not Z_NULL, then up to comm_max characters are written there,
+   terminated with a zero unless the length is greater than comm_max.  When any
+   of extra, name, or comment are not Z_NULL and the respective field is not
+   present in the header, then that field is set to Z_NULL to signal its
+   absence.  This allows the use of deflateSetHeader() with the returned
+   structure to duplicate the header.  However if those fields are set to
+   allocated memory, then the application will need to save those pointers
+   elsewhere so that they can be eventually freed.
+
+     If inflateGetHeader is not used, then the header information is simply
+   discarded.  The header is always checked for validity, including the header
+   CRC if present.  inflateReset() will reset the process to discard the header
+   information.  The application would need to call inflateGetHeader() again to
+   retrieve the header from the next gzip stream.
+
+     inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits,
+                                        unsigned char FAR *window));
+
+     Initialize the internal stream state for decompression using inflateBack()
+   calls.  The fields zalloc, zfree and opaque in strm must be initialized
+   before the call.  If zalloc and zfree are Z_NULL, then the default library-
+   derived memory allocation routines are used.  windowBits is the base two
+   logarithm of the window size, in the range 8..15.  window is a caller
+   supplied buffer of that size.  Except for special applications where it is
+   assured that deflate was used with small window sizes, windowBits must be 15
+   and a 32K byte window must be supplied to be able to decompress general
+   deflate streams.
+
+     See inflateBack() for the usage of these routines.
+
+     inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of
+   the parameters are invalid, Z_MEM_ERROR if the internal state could not be
+   allocated, or Z_VERSION_ERROR if the version of the library does not match
+   the version of the header file.
+*/
+
+typedef unsigned (*in_func) OF((void FAR *,
+                                z_const unsigned char FAR * FAR *));
+typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
+
+ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,
+                                    in_func in, void FAR *in_desc,
+                                    out_func out, void FAR *out_desc));
+/*
+     inflateBack() does a raw inflate with a single call using a call-back
+   interface for input and output.  This is potentially more efficient than
+   inflate() for file i/o applications, in that it avoids copying between the
+   output and the sliding window by simply making the window itself the output
+   buffer.  inflate() can be faster on modern CPUs when used with large
+   buffers.  inflateBack() trusts the application to not change the output
+   buffer passed by the output function, at least until inflateBack() returns.
+
+     inflateBackInit() must be called first to allocate the internal state
+   and to initialize the state with the user-provided window buffer.
+   inflateBack() may then be used multiple times to inflate a complete, raw
+   deflate stream with each call.  inflateBackEnd() is then called to free the
+   allocated state.
+
+     A raw deflate stream is one with no zlib or gzip header or trailer.
+   This routine would normally be used in a utility that reads zip or gzip
+   files and writes out uncompressed files.  The utility would decode the
+   header and process the trailer on its own, hence this routine expects only
+   the raw deflate stream to decompress.  This is different from the default
+   behavior of inflate(), which expects a zlib header and trailer around the
+   deflate stream.
+
+     inflateBack() uses two subroutines supplied by the caller that are then
+   called by inflateBack() for input and output.  inflateBack() calls those
+   routines until it reads a complete deflate stream and writes out all of the
+   uncompressed data, or until it encounters an error.  The function's
+   parameters and return types are defined above in the in_func and out_func
+   typedefs.  inflateBack() will call in(in_desc, &buf) which should return the
+   number of bytes of provided input, and a pointer to that input in buf.  If
+   there is no input available, in() must return zero -- buf is ignored in that
+   case -- and inflateBack() will return a buffer error.  inflateBack() will
+   call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1].
+   out() should return zero on success, or non-zero on failure.  If out()
+   returns non-zero, inflateBack() will return with an error.  Neither in() nor
+   out() are permitted to change the contents of the window provided to
+   inflateBackInit(), which is also the buffer that out() uses to write from.
+   The length written by out() will be at most the window size.  Any non-zero
+   amount of input may be provided by in().
+
+     For convenience, inflateBack() can be provided input on the first call by
+   setting strm->next_in and strm->avail_in.  If that input is exhausted, then
+   in() will be called.  Therefore strm->next_in must be initialized before
+   calling inflateBack().  If strm->next_in is Z_NULL, then in() will be called
+   immediately for input.  If strm->next_in is not Z_NULL, then strm->avail_in
+   must also be initialized, and then if strm->avail_in is not zero, input will
+   initially be taken from strm->next_in[0 ..  strm->avail_in - 1].
+
+     The in_desc and out_desc parameters of inflateBack() is passed as the
+   first parameter of in() and out() respectively when they are called.  These
+   descriptors can be optionally used to pass any information that the caller-
+   supplied in() and out() functions need to do their job.
+
+     On return, inflateBack() will set strm->next_in and strm->avail_in to
+   pass back any unused input that was provided by the last in() call.  The
+   return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR
+   if in() or out() returned an error, Z_DATA_ERROR if there was a format error
+   in the deflate stream (in which case strm->msg is set to indicate the nature
+   of the error), or Z_STREAM_ERROR if the stream was not properly initialized.
+   In the case of Z_BUF_ERROR, an input or output error can be distinguished
+   using strm->next_in which will be Z_NULL only if in() returned an error.  If
+   strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning
+   non-zero.  (in() will always be called before out(), so strm->next_in is
+   assured to be defined if out() returns non-zero.)  Note that inflateBack()
+   cannot return Z_OK.
+*/
+
+ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm));
+/*
+     All memory allocated by inflateBackInit() is freed.
+
+     inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream
+   state was inconsistent.
+*/
+
+ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
+/* Return flags indicating compile-time options.
+
+    Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
+     1.0: size of uInt
+     3.2: size of uLong
+     5.4: size of voidpf (pointer)
+     7.6: size of z_off_t
+
+    Compiler, assembler, and debug options:
+     8: ZLIB_DEBUG
+     9: ASMV or ASMINF -- use ASM code
+     10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention
+     11: 0 (reserved)
+
+    One-time table building (smaller code, but not thread-safe if true):
+     12: BUILDFIXED -- build static block decoding tables when needed
+     13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed
+     14,15: 0 (reserved)
+
+    Library content (indicates missing functionality):
+     16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
+                          deflate code when not needed)
+     17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect
+                    and decode gzip streams (to avoid linking crc code)
+     18-19: 0 (reserved)
+
+    Operation variations (changes in library functionality):
+     20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate
+     21: FASTEST -- deflate algorithm with only one, lowest compression level
+     22,23: 0 (reserved)
+
+    The sprintf variant used by gzprintf (zero is best):
+     24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format
+     25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure!
+     26: 0 = returns value, 1 = void -- 1 means inferred string length returned
+
+    Remainder:
+     27-31: 0 (reserved)
+ */
+
+#ifndef Z_SOLO
+
+                        /* utility functions */
+
+/*
+     The following utility functions are implemented on top of the basic
+   stream-oriented functions.  To simplify the interface, some default options
+   are assumed (compression level and memory usage, standard memory allocation
+   functions).  The source code of these utility functions can be modified if
+   you need special options.
+*/
+
+ZEXTERN int ZEXPORT compress OF((Bytef *dest,   uLongf *destLen,
+                                 const Bytef *source, uLong sourceLen));
+/*
+     Compresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer.  Upon entry, destLen is the total size
+   of the destination buffer, which must be at least the value returned by
+   compressBound(sourceLen).  Upon exit, destLen is the actual size of the
+   compressed data.  compress() is equivalent to compress2() with a level
+   parameter of Z_DEFAULT_COMPRESSION.
+
+     compress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer.
+*/
+
+ZEXTERN int ZEXPORT compress2 OF((Bytef *dest,   uLongf *destLen,
+                                  const Bytef *source, uLong sourceLen,
+                                  int level));
+/*
+     Compresses the source buffer into the destination buffer.  The level
+   parameter has the same meaning as in deflateInit.  sourceLen is the byte
+   length of the source buffer.  Upon entry, destLen is the total size of the
+   destination buffer, which must be at least the value returned by
+   compressBound(sourceLen).  Upon exit, destLen is the actual size of the
+   compressed data.
+
+     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+   Z_STREAM_ERROR if the level parameter is invalid.
+*/
+
+ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));
+/*
+     compressBound() returns an upper bound on the compressed size after
+   compress() or compress2() on sourceLen bytes.  It would be used before a
+   compress() or compress2() call to allocate the destination buffer.
+*/
+
+ZEXTERN int ZEXPORT uncompress OF((Bytef *dest,   uLongf *destLen,
+                                   const Bytef *source, uLong sourceLen));
+/*
+     Decompresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer.  Upon entry, destLen is the total size
+   of the destination buffer, which must be large enough to hold the entire
+   uncompressed data.  (The size of the uncompressed data must have been saved
+   previously by the compressor and transmitted to the decompressor by some
+   mechanism outside the scope of this compression library.) Upon exit, destLen
+   is the actual size of the uncompressed data.
+
+     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.  In
+   the case where there is not enough room, uncompress() will fill the output
+   buffer with the uncompressed data up to that point.
+*/
+
+ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest,   uLongf *destLen,
+                                    const Bytef *source, uLong *sourceLen));
+/*
+     Same as uncompress, except that sourceLen is a pointer, where the
+   length of the source is *sourceLen.  On return, *sourceLen is the number of
+   source bytes consumed.
+*/
+
+                        /* gzip file access functions */
+
+/*
+     This library supports reading and writing files in gzip (.gz) format with
+   an interface similar to that of stdio, using the functions that start with
+   "gz".  The gzip format is different from the zlib format.  gzip is a gzip
+   wrapper, documented in RFC 1952, wrapped around a deflate stream.
+*/
+
+typedef struct gzFile_s *gzFile;    /* semi-opaque gzip file descriptor */
+
+/*
+ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
+
+     Opens a gzip (.gz) file for reading or writing.  The mode parameter is as
+   in fopen ("rb" or "wb") but can also include a compression level ("wb9") or
+   a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only
+   compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F'
+   for fixed code compression as in "wb9F".  (See the description of
+   deflateInit2 for more information about the strategy parameter.)  'T' will
+   request transparent writing or appending with no compression and not using
+   the gzip format.
+
+     "a" can be used instead of "w" to request that the gzip stream that will
+   be written be appended to the file.  "+" will result in an error, since
+   reading and writing to the same gzip file is not supported.  The addition of
+   "x" when writing will create the file exclusively, which fails if the file
+   already exists.  On systems that support it, the addition of "e" when
+   reading or writing will set the flag to close the file on an execve() call.
+
+     These functions, as well as gzip, will read and decode a sequence of gzip
+   streams in a file.  The append function of gzopen() can be used to create
+   such a file.  (Also see gzflush() for another way to do this.)  When
+   appending, gzopen does not test whether the file begins with a gzip stream,
+   nor does it look for the end of the gzip streams to begin appending.  gzopen
+   will simply append a gzip stream to the existing file.
+
+     gzopen can be used to read a file which is not in gzip format; in this
+   case gzread will directly read from the file without decompression.  When
+   reading, this will be detected automatically by looking for the magic two-
+   byte gzip header.
+
+     gzopen returns NULL if the file could not be opened, if there was
+   insufficient memory to allocate the gzFile state, or if an invalid mode was
+   specified (an 'r', 'w', or 'a' was not provided, or '+' was provided).
+   errno can be checked to determine if the reason gzopen failed was that the
+   file could not be opened.
+*/
+
+ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
+/*
+     gzdopen associates a gzFile with the file descriptor fd.  File descriptors
+   are obtained from calls like open, dup, creat, pipe or fileno (if the file
+   has been previously opened with fopen).  The mode parameter is as in gzopen.
+
+     The next call of gzclose on the returned gzFile will also close the file
+   descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor
+   fd.  If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd,
+   mode);.  The duplicated descriptor should be saved to avoid a leak, since
+   gzdopen does not close fd if it fails.  If you are using fileno() to get the
+   file descriptor from a FILE *, then you will have to use dup() to avoid
+   double-close()ing the file descriptor.  Both gzclose() and fclose() will
+   close the associated file descriptor, so they need to have different file
+   descriptors.
+
+     gzdopen returns NULL if there was insufficient memory to allocate the
+   gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not
+   provided, or '+' was provided), or if fd is -1.  The file descriptor is not
+   used until the next gz* read, write, seek, or close operation, so gzdopen
+   will not detect if fd is invalid (unless fd is -1).
+*/
+
+ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size));
+/*
+     Set the internal buffer size used by this library's functions.  The
+   default buffer size is 8192 bytes.  This function must be called after
+   gzopen() or gzdopen(), and before any other calls that read or write the
+   file.  The buffer memory allocation is always deferred to the first read or
+   write.  Three times that size in buffer space is allocated.  A larger buffer
+   size of, for example, 64K or 128K bytes will noticeably increase the speed
+   of decompression (reading).
+
+     The new buffer size also affects the maximum length for gzprintf().
+
+     gzbuffer() returns 0 on success, or -1 on failure, such as being called
+   too late.
+*/
+
+ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
+/*
+     Dynamically update the compression level or strategy.  See the description
+   of deflateInit2 for the meaning of these parameters.  Previously provided
+   data is flushed before the parameter change.
+
+     gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not
+   opened for writing, Z_ERRNO if there is an error writing the flushed data,
+   or Z_MEM_ERROR if there is a memory allocation error.
+*/
+
+ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
+/*
+     Reads the given number of uncompressed bytes from the compressed file.  If
+   the input file is not in gzip format, gzread copies the given number of
+   bytes into the buffer directly from the file.
+
+     After reaching the end of a gzip stream in the input, gzread will continue
+   to read, looking for another gzip stream.  Any number of gzip streams may be
+   concatenated in the input file, and will all be decompressed by gzread().
+   If something other than a gzip stream is encountered after a gzip stream,
+   that remaining trailing garbage is ignored (and no error is returned).
+
+     gzread can be used to read a gzip file that is being concurrently written.
+   Upon reaching the end of the input, gzread will return with the available
+   data.  If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then
+   gzclearerr can be used to clear the end of file indicator in order to permit
+   gzread to be tried again.  Z_OK indicates that a gzip stream was completed
+   on the last gzread.  Z_BUF_ERROR indicates that the input file ended in the
+   middle of a gzip stream.  Note that gzread does not return -1 in the event
+   of an incomplete gzip stream.  This error is deferred until gzclose(), which
+   will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip
+   stream.  Alternatively, gzerror can be used before gzclose to detect this
+   case.
+
+     gzread returns the number of uncompressed bytes actually read, less than
+   len for end of file, or -1 for error.  If len is too large to fit in an int,
+   then nothing is read, -1 is returned, and the error state is set to
+   Z_STREAM_ERROR.
+*/
+
+ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems,
+                                     gzFile file));
+/*
+     Read up to nitems items of size size from file to buf, otherwise operating
+   as gzread() does.  This duplicates the interface of stdio's fread(), with
+   size_t request and return types.  If the library defines size_t, then
+   z_size_t is identical to size_t.  If not, then z_size_t is an unsigned
+   integer type that can contain a pointer.
+
+     gzfread() returns the number of full items read of size size, or zero if
+   the end of the file was reached and a full item could not be read, or if
+   there was an error.  gzerror() must be consulted if zero is returned in
+   order to determine if there was an error.  If the multiplication of size and
+   nitems overflows, i.e. the product does not fit in a z_size_t, then nothing
+   is read, zero is returned, and the error state is set to Z_STREAM_ERROR.
+
+     In the event that the end of file is reached and only a partial item is
+   available at the end, i.e. the remaining uncompressed data length is not a
+   multiple of size, then the final partial item is nevetheless read into buf
+   and the end-of-file flag is set.  The length of the partial item read is not
+   provided, but could be inferred from the result of gztell().  This behavior
+   is the same as the behavior of fread() implementations in common libraries,
+   but it prevents the direct use of gzfread() to read a concurrently written
+   file, reseting and retrying on end-of-file, when size is not 1.
+*/
+
+ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
+                                voidpc buf, unsigned len));
+/*
+     Writes the given number of uncompressed bytes into the compressed file.
+   gzwrite returns the number of uncompressed bytes written or 0 in case of
+   error.
+*/
+
+ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size,
+                                      z_size_t nitems, gzFile file));
+/*
+     gzfwrite() writes nitems items of size size from buf to file, duplicating
+   the interface of stdio's fwrite(), with size_t request and return types.  If
+   the library defines size_t, then z_size_t is identical to size_t.  If not,
+   then z_size_t is an unsigned integer type that can contain a pointer.
+
+     gzfwrite() returns the number of full items written of size size, or zero
+   if there was an error.  If the multiplication of size and nitems overflows,
+   i.e. the product does not fit in a z_size_t, then nothing is written, zero
+   is returned, and the error state is set to Z_STREAM_ERROR.
+*/
+
+ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...));
+/*
+     Converts, formats, and writes the arguments to the compressed file under
+   control of the format string, as in fprintf.  gzprintf returns the number of
+   uncompressed bytes actually written, or a negative zlib error code in case
+   of error.  The number of uncompressed bytes written is limited to 8191, or
+   one less than the buffer size given to gzbuffer().  The caller should assure
+   that this limit is not exceeded.  If it is exceeded, then gzprintf() will
+   return an error (0) with nothing written.  In this case, there may also be a
+   buffer overflow with unpredictable consequences, which is possible only if
+   zlib was compiled with the insecure functions sprintf() or vsprintf()
+   because the secure snprintf() or vsnprintf() functions were not available.
+   This can be determined using zlibCompileFlags().
+*/
+
+ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
+/*
+     Writes the given null-terminated string to the compressed file, excluding
+   the terminating null character.
+
+     gzputs returns the number of characters written, or -1 in case of error.
+*/
+
+ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
+/*
+     Reads bytes from the compressed file until len-1 characters are read, or a
+   newline character is read and transferred to buf, or an end-of-file
+   condition is encountered.  If any characters are read or if len == 1, the
+   string is terminated with a null character.  If no characters are read due
+   to an end-of-file or len < 1, then the buffer is left untouched.
+
+     gzgets returns buf which is a null-terminated string, or it returns NULL
+   for end-of-file or in case of error.  If there was an error, the contents at
+   buf are indeterminate.
+*/
+
+ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
+/*
+     Writes c, converted to an unsigned char, into the compressed file.  gzputc
+   returns the value that was written, or -1 in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
+/*
+     Reads one byte from the compressed file.  gzgetc returns this byte or -1
+   in case of end of file or error.  This is implemented as a macro for speed.
+   As such, it does not do all of the checking the other functions do.  I.e.
+   it does not check to see if file is NULL, nor whether the structure file
+   points to has been clobbered or not.
+*/
+
+ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));
+/*
+     Push one character back onto the stream to be read as the first character
+   on the next read.  At least one character of push-back is allowed.
+   gzungetc() returns the character pushed, or -1 on failure.  gzungetc() will
+   fail if c is -1, and may fail if a character has been pushed but not read
+   yet.  If gzungetc is used immediately after gzopen or gzdopen, at least the
+   output buffer size of pushed characters is allowed.  (See gzbuffer above.)
+   The pushed character will be discarded if the stream is repositioned with
+   gzseek() or gzrewind().
+*/
+
+ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
+/*
+     Flushes all pending output into the compressed file.  The parameter flush
+   is as in the deflate() function.  The return value is the zlib error number
+   (see function gzerror below).  gzflush is only permitted when writing.
+
+     If the flush parameter is Z_FINISH, the remaining data is written and the
+   gzip stream is completed in the output.  If gzwrite() is called again, a new
+   gzip stream will be started in the output.  gzread() is able to read such
+   concatenated gzip streams.
+
+     gzflush should be called only when strictly necessary because it will
+   degrade compression if called too often.
+*/
+
+/*
+ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
+                                   z_off_t offset, int whence));
+
+     Sets the starting position for the next gzread or gzwrite on the given
+   compressed file.  The offset represents a number of bytes in the
+   uncompressed data stream.  The whence parameter is defined as in lseek(2);
+   the value SEEK_END is not supported.
+
+     If the file is opened for reading, this function is emulated but can be
+   extremely slow.  If the file is opened for writing, only forward seeks are
+   supported; gzseek then compresses a sequence of zeroes up to the new
+   starting position.
+
+     gzseek returns the resulting offset location as measured in bytes from
+   the beginning of the uncompressed stream, or -1 in case of error, in
+   particular if the file is opened for writing and the new starting position
+   would be before the current position.
+*/
+
+ZEXTERN int ZEXPORT    gzrewind OF((gzFile file));
+/*
+     Rewinds the given file. This function is supported only for reading.
+
+     gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+*/
+
+/*
+ZEXTERN z_off_t ZEXPORT    gztell OF((gzFile file));
+
+     Returns the starting position for the next gzread or gzwrite on the given
+   compressed file.  This position represents a number of bytes in the
+   uncompressed data stream, and is zero when starting, even if appending or
+   reading a gzip stream from the middle of a file using gzdopen().
+
+     gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
+*/
+
+/*
+ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file));
+
+     Returns the current offset in the file being read or written.  This offset
+   includes the count of bytes that precede the gzip stream, for example when
+   appending or when using gzdopen() for reading.  When reading, the offset
+   does not include as yet unused buffered input.  This information can be used
+   for a progress indicator.  On error, gzoffset() returns -1.
+*/
+
+ZEXTERN int ZEXPORT gzeof OF((gzFile file));
+/*
+     Returns true (1) if the end-of-file indicator has been set while reading,
+   false (0) otherwise.  Note that the end-of-file indicator is set only if the
+   read tried to go past the end of the input, but came up short.  Therefore,
+   just like feof(), gzeof() may return false even if there is no more data to
+   read, in the event that the last read request was for the exact number of
+   bytes remaining in the input file.  This will happen if the input file size
+   is an exact multiple of the buffer size.
+
+     If gzeof() returns true, then the read functions will return no more data,
+   unless the end-of-file indicator is reset by gzclearerr() and the input file
+   has grown since the previous end of file was detected.
+*/
+
+ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
+/*
+     Returns true (1) if file is being copied directly while reading, or false
+   (0) if file is a gzip stream being decompressed.
+
+     If the input file is empty, gzdirect() will return true, since the input
+   does not contain a gzip stream.
+
+     If gzdirect() is used immediately after gzopen() or gzdopen() it will
+   cause buffers to be allocated to allow reading the file to determine if it
+   is a gzip file.  Therefore if gzbuffer() is used, it should be called before
+   gzdirect().
+
+     When writing, gzdirect() returns true (1) if transparent writing was
+   requested ("wT" for the gzopen() mode), or false (0) otherwise.  (Note:
+   gzdirect() is not needed when writing.  Transparent writing must be
+   explicitly requested, so the application already knows the answer.  When
+   linking statically, using gzdirect() will include all of the zlib code for
+   gzip file reading and decompression, which may not be desired.)
+*/
+
+ZEXTERN int ZEXPORT    gzclose OF((gzFile file));
+/*
+     Flushes all pending output if necessary, closes the compressed file and
+   deallocates the (de)compression state.  Note that once file is closed, you
+   cannot call gzerror with file, since its structures have been deallocated.
+   gzclose must not be called more than once on the same file, just as free
+   must not be called more than once on the same allocation.
+
+     gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a
+   file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the
+   last read ended in the middle of a gzip stream, or Z_OK on success.
+*/
+
+ZEXTERN int ZEXPORT gzclose_r OF((gzFile file));
+ZEXTERN int ZEXPORT gzclose_w OF((gzFile file));
+/*
+     Same as gzclose(), but gzclose_r() is only for use when reading, and
+   gzclose_w() is only for use when writing or appending.  The advantage to
+   using these instead of gzclose() is that they avoid linking in zlib
+   compression or decompression code that is not used when only reading or only
+   writing respectively.  If gzclose() is used, then both compression and
+   decompression code will be included the application when linking to a static
+   zlib library.
+*/
+
+ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
+/*
+     Returns the error message for the last error which occurred on the given
+   compressed file.  errnum is set to zlib error number.  If an error occurred
+   in the file system and not in the compression library, errnum is set to
+   Z_ERRNO and the application may consult errno to get the exact error code.
+
+     The application must not modify the returned string.  Future calls to
+   this function may invalidate the previously returned string.  If file is
+   closed, then the string previously returned by gzerror will no longer be
+   available.
+
+     gzerror() should be used to distinguish errors from end-of-file for those
+   functions above that do not distinguish those cases in their return values.
+*/
+
+ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
+/*
+     Clears the error and end-of-file flags for file.  This is analogous to the
+   clearerr() function in stdio.  This is useful for continuing to read a gzip
+   file that is being written concurrently.
+*/
+
+#endif /* !Z_SOLO */
+
+                        /* checksum functions */
+
+/*
+     These functions are not related to compression but are exported
+   anyway because they might be useful in applications using the compression
+   library.
+*/
+
+ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+/*
+     Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+   return the updated checksum.  If buf is Z_NULL, this function returns the
+   required initial value for the checksum.
+
+     An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed
+   much faster.
+
+   Usage example:
+
+     uLong adler = adler32(0L, Z_NULL, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       adler = adler32(adler, buffer, length);
+     }
+     if (adler != original_adler) error();
+*/
+
+ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf,
+                                    z_size_t len));
+/*
+     Same as adler32(), but with a size_t length.
+*/
+
+/*
+ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2,
+                                          z_off_t len2));
+
+     Combine two Adler-32 checksums into one.  For two sequences of bytes, seq1
+   and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for
+   each, adler1 and adler2.  adler32_combine() returns the Adler-32 checksum of
+   seq1 and seq2 concatenated, requiring only adler1, adler2, and len2.  Note
+   that the z_off_t type (like off_t) is a signed integer.  If len2 is
+   negative, the result has no meaning or utility.
+*/
+
+ZEXTERN uLong ZEXPORT crc32   OF((uLong crc, const Bytef *buf, uInt len));
+/*
+     Update a running CRC-32 with the bytes buf[0..len-1] and return the
+   updated CRC-32.  If buf is Z_NULL, this function returns the required
+   initial value for the crc.  Pre- and post-conditioning (one's complement) is
+   performed within this function so it shouldn't be done by the application.
+
+   Usage example:
+
+     uLong crc = crc32(0L, Z_NULL, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       crc = crc32(crc, buffer, length);
+     }
+     if (crc != original_crc) error();
+*/
+
+ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf,
+                                  z_size_t len));
+/*
+     Same as crc32(), but with a size_t length.
+*/
+
+/*
+ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2));
+
+     Combine two CRC-32 check values into one.  For two sequences of bytes,
+   seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
+   calculated for each, crc1 and crc2.  crc32_combine() returns the CRC-32
+   check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and
+   len2.
+*/
+
+
+                        /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
+                                     const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
+                                     const char *version, int stream_size));
+ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int  level, int  method,
+                                      int windowBits, int memLevel,
+                                      int strategy, const char *version,
+                                      int stream_size));
+ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int  windowBits,
+                                      const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
+                                         unsigned char FAR *window,
+                                         const char *version,
+                                         int stream_size));
+#ifdef Z_PREFIX_SET
+#  define z_deflateInit(strm, level) \
+          deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream))
+#  define z_inflateInit(strm) \
+          inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream))
+#  define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+          deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+                        (strategy), ZLIB_VERSION, (int)sizeof(z_stream))
+#  define z_inflateInit2(strm, windowBits) \
+          inflateInit2_((strm), (windowBits), ZLIB_VERSION, \
+                        (int)sizeof(z_stream))
+#  define z_inflateBackInit(strm, windowBits, window) \
+          inflateBackInit_((strm), (windowBits), (window), \
+                           ZLIB_VERSION, (int)sizeof(z_stream))
+#else
+#  define deflateInit(strm, level) \
+          deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream))
+#  define inflateInit(strm) \
+          inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream))
+#  define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+          deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+                        (strategy), ZLIB_VERSION, (int)sizeof(z_stream))
+#  define inflateInit2(strm, windowBits) \
+          inflateInit2_((strm), (windowBits), ZLIB_VERSION, \
+                        (int)sizeof(z_stream))
+#  define inflateBackInit(strm, windowBits, window) \
+          inflateBackInit_((strm), (windowBits), (window), \
+                           ZLIB_VERSION, (int)sizeof(z_stream))
+#endif
+
+#ifndef Z_SOLO
+
+/* gzgetc() macro and its supporting function and exposed data structure.  Note
+ * that the real internal state is much larger than the exposed structure.
+ * This abbreviated structure exposes just enough for the gzgetc() macro.  The
+ * user should not mess with these exposed elements, since their names or
+ * behavior could change in the future, perhaps even capriciously.  They can
+ * only be used by the gzgetc() macro.  You have been warned.
+ */
+struct gzFile_s {
+    unsigned have;
+    unsigned char *next;
+    z_off64_t pos;
+};
+ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file));  /* backward compatibility */
+#ifdef Z_PREFIX_SET
+#  undef z_gzgetc
+#  define z_gzgetc(g) \
+          ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g))
+#else
+#  define gzgetc(g) \
+          ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g))
+#endif
+
+/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or
+ * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if
+ * both are true, the application gets the *64 functions, and the regular
+ * functions are changed to 64 bits) -- in case these are set on systems
+ * without large file support, _LFS64_LARGEFILE must also be true
+ */
+#ifdef Z_LARGE64
+   ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
+   ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
+   ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
+   ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));
+   ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t));
+   ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t));
+#endif
+
+#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64)
+#  ifdef Z_PREFIX_SET
+#    define z_gzopen z_gzopen64
+#    define z_gzseek z_gzseek64
+#    define z_gztell z_gztell64
+#    define z_gzoffset z_gzoffset64
+#    define z_adler32_combine z_adler32_combine64
+#    define z_crc32_combine z_crc32_combine64
+#  else
+#    define gzopen gzopen64
+#    define gzseek gzseek64
+#    define gztell gztell64
+#    define gzoffset gzoffset64
+#    define adler32_combine adler32_combine64
+#    define crc32_combine crc32_combine64
+#  endif
+#  ifndef Z_LARGE64
+     ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
+     ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int));
+     ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile));
+     ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile));
+     ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
+     ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
+#  endif
+#else
+   ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *));
+   ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int));
+   ZEXTERN z_off_t ZEXPORT gztell OF((gzFile));
+   ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile));
+   ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));
+   ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
+#endif
+
+#else /* Z_SOLO */
+
+   ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));
+   ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
+
+#endif /* !Z_SOLO */
+
+/* undocumented functions */
+ZEXTERN const char   * ZEXPORT zError           OF((int));
+ZEXTERN int            ZEXPORT inflateSyncPoint OF((z_streamp));
+ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table    OF((void));
+ZEXTERN int            ZEXPORT inflateUndermine OF((z_streamp, int));
+ZEXTERN int            ZEXPORT inflateValidate OF((z_streamp, int));
+ZEXTERN unsigned long  ZEXPORT inflateCodesUsed OF ((z_streamp));
+ZEXTERN int            ZEXPORT inflateResetKeep OF((z_streamp));
+ZEXTERN int            ZEXPORT deflateResetKeep OF((z_streamp));
+#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO)
+ZEXTERN gzFile         ZEXPORT gzopen_w OF((const wchar_t *path,
+                                            const char *mode));
+#endif
+#if defined(STDC) || defined(Z_HAVE_STDARG_H)
+#  ifndef Z_SOLO
+ZEXTERN int            ZEXPORTVA gzvprintf Z_ARG((gzFile file,
+                                                  const char *format,
+                                                  va_list va));
+#  endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZLIB_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/zutil.c	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,325 @@
+/* zutil.c -- target dependent utility functions for the compression library
+ * Copyright (C) 1995-2017 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#include "zutil.h"
+#ifndef Z_SOLO
+#  include "gzguts.h"
+#endif
+
+z_const char * const z_errmsg[10] = {
+    (z_const char *)"need dictionary",     /* Z_NEED_DICT       2  */
+    (z_const char *)"stream end",          /* Z_STREAM_END      1  */
+    (z_const char *)"",                    /* Z_OK              0  */
+    (z_const char *)"file error",          /* Z_ERRNO         (-1) */
+    (z_const char *)"stream error",        /* Z_STREAM_ERROR  (-2) */
+    (z_const char *)"data error",          /* Z_DATA_ERROR    (-3) */
+    (z_const char *)"insufficient memory", /* Z_MEM_ERROR     (-4) */
+    (z_const char *)"buffer error",        /* Z_BUF_ERROR     (-5) */
+    (z_const char *)"incompatible version",/* Z_VERSION_ERROR (-6) */
+    (z_const char *)""
+};
+
+
+const char * ZEXPORT zlibVersion()
+{
+    return ZLIB_VERSION;
+}
+
+uLong ZEXPORT zlibCompileFlags()
+{
+    uLong flags;
+
+    flags = 0;
+    switch ((int)(sizeof(uInt))) {
+    case 2:     break;
+    case 4:     flags += 1;     break;
+    case 8:     flags += 2;     break;
+    default:    flags += 3;
+    }
+    switch ((int)(sizeof(uLong))) {
+    case 2:     break;
+    case 4:     flags += 1 << 2;        break;
+    case 8:     flags += 2 << 2;        break;
+    default:    flags += 3 << 2;
+    }
+    switch ((int)(sizeof(voidpf))) {
+    case 2:     break;
+    case 4:     flags += 1 << 4;        break;
+    case 8:     flags += 2 << 4;        break;
+    default:    flags += 3 << 4;
+    }
+    switch ((int)(sizeof(z_off_t))) {
+    case 2:     break;
+    case 4:     flags += 1 << 6;        break;
+    case 8:     flags += 2 << 6;        break;
+    default:    flags += 3 << 6;
+    }
+#ifdef ZLIB_DEBUG
+    flags += 1 << 8;
+#endif
+#if defined(ASMV) || defined(ASMINF)
+    flags += 1 << 9;
+#endif
+#ifdef ZLIB_WINAPI
+    flags += 1 << 10;
+#endif
+#ifdef BUILDFIXED
+    flags += 1 << 12;
+#endif
+#ifdef DYNAMIC_CRC_TABLE
+    flags += 1 << 13;
+#endif
+#ifdef NO_GZCOMPRESS
+    flags += 1L << 16;
+#endif
+#ifdef NO_GZIP
+    flags += 1L << 17;
+#endif
+#ifdef PKZIP_BUG_WORKAROUND
+    flags += 1L << 20;
+#endif
+#ifdef FASTEST
+    flags += 1L << 21;
+#endif
+#if defined(STDC) || defined(Z_HAVE_STDARG_H)
+#  ifdef NO_vsnprintf
+    flags += 1L << 25;
+#    ifdef HAS_vsprintf_void
+    flags += 1L << 26;
+#    endif
+#  else
+#    ifdef HAS_vsnprintf_void
+    flags += 1L << 26;
+#    endif
+#  endif
+#else
+    flags += 1L << 24;
+#  ifdef NO_snprintf
+    flags += 1L << 25;
+#    ifdef HAS_sprintf_void
+    flags += 1L << 26;
+#    endif
+#  else
+#    ifdef HAS_snprintf_void
+    flags += 1L << 26;
+#    endif
+#  endif
+#endif
+    return flags;
+}
+
+#ifdef ZLIB_DEBUG
+#include <stdlib.h>
+#  ifndef verbose
+#    define verbose 0
+#  endif
+int ZLIB_INTERNAL z_verbose = verbose;
+
+void ZLIB_INTERNAL z_error (m)
+    char *m;
+{
+    fprintf(stderr, "%s\n", m);
+    exit(1);
+}
+#endif
+
+/* exported to allow conversion of error code to string for compress() and
+ * uncompress()
+ */
+const char * ZEXPORT zError(err)
+    int err;
+{
+    return ERR_MSG(err);
+}
+
+#if defined(_WIN32_WCE)
+    /* The Microsoft C Run-Time Library for Windows CE doesn't have
+     * errno.  We define it as a global variable to simplify porting.
+     * Its value is always 0 and should not be used.
+     */
+    int errno = 0;
+#endif
+
+#ifndef HAVE_MEMCPY
+
+void ZLIB_INTERNAL zmemcpy(dest, source, len)
+    Bytef* dest;
+    const Bytef* source;
+    uInt  len;
+{
+    if (len == 0) return;
+    do {
+        *dest++ = *source++; /* ??? to be unrolled */
+    } while (--len != 0);
+}
+
+int ZLIB_INTERNAL zmemcmp(s1, s2, len)
+    const Bytef* s1;
+    const Bytef* s2;
+    uInt  len;
+{
+    uInt j;
+
+    for (j = 0; j < len; j++) {
+        if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
+    }
+    return 0;
+}
+
+void ZLIB_INTERNAL zmemzero(dest, len)
+    Bytef* dest;
+    uInt  len;
+{
+    if (len == 0) return;
+    do {
+        *dest++ = 0;  /* ??? to be unrolled */
+    } while (--len != 0);
+}
+#endif
+
+#ifndef Z_SOLO
+
+#ifdef SYS16BIT
+
+#ifdef __TURBOC__
+/* Turbo C in 16-bit mode */
+
+#  define MY_ZCALLOC
+
+/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
+ * and farmalloc(64K) returns a pointer with an offset of 8, so we
+ * must fix the pointer. Warning: the pointer must be put back to its
+ * original form in order to free it, use zcfree().
+ */
+
+#define MAX_PTR 10
+/* 10*64K = 640K */
+
+local int next_ptr = 0;
+
+typedef struct ptr_table_s {
+    voidpf org_ptr;
+    voidpf new_ptr;
+} ptr_table;
+
+local ptr_table table[MAX_PTR];
+/* This table is used to remember the original form of pointers
+ * to large buffers (64K). Such pointers are normalized with a zero offset.
+ * Since MSDOS is not a preemptive multitasking OS, this table is not
+ * protected from concurrent access. This hack doesn't work anyway on
+ * a protected system like OS/2. Use Microsoft C instead.
+ */
+
+voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+    voidpf buf;
+    ulg bsize = (ulg)items*size;
+
+    (void)opaque;
+
+    /* If we allocate less than 65520 bytes, we assume that farmalloc
+     * will return a usable pointer which doesn't have to be normalized.
+     */
+    if (bsize < 65520L) {
+        buf = farmalloc(bsize);
+        if (*(ush*)&buf != 0) return buf;
+    } else {
+        buf = farmalloc(bsize + 16L);
+    }
+    if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
+    table[next_ptr].org_ptr = buf;
+
+    /* Normalize the pointer to seg:0 */
+    *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;
+    *(ush*)&buf = 0;
+    table[next_ptr++].new_ptr = buf;
+    return buf;
+}
+
+void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)
+{
+    int n;
+
+    (void)opaque;
+
+    if (*(ush*)&ptr != 0) { /* object < 64K */
+        farfree(ptr);
+        return;
+    }
+    /* Find the original pointer */
+    for (n = 0; n < next_ptr; n++) {
+        if (ptr != table[n].new_ptr) continue;
+
+        farfree(table[n].org_ptr);
+        while (++n < next_ptr) {
+            table[n-1] = table[n];
+        }
+        next_ptr--;
+        return;
+    }
+    Assert(0, "zcfree: ptr not found");
+}
+
+#endif /* __TURBOC__ */
+
+
+#ifdef M_I86
+/* Microsoft C in 16-bit mode */
+
+#  define MY_ZCALLOC
+
+#if (!defined(_MSC_VER) || (_MSC_VER <= 600))
+#  define _halloc  halloc
+#  define _hfree   hfree
+#endif
+
+voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size)
+{
+    (void)opaque;
+    return _halloc((long)items, size);
+}
+
+void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)
+{
+    (void)opaque;
+    _hfree(ptr);
+}
+
+#endif /* M_I86 */
+
+#endif /* SYS16BIT */
+
+
+#ifndef MY_ZCALLOC /* Any system without a special alloc function */
+
+#ifndef STDC
+extern voidp  malloc OF((uInt size));
+extern voidp  calloc OF((uInt items, uInt size));
+extern void   free   OF((voidpf ptr));
+#endif
+
+voidpf ZLIB_INTERNAL zcalloc (opaque, items, size)
+    voidpf opaque;
+    unsigned items;
+    unsigned size;
+{
+    (void)opaque;
+    return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) :
+                              (voidpf)calloc(items, size);
+}
+
+void ZLIB_INTERNAL zcfree (opaque, ptr)
+    voidpf opaque;
+    voidpf ptr;
+{
+    (void)opaque;
+    free(ptr);
+}
+
+#endif /* MY_ZCALLOC */
+
+#endif /* !Z_SOLO */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zlib/zutil.h	Tue Dec 25 11:12:26 2018 -0800
@@ -0,0 +1,271 @@
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZUTIL_H
+#define ZUTIL_H
+
+#ifdef HAVE_HIDDEN
+#  define ZLIB_INTERNAL __attribute__((visibility ("hidden")))
+#else
+#  define ZLIB_INTERNAL
+#endif
+
+#include "zlib.h"
+
+#if defined(STDC) && !defined(Z_SOLO)
+#  if !(defined(_WIN32_WCE) && defined(_MSC_VER))
+#    include <stddef.h>
+#  endif
+#  include <string.h>
+#  include <stdlib.h>
+#endif
+
+#ifdef Z_SOLO
+   typedef long ptrdiff_t;  /* guess -- will be caught if guess is wrong */
+#endif
+
+#ifndef local
+#  define local static
+#endif
+/* since "static" is used to mean two completely different things in C, we
+   define "local" for the non-static meaning of "static", for readability
+   (compile with -Dlocal if your debugger can't find static symbols) */
+
+typedef unsigned char  uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long  ulg;
+
+extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
+/* (size given to avoid silly warnings with Visual C++) */
+
+#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
+
+#define ERR_RETURN(strm,err) \
+  return (strm->msg = ERR_MSG(err), (err))
+/* To be used only when the state is known to be valid */
+
+        /* common constants */
+
+#ifndef DEF_WBITS
+#  define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+#  define DEF_MEM_LEVEL 8
+#else
+#  define DEF_MEM_LEVEL  MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES    2
+/* The three kinds of block type */
+
+#define MIN_MATCH  3
+#define MAX_MATCH  258
+/* The minimum and maximum match lengths */
+
+#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
+
+        /* target dependencies */
+
+#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32))
+#  define OS_CODE  0x00
+#  ifndef Z_SOLO
+#    if defined(__TURBOC__) || defined(__BORLANDC__)
+#      if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
+         /* Allow compilation with ANSI keywords only enabled */
+         void _Cdecl farfree( void *block );
+         void *_Cdecl farmalloc( unsigned long nbytes );
+#      else
+#        include <alloc.h>
+#      endif
+#    else /* MSC or DJGPP */
+#      include <malloc.h>
+#    endif
+#  endif
+#endif
+
+#ifdef AMIGA
+#  define OS_CODE  1
+#endif
+
+#if defined(VAXC) || defined(VMS)
+#  define OS_CODE  2
+#  define F_OPEN(name, mode) \
+     fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
+#endif
+
+#ifdef __370__
+#  if __TARGET_LIB__ < 0x20000000
+#    define OS_CODE 4
+#  elif __TARGET_LIB__ < 0x40000000
+#    define OS_CODE 11
+#  else
+#    define OS_CODE 8
+#  endif
+#endif
+
+#if defined(ATARI) || defined(atarist)
+#  define OS_CODE  5
+#endif
+
+#ifdef OS2
+#  define OS_CODE  6
+#  if defined(M_I86) && !defined(Z_SOLO)
+#    include <malloc.h>
+#  endif
+#endif
+
+#if defined(MACOS) || defined(TARGET_OS_MAC)
+#  define OS_CODE  7
+#  ifndef Z_SOLO
+#    if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+#      include <unix.h> /* for fdopen */
+#    else
+#      ifndef fdopen
+#        define fdopen(fd,mode) NULL /* No fdopen() */
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifdef __acorn
+#  define OS_CODE 13
+#endif
+
+#if defined(WIN32) && !defined(__CYGWIN__)
+#  define OS_CODE  10
+#endif
+
+#ifdef _BEOS_
+#  define OS_CODE  16
+#endif
+
+#ifdef __TOS_OS400__
+#  define OS_CODE 18
+#endif
+
+#ifdef __APPLE__
+#  define OS_CODE 19
+#endif
+
+#if defined(_BEOS_) || defined(RISCOS)
+#  define fdopen(fd,mode) NULL /* No fdopen() */
+#endif
+
+#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX
+#  if defined(_WIN32_WCE)
+#    define fdopen(fd,mode) NULL /* No fdopen() */
+#    ifndef _PTRDIFF_T_DEFINED
+       typedef int ptrdiff_t;
+#      define _PTRDIFF_T_DEFINED
+#    endif
+#  else
+#    define fdopen(fd,type)  _fdopen(fd,type)
+#  endif
+#endif
+
+#if defined(__BORLANDC__) && !defined(MSDOS)
+  #pragma warn -8004
+  #pragma warn -8008
+  #pragma warn -8066
+#endif
+
+/* provide prototypes for these when building zlib without LFS */
+#if !defined(_WIN32) && \
+    (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0)
+    ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
+    ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
+#endif
+
+        /* common defaults */
+
+#ifndef OS_CODE
+#  define OS_CODE  3     /* assume Unix */
+#endif
+
+#ifndef F_OPEN
+#  define F_OPEN(name, mode) fopen((name), (mode))
+#endif
+
+         /* functions */
+
+#if defined(pyr) || defined(Z_SOLO)
+#  define NO_MEMCPY
+#endif
+#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
+ /* Use our own functions for small and medium model with MSC <= 5.0.
+  * You may have to use the same strategy for Borland C (untested).
+  * The __SC__ check is for Symantec.
+  */
+#  define NO_MEMCPY
+#endif
+#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
+#  define HAVE_MEMCPY
+#endif
+#ifdef HAVE_MEMCPY
+#  ifdef SMALL_MEDIUM /* MSDOS small or medium model */
+#    define zmemcpy _fmemcpy
+#    define zmemcmp _fmemcmp
+#    define zmemzero(dest, len) _fmemset(dest, 0, len)
+#  else
+#    define zmemcpy memcpy
+#    define zmemcmp memcmp
+#    define zmemzero(dest, len) memset(dest, 0, len)
+#  endif
+#else
+   void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));
+   int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));
+   void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len));
+#endif
+
+/* Diagnostic functions */
+#ifdef ZLIB_DEBUG
+#  include <stdio.h>
+   extern int ZLIB_INTERNAL z_verbose;
+   extern void ZLIB_INTERNAL z_error OF((char *m));
+#  define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+#  define Trace(x) {if (z_verbose>=0) fprintf x ;}
+#  define Tracev(x) {if (z_verbose>0) fprintf x ;}
+#  define Tracevv(x) {if (z_verbose>1) fprintf x ;}
+#  define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
+#  define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}
+#else
+#  define Assert(cond,msg)
+#  define Trace(x)
+#  define Tracev(x)
+#  define Tracevv(x)
+#  define Tracec(c,x)
+#  define Tracecv(c,x)
+#endif
+
+#ifndef Z_SOLO
+   voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items,
+                                    unsigned size));
+   void ZLIB_INTERNAL zcfree  OF((voidpf opaque, voidpf ptr));
+#endif
+
+#define ZALLOC(strm, items, size) \
+           (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr)  (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
+#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
+
+/* Reverse the bytes in a 32-bit value */
+#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \
+                    (((q) & 0xff00) << 8) + (((q) & 0xff) << 24))
+
+#endif /* ZUTIL_H */